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 {
IdList<EntityMap,EntityId> &M() { return *((IdList<EntityMap,EntityId> *)this); }
std::string &S() { return *((std::string *)this); }
Platform::Path &P() { return *((Platform::Path *)this); }
EntityMap &M() { return *((EntityMap *)this); }
std::string &S() { return *((std::string *)this); }
Platform::Path &P() { return *((Platform::Path *)this); }
bool &b() { return *((bool *)this); }
RgbaColor &c() { return *((RgbaColor *)this); }
int &d() { return *((int *)this); }
@ -254,12 +254,16 @@ void SolveSpaceUI::SaveUsingTable(const Platform::Path &filename, int type) {
}
case 'M': {
int j;
fprintf(fh, "{\n");
for(j = 0; j < p->M().n; j++) {
EntityMap *em = &(p->M().elem[j]);
// Sort the mapping, since EntityMap is not deterministic.
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",
em->h.v, em->input.v, em->copyNumber);
it.second.v, it.first.input.v, it.first.copyNumber);
}
fprintf(fh, "}");
break;
@ -424,20 +428,17 @@ void SolveSpaceUI::LoadUsingTable(const Platform::Path &filename, char *key, cha
break;
case 'M': {
// Don't clear this list! When the group gets added, it
// 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() = {};
p->M().clear();
for(;;) {
EntityMap em;
EntityKey ek;
EntityId ei;
char line2[1024];
if (fgets(line2, (int)sizeof(line2), fh) == NULL)
break;
if(sscanf(line2, "%d %x %d", &(em.h.v), &(em.input.v),
&(em.copyNumber)) == 3)
if(sscanf(line2, "%d %x %d", &(ei.v), &(ek.input.v),
&(ek.copyNumber)) == 3)
{
p->M().Add(&em);
p->M().insert({ ek, ei });
} else {
break;
}
@ -719,9 +720,8 @@ bool SolveSpaceUI::LoadEntitiesFromFile(const Platform::Path &filename, EntityLi
char *key = line, *val = e+1;
LoadUsingTable(filename, key, val);
} else if(strcmp(line, "AddGroup")==0) {
// Don't leak memory; these get allocated whether we want them
// or not.
sv.g.remap.Clear();
// These get allocated whether we want them or not.
sv.g.remap.clear();
} else if(strcmp(line, "AddParam")==0) {
} else if(strcmp(line, "AddEntity")==0) {

View File

@ -31,7 +31,7 @@ void Group::Clear() {
impShell.Clear();
impEntity.Clear();
// 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) {
@ -641,30 +641,12 @@ void Group::GenerateEquations(IdList<Equation,hEquation> *l) {
}
hEntity Group::Remap(hEntity in, int copyNumber) {
// A hash table is used to accelerate the search
int hash = ((unsigned)(in.v*61 + copyNumber)) % REMAP_PRIME;
int i = remapCache[hash];
if(i >= 0 && i < remap.n) {
EntityMap *em = &(remap.elem[i]);
if(em->input.v == in.v && em->copyNumber == copyNumber) {
return h.entity(em->h.v);
}
auto it = remap.find({ in, copyNumber });
if(it == remap.end()) {
std::tie(it, std::ignore) =
remap.insert({ { in, copyNumber }, { (uint32_t)remap.size() } });
}
// but if we don't find it in the hash table, then linear search
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);
return h.entity(it->second.v);
}
void Group::MakeExtrusionLines(IdList<Entity,hEntity> *el, hEntity in) {

View File

@ -94,21 +94,27 @@ public:
uint32_t v;
};
class EntityId {
public:
struct EntityId {
uint32_t v; // entity ID, starting from 0
};
class EntityMap {
public:
int tag;
EntityId h;
struct EntityKey {
hEntity input;
int copyNumber;
// (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.
class Group {
@ -214,9 +220,7 @@ public:
bool forceToMesh;
IdList<EntityMap,EntityId> remap;
enum { REMAP_PRIME = 19477 };
int remapCache[REMAP_PRIME];
EntityMap remap;
Platform::Path linkFile;
SMesh impMesh;

View File

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