Profiler shows Object.ElementAddr_3_8() called over a million times. What is it? How do I reduce this?

I’m working on a voxel game, and in the profiler, under ChunkBehavior.CreateMeshAndCollision(), I see that Object.ElementAddr_3_8() is called tens of thousand of times per call of CreateMeshAndCollision(). It’s creating over 70ms of delay every time I create new chunks, making my game stutter. Any idea why Object.ElementAddr_3_8() is being called so many times?


I appreciate any help. I’ve dropped my CreateMeshAndCollision() method below, if you’d like to take a look at it. Thanks!


public void CreateMeshAndCollision() {
    vertices = new List<Vector3>();
    triangles = new List<int>();

    ChunkBehavior topNeighbor = tb.GetChunk(transform.position.x, transform.position.y + SIZE, transform.position.z);
    ChunkBehavior bottomNeightbor = tb.GetChunk(transform.position.x, transform.position.y - SIZE, transform.position.z);
    ChunkBehavior posXNeighbor = tb.GetChunk(transform.position.x + SIZE, transform.position.y, transform.position.z);
    ChunkBehavior negXNeighbor = tb.GetChunk(transform.position.x - SIZE, transform.position.y, transform.position.z);
    ChunkBehavior posZNeighbor = tb.GetChunk(transform.position.x, transform.position.y, transform.position.z + SIZE);
    ChunkBehavior negZNeighbor = tb.GetChunk(transform.position.x, transform.position.y, transform.position.z - SIZE);

    for (int x = 0; x < SIZE; x++) {
        for (int y = 0; y < SIZE; y++) {
            for (int z = 0; z < SIZE; z++) {
                if (blocks[x, y, z] != null) {

                    bool needsCollision = false;

                    if (y == SIZE - 1) {
                        if (topNeighbor != null) {
                            if (!topNeighbor.BlocksVew(x, 0, z)) {
                                blocks[x, y, z].CreateTopFace(vertices, triangles);
                                needsCollision = true;
                            }
                        } else {
                            blocks[x, y, z].CreateTopFace(vertices, triangles);
                            needsCollision = true;
                        }
                    } else if (blocks[x, y + 1, z] == null) {
                        blocks[x, y, z].CreateTopFace(vertices, triangles);
                        needsCollision = true;
                    }
                    
                    if (y == 0) {
                        if (bottomNeightbor != null && !bottomNeightbor.BlocksVew(x, SIZE - 1, z)) {
                            blocks[x, y, z].CreateBottomFace(vertices, triangles);
                            needsCollision = true;
                        }
                    } else if (blocks[x, y - 1, z] == null) {
                        blocks[x, y, z].CreateBottomFace(vertices, triangles);
                        needsCollision = true;
                    }

                    if (x == SIZE - 1) {
                        if (posXNeighbor != null && !posXNeighbor.BlocksVew(0, y, z)) {
                            blocks[x, y, z].CreatePosXFace(vertices, triangles);
                            needsCollision = true;
                        }
                    } else if (blocks[x + 1, y, z] == null) {
                        blocks[x, y, z].CreatePosXFace(vertices, triangles);
                        needsCollision = true;
                    }

                    if (x == 0) {
                        if (negXNeighbor != null && !negXNeighbor.BlocksVew(SIZE - 1, y, z)) {
                            blocks[x, y, z].CreateNegXFace(vertices, triangles);
                            needsCollision = true;
                        }
                    } else if (blocks[x - 1, y, z] == null) {
                        blocks[x, y, z].CreateNegXFace(vertices, triangles);
                        needsCollision = true;
                    }

                    if (z == SIZE - 1) {
                        if (posZNeighbor != null && !posZNeighbor.BlocksVew(x, y, 0)) {
                            blocks[x, y, z].CreatePosZFace(vertices, triangles);
                            needsCollision = true;
                        }
                    } else if (blocks[x, y, z + 1] == null) {
                        blocks[x, y, z].CreatePosZFace(vertices, triangles);
                        needsCollision = true;
                    }

                    if (z == 0) {
                        if (negZNeighbor != null && !negZNeighbor.BlocksVew(x, y, SIZE - 1)) {
                            blocks[x, y, z].CreateNegZFace(vertices, triangles);
                            needsCollision = true;
                        }
                    } else if (blocks[x, y, z - 1] == null) {
                        blocks[x, y, z].CreateNegZFace(vertices, triangles);
                        needsCollision = true;
                    }
                    /* Don't create collision until we've fixed this problem.
                    if (needsCollision) {
                        BoxCollider newBoxCollider = gameObject.AddComponent<BoxCollider>();
                        newBoxCollider.center = new Vector3(x + .5f, y + .5f, z + .5f);
                    }*/
                }
            }
        }
    }

    mesh = new Mesh();
    mesh.vertices = vertices.ToArray();
    mesh.triangles = triangles.ToArray();
    mesh.RecalculateNormals();
    GetComponent<MeshFilter>().mesh = mesh;
}

ElementAddr_3_8 is the internal method to calculate the flattend array index for your multidimensional array. Since you have a lot array accesses inside the inner most loop you will of course get a lot of these.

You can eliminate some of them by caching the current block at the start of your innermost loop. However for preformance it would be better to use a one dimensional flattend array yourself. Multidimensional arrays are quite slow. When you manually flatten the array you can calculate the intermediate offsets before each nested loop so the index calculation would get simpler.

Though those pure number crunching stuff you may want to carry out on a seperate thread. Just prepare all data required on the main thread and then start the generation. Of course the actual final Mesh creation has to be done on the main thread again.