I am trying to improve the performance of my Python script by moving the Python code below, which creates several meshes, into a C++ API DLL.
faces = meshGeo.Faces
# Make .NET List of all faces to use in AddFaces below.
facesL = List[MeshFace](nvert)
# Add all faces from unified mesh to .NET List for quick access using GetRange.
facesL.AddRange(faces)
lower_index = 0
# Make duplicate mesh without faces.
dmesh = meshGeo.DuplicateMesh()
dmesh.Faces.Clear()
last_index = upper_face_index[-1][0]
for upper_index, name in upper_face_index:
# If not last mesh, duplicate the no-faces mesh.
if upper_index != last_index: next_meshGeo = dmesh.DuplicateMesh()
# Use dmesh when making the last mesh.
else: next_meshGeo = dmesh
# Copy over the faces used in this mesh.
next_meshGeo.Faces.AddFaces(facesL.GetRange(lower_index, upper_index - lower_index))
# Use upper_index for lower limit in next range.
lower_index = upper_index
# Compact the mesh to remove unused vertices, colors, textures and normals.
next_meshGeo.Compact()
# Add mesh to document.
doc.Objects.AddMesh(next_meshGeo)
The corresponding C++ API code is:
DLLEXPORT void make_meshes(uint32_t doc_serial_number, uint32_t mesh_serial_number, uint32_t nmi, int32_t* upper_face_indices, uint32_t* mesh_serial_numbers) {
// Get doc from RuntimeSerialNumber passed as uint_32_t.
CRhinoDoc* pDoc = CRhinoDoc::FromRuntimeSerialNumber(doc_serial_number);
const CRhinoObject* obj = pDoc->LookupObjectByRuntimeSerialNumber(mesh_serial_number);
const ON_Mesh* mesh = ON_Mesh::Cast(obj->Geometry());
// Make new meshes.
int lower_index = 0;
// Duplicate mesh.
ON_Mesh dmesh(*mesh);
// Remove all faces.
dmesh.DeleteComponent(ON_COMPONENT_INDEX(ON_COMPONENT_INDEX::mesh_face, 14));
// Get index of last face.
int32_t last_index = upper_face_indices[nmi-1];
for (int i = 0; i < nmi; ++i) {
// Get upper face index for this mesh.
int upper_index = upper_face_indices[i];
// Find number of faces in this mesh.
int num = upper_index - lower_index;
// If not last mesh, duplicate the no-faces mesh.
if (upper_index != last_index) {
// Duplicate no-faces mesh.
ON_Mesh new_mesh(dmesh);
// Set capacity of new mesh. Without this, face copying is 30% slower.
new_mesh.m_F.SetCapacity(num);
// Copy over faces used in this mesh.
for (int j = 0; j < num; ++j) {
new_mesh.m_F[j] = mesh->m_F[lower_index + j];
}
new_mesh.Compact();
// Add new mesh to Rhino document.
CRhinoMeshObject* meshObject = pDoc->AddMeshObject(new_mesh);
mesh_serial_numbers[i] = meshObject ? meshObject->RuntimeSerialNumber() : 0;
// Use upper_index for lower limit in next range.
lower_index = upper_index;
}
The slow C++ part is where the old faces are copied into the new_mesh:
for (int j = 0; j < num; ++j) { new_mesh.m_F[j] = mesh->m_F[lower_index + j]; }
In Python this is done using the higher performance .NET List instead of a Python list:
next_meshGeo.Faces.AddFaces(facesL.GetRange(lower_index,upper_index-lower_index))
The Python+.NET+Rhinocommon code is 50% faster. Is there a way to do this faster in the C++ API?
I have moved several blocks of Python code that talk to Rhino into a C++ API DLL and seen significant speed up. But not in this case. Is it simply that a .NET List is faster than a C++ API ON_SimpleArray for copy operations? Or is the Rhinocommon meshGeo.Faces.AddFaces(IEnumerable)
procedure faster than looping thru: new_mesh.m_F[i] = mesh->m_F[lower_index+j];
. Since only a subset of the faces are being copied, I do not see a way to use the possibly more efficient C++ option of:
new_mesh.m_F.Append(num, mesh->m_F.Array())
to perform the copy.
Regards,
Terry.