diff --git a/src/bundles/graphics/_graphics/mesh_edges.cpp b/src/bundles/graphics/_graphics/mesh_edges.cpp
index e0537a835..215d82122 100644
--- a/src/bundles/graphics/_graphics/mesh_edges.cpp
+++ b/src/bundles/graphics/_graphics/mesh_edges.cpp
@@ -13,7 +13,7 @@
  * === UCSF ChimeraX Copyright ===
  */
 
-#include <set>				// use std::set
+#include <unordered_set>				// use std::unordered_set
 
 #include <arrays/pythonarray.h>		// use array_from_python()
 #include <arrays/rcarray.h>		// use Numeric_Array, Array<T>
@@ -25,6 +25,16 @@
 namespace Map_Cpp
 {
 
+static_assert(sizeof(int) * 2 == sizeof(size_t), "Size of int is not half of size_t - hash needs a re-think!");
+
+struct edge_hash
+{
+  std::size_t operator() (const std::pair<int, int> &edge) const noexcept
+  {
+    return (size_t)edge.first << 32 | edge.second; 
+  }
+};
+
 // ----------------------------------------------------------------------------
 // Find edges of displayed triangles.  Edges that appear in 2 or more triangles
 // are only listed once.
@@ -32,11 +42,14 @@ namespace Map_Cpp
 static IArray calculate_masked_edges(const IArray &triangles,
 				     const BArray &tmask, const BArray &emask)
 {
-  std::set< std::pair<int,int> > edges;
+  std::unordered_set< std::pair<int,int>, edge_hash > edges;
 
   unsigned char *show_t = (tmask.size() > 0 ? tmask.values() : NULL);
   unsigned char *show_e = (emask.size() > 0 ? emask.values() : NULL);
   int n = triangles.size(0);
+  edges.max_load_factor(0.75);
+  edges.reserve(n*2);
+
   int *tarray = triangles.values();
   for (int k = 0 ; k < n ; ++k, tarray += 3)
     {
@@ -56,7 +69,7 @@ static IArray calculate_masked_edges(const IArray &triangles,
   int64_t size[2] = {(int64_t)edges.size(), 2};
   IArray masked_edges(2, size);
   int *eiarray = masked_edges.values();
-  for (std::set< std::pair<int,int> >::iterator ei = edges.begin() ; 
+  for (auto ei = edges.begin() ; 
        ei != edges.end() ; ++ei)
     {
       *eiarray = (*ei).first; eiarray += 1;
