Replace entity map implementation with std::unordered_map.

On a single load benchmark this provides about 25% speedup.
pull/434/head
whitequark 2019-05-24 17:54:02 +00:00
parent 406c55e8b9
commit bd84bc1ae9
4 changed files with 43 additions and 58 deletions

View File

@ -210,9 +210,9 @@ const SolveSpaceUI::SaveTable SolveSpaceUI::SAVED[] = {
}; };
struct SAVEDptr { struct SAVEDptr {
IdList<EntityMap,EntityId> &M() { return *((IdList<EntityMap,EntityId> *)this); } EntityMap &M() { return *((EntityMap *)this); }
std::string &S() { return *((std::string *)this); } std::string &S() { return *((std::string *)this); }
Platform::Path &P() { return *((Platform::Path *)this); } Platform::Path &P() { return *((Platform::Path *)this); }
bool &b() { return *((bool *)this); } bool &b() { return *((bool *)this); }
RgbaColor &c() { return *((RgbaColor *)this); } RgbaColor &c() { return *((RgbaColor *)this); }
int &d() { return *((int *)this); } int &d() { return *((int *)this); }
@ -254,12 +254,16 @@ void SolveSpaceUI::SaveUsingTable(const Platform::Path &filename, int type) {
} }
case 'M': { case 'M': {
int j;
fprintf(fh, "{\n"); fprintf(fh, "{\n");
for(j = 0; j < p->M().n; j++) { // Sort the mapping, since EntityMap is not deterministic.
EntityMap *em = &(p->M().elem[j]); std::vector<std::pair<EntityKey, EntityId>> sorted(p->M().begin(), p->M().end());
std::sort(sorted.begin(), sorted.end(),
[](std::pair<EntityKey, EntityId> &a, std::pair<EntityKey, EntityId> &b) {
return a.second.v < b.second.v;
});
for(auto it : sorted) {
fprintf(fh, " %d %08x %d\n", fprintf(fh, " %d %08x %d\n",
em->h.v, em->input.v, em->copyNumber); it.second.v, it.first.input.v, it.first.copyNumber);
} }
fprintf(fh, "}"); fprintf(fh, "}");
break; break;
@ -424,20 +428,17 @@ void SolveSpaceUI::LoadUsingTable(const Platform::Path &filename, char *key, cha
break; break;
case 'M': { case 'M': {
// Don't clear this list! When the group gets added, it p->M().clear();
// makes a shallow copy, so that would result in us
// freeing memory that we want to keep around. Just
// zero it out so that new memory is allocated.
p->M() = {};
for(;;) { for(;;) {
EntityMap em; EntityKey ek;
EntityId ei;
char line2[1024]; char line2[1024];
if (fgets(line2, (int)sizeof(line2), fh) == NULL) if (fgets(line2, (int)sizeof(line2), fh) == NULL)
break; break;
if(sscanf(line2, "%d %x %d", &(em.h.v), &(em.input.v), if(sscanf(line2, "%d %x %d", &(ei.v), &(ek.input.v),
&(em.copyNumber)) == 3) &(ek.copyNumber)) == 3)
{ {
p->M().Add(&em); p->M().insert({ ek, ei });
} else { } else {
break; break;
} }
@ -719,9 +720,8 @@ bool SolveSpaceUI::LoadEntitiesFromFile(const Platform::Path &filename, EntityLi
char *key = line, *val = e+1; char *key = line, *val = e+1;
LoadUsingTable(filename, key, val); LoadUsingTable(filename, key, val);
} else if(strcmp(line, "AddGroup")==0) { } else if(strcmp(line, "AddGroup")==0) {
// Don't leak memory; these get allocated whether we want them // These get allocated whether we want them or not.
// or not. sv.g.remap.clear();
sv.g.remap.Clear();
} else if(strcmp(line, "AddParam")==0) { } else if(strcmp(line, "AddParam")==0) {
} else if(strcmp(line, "AddEntity")==0) { } else if(strcmp(line, "AddEntity")==0) {

View File

@ -31,7 +31,7 @@ void Group::Clear() {
impShell.Clear(); impShell.Clear();
impEntity.Clear(); impEntity.Clear();
// remap is the only one that doesn't get recreated when we regen // remap is the only one that doesn't get recreated when we regen
remap.Clear(); remap.clear();
} }
void Group::AddParam(IdList<Param,hParam> *param, hParam hp, double v) { void Group::AddParam(IdList<Param,hParam> *param, hParam hp, double v) {
@ -641,30 +641,12 @@ void Group::GenerateEquations(IdList<Equation,hEquation> *l) {
} }
hEntity Group::Remap(hEntity in, int copyNumber) { hEntity Group::Remap(hEntity in, int copyNumber) {
// A hash table is used to accelerate the search auto it = remap.find({ in, copyNumber });
int hash = ((unsigned)(in.v*61 + copyNumber)) % REMAP_PRIME; if(it == remap.end()) {
int i = remapCache[hash]; std::tie(it, std::ignore) =
if(i >= 0 && i < remap.n) { remap.insert({ { in, copyNumber }, { (uint32_t)remap.size() } });
EntityMap *em = &(remap.elem[i]);
if(em->input.v == in.v && em->copyNumber == copyNumber) {
return h.entity(em->h.v);
}
} }
// but if we don't find it in the hash table, then linear search return h.entity(it->second.v);
for(i = 0; i < remap.n; i++) {
EntityMap *em = &(remap.elem[i]);
if(em->input.v == in.v && em->copyNumber == copyNumber) {
// We already have a mapping for this entity.
remapCache[hash] = i;
return h.entity(em->h.v);
}
}
// And if we still don't find it, then create a new entry.
EntityMap em;
em.input = in;
em.copyNumber = copyNumber;
remap.AddAndAssignId(&em);
return h.entity(em.h.v);
} }
void Group::MakeExtrusionLines(IdList<Entity,hEntity> *el, hEntity in) { void Group::MakeExtrusionLines(IdList<Entity,hEntity> *el, hEntity in) {

View File

@ -94,21 +94,27 @@ public:
uint32_t v; uint32_t v;
}; };
class EntityId { struct EntityId {
public:
uint32_t v; // entity ID, starting from 0 uint32_t v; // entity ID, starting from 0
}; };
class EntityMap { struct EntityKey {
public:
int tag;
EntityId h;
hEntity input; hEntity input;
int copyNumber; int copyNumber;
// (input, copyNumber) gets mapped to ((Request)xxx).entity(h.v) // (input, copyNumber) gets mapped to ((Request)xxx).entity(h.v)
void Clear() {}
}; };
struct EntityKeyHash {
size_t operator()(const EntityKey &k) const {
size_t h1 = std::hash<uint32_t>{}(k.input.v),
h2 = std::hash<uint32_t>{}(k.copyNumber);
return h1 ^ (h2 << 1);
}
};
struct EntityKeyEqual {
bool operator()(const EntityKey &a, const EntityKey &b) const {
return std::tie(a.input.v, a.copyNumber) == std::tie(b.input.v, b.copyNumber);
}
};
typedef std::unordered_map<EntityKey, EntityId, EntityKeyHash, EntityKeyEqual> EntityMap;
// A set of requests. Every request must have an associated group. // A set of requests. Every request must have an associated group.
class Group { class Group {
@ -214,9 +220,7 @@ public:
bool forceToMesh; bool forceToMesh;
IdList<EntityMap,EntityId> remap; EntityMap remap;
enum { REMAP_PRIME = 19477 };
int remapCache[REMAP_PRIME];
Platform::Path linkFile; Platform::Path linkFile;
SMesh impMesh; SMesh impMesh;

View File

@ -66,8 +66,7 @@ void SolveSpaceUI::PushFromCurrentOnto(UndoStack *uk) {
dest.displayMesh = {}; dest.displayMesh = {};
dest.displayOutlines = {}; dest.displayOutlines = {};
dest.remap = {}; dest.remap = src->remap;
src->remap.DeepCopyInto(&(dest.remap));
dest.impMesh = {}; dest.impMesh = {};
dest.impShell = {}; dest.impShell = {};
@ -161,7 +160,7 @@ void SolveSpaceUI::UndoClearState(UndoState *ut) {
for(i = 0; i < ut->group.n; i++) { for(i = 0; i < ut->group.n; i++) {
Group *g = &(ut->group.elem[i]); Group *g = &(ut->group.elem[i]);
g->remap.Clear(); g->remap.clear();
} }
ut->group.Clear(); ut->group.Clear();
ut->request.Clear(); ut->request.Clear();