I tried to make my own “unsafe” version of the basic idea (for Faces) but the code below wasn’t any faster than collecting the face-midpoints with the existing method (looping over Mesh.GetFaceCenters(i); ):
public static class MeshExt
{
/// <summary>
/// Unsafe. Extends a Mesh with a CenterPoint to Face-Indices
/// mapper (Dictionary).
/// </summary>
public unsafe static Dictionary<Point3d, int> GetPointToIndicesMapping_Unsafe(this Rhino.Geometry.Mesh mesh,
out int face_dupes_count, int decimals = 4)
{
face_dupes_count = 0;
var point_index_mapper = new Dictionary<Point3d, int>();
// accessing the mesh and its raw arrays
using (var mesh_access = mesh.GetUnsafeLock(false))
{
// Pointers to the beginning of the respective raw arrays
var p_face = mesh_access.FacesArray(out int face_len);
var p_vertices = mesh_access.VertexPoint3fArray(out int vert_len);
// Calculate face center point from vertices and store the
// center point and the face indices in the dictionary.
// Rounding (n decimals) helps prune off dupe faces being the
// the close but not having "bit-exactly" matching center points.
for (int index = 0; index < face_len; index++)
{
Point3f* p_vertice_A, p_vertice_B, p_vertice_C;
// Advance vertex pointer to each face-corner's respective vertex position:
p_vertice_A = p_vertices + p_face->A;
p_vertice_B = p_vertices + p_face->B;
p_vertice_C = p_vertices + p_face->C;
// Calc face center
var center = new Point3d
{
X = p_vertice_A->X + p_vertice_B->X + p_vertice_C->X,
Y = p_vertice_A->Y + p_vertice_B->Y + p_vertice_C->Y,
Z = p_vertice_A->Z + p_vertice_B->Z + p_vertice_C->Z
};
center /= 3; // = center
if (decimals > 0) // skip rounding if <= 0
{
center.X = Math.Round(center.X, decimals); // Fixed bug
center.Y = Math.Round(center.Y, decimals);
center.Z = Math.Round(center.Z, decimals);
}
// Add center point (=key) and face index (value) to
// the mapper, returning count of any skipped dupes.
try { point_index_mapper.Add(center, index); }
catch { face_dupes_count++; }
// advance face pointer
p_face++;
}
return point_index_mapper;
}
}
This kind of works, but calculating the center points with C# is slower than the API method GetFaceCenters() etc, etc.
The Dictionary should have been a ConcurrentDictionary to speed up the loading of items (reading is isn’t very fast using this dict either).
Hope this inspires you to much better ideas about how to speed this up.
The code above, plus the “remapper” resolving the indices, has the following profiling numbers for a 2.6 million faces “BaseMesh” (blue) with a smaller SubMesh (red) remapping its (280.000) indices against that big ugly one .
Those numbers should go down significantly, both for the mapper and the remapper. Btw, the component to the far left is replacing the native Rhino.GetDuplicateFaces() and runs 2x faster. 
// Rolf
- Edit: Rounding bug fixed.