Why does it take so long to get vertices from a mesh?

I just spent a while trying to figure out why a 9000-iteration loop over some mesh vertices took over 2 seconds. I eventually discovered it was because I was accessing MeshFilter.mesh.vertices inside the loop. Anyone know why this is so incredibly slow? Even stranger was that if I had 1500 vertices, the loop only took 1/50 of a second.

Even if “mesh.vertices” copied the entire array, that wouldn’t account for a 100x slowdown for only 6x as many vertices. There has to be a O(n^2) or even O(n^3) algorithm in there somewhere.

With the tiny optimization of pulling MeshFilter.mesh.vertices out of the loop, I was able to instantiate hundreds of prefabs, and still see a performance boost of 200x!

1 Like

Post the code.

If you are iterating over all vertices, and copying the array there, then doubling the number of vertices will both double the number of copies, and double the amount to copy.

Look at the docs for mesh.vertices:

“Returns a copy of the vertex positions or assigns a new vertex positions array”

So every time you get the verts, you generate a new array, and copy over all of the vertices. Which means that this:

var positionSum = Vector3.zero;
for (int i = 0; i < mesh.vertices.Length; i++) {
    positionSum += mesh.vertices[i];
}
var averagePosition = positionSum / mesh.vertices.Length;

Creates 2*n+1 copies of the vertices, where n is the number of vertices, while this:

var vertices = mesh.vertices;
var positionSum = Vector3.zero;
for (int i = 0; i < vertices.Length; i++) {
    positionSum += vertices[i];
}
var averagePosition = positionSum / vertices.Length;

Creates one.

This is one of those old decisions that Unity made way back which keeps confusing people. There’s very good reasons for things to function like this, but having it be behind a .vertices property keeps tricking people into thinking that they’re getting a reference to the verts.

6 Likes

Ah, that makes much more sense. Once I started calling “mesh.vertices*” in a loop, it became an O(n^2) algorithm. They could probably fix this with a mesh.GetVertex() method, but I should have been caching the object outside the loop anyway, so it’s not a big deal.*

There’s also non-allocating api Why does it take so long to get vertices from a mesh?
This allows to reuse memory on your side so you don’t need to allocate buffer for vertices every frame (if modificatin is continious of course) and for every singele mesh, if you apply modification to multiple meshes per frame.

That sounds really useful, but the link you gave is for this thread. :slight_smile:

Ouch! Here you go https://docs.unity3d.com/ScriptReference/Mesh.GetVertices.html

3 Likes

Thanks. Is the only advantage that memory isn’t allocated (which is pretty good)? It seems like it will still copy all the data, rather than calling by reference.

Oh, I get it. You can call GetVertices() in a less-frequently-accessed scope (like Start()).

The mesh data is stored on the c++ engine side, so having a direct reference would be very, very, very hairy, and not at all portable.

It will copy data to the list you provided instead of allocating memory for new array every time you use .vertices. Yes, the only advantage of this method is what you may allocalte list at start and only ask mesh to refill it instead of creating new one. Copying occurs anyway, it just the matter where to - to the newly allocated array, or to the list you managed to provide.

I usually use this with static list instance for easy. It works fine until you don’t do multithreading meshes generation.

I do use multithreading to generate my meshes, and I only fetch vertices once per mesh, so this won’t help me, but I’ll keep it in mind for future projects. A better approach for me would be to save the vertices array when I first create it. Since I generate the mesh in code, I have complete control over the vertices. No need to copy them at all.

If you’re using your own thread pool, you should be aware there’s ThreadStatic attribute for creating static caches at thread level.

This way it should be even faster, without retreiving vertices array at all.

Is it safe in C# to pass an array reference around between threads? The vertices array is created in a worker thread, after which the object containing the array is (or at least should be) garbage collected.

Seems to work fine. Thanks for your help!