I have a large list of strings stored in one huge memory block (usually there is 100k+ or even 1M+ of them). These are actually hashes, so the alphabet of the strings is limited to A-F0-9 and each string is exactly 32 bytes long (so its stored ‘compressed’). I will call this list the main list from now on.
I want to be able to remove items from the main list. This will be usually done in bulks, so i get a large list (about 100 to 10k usually) of hashes which i need to find in this list and remove them. At the end of this operation there cannot be any empty blocks in the large memory block, so i need to take that into account. It is not guaranteed that all of the items will be in the main list, but none will be there multiple times. No rellocation can be done, the main block will always stay the same size.
The naive approach of iterating through the main list and checking if given hash shall be removed of course works, but is a bit slow. Also there is a bit too much moving of small memory blocks, because every time when a hash is flagged for removal i rewrite it with the last element of the main list, thus satisfying the condition of no empty blocks. This of course creates thousands of small memcpy’s which in turn slow the thing down more because i get tons of cache misses.
Is there a better approach?
Some important notes:
- the main list is not sorted and i cannot waste time sorting it, this
is a limitation imposed by the whole project and rewriting it so the
list is always sorted is not an option (it might not even be
possible) - memory is not really a problem, but the less is used the better
- i can use STL, but not boost
Okay, here’s what I’d do if I absolutely had to optimize the hell out of this.
I’m assuming order doesn’t matter, which seems to be the case as you (IIUC) remove items by swapping them with the last item.
dict.Now, this is a lot more work and maintenance burden than using off-the-shelf solutions. I wouldn’t advise it unless this really is as performance-critical as it sounds in your description.
If C++11 is an option, and your compiler’s
unordered_setis any good, maybe you should just use it and save yourself most of the hassle (but be aware that this probably increases memory requirements). You still need to specializestd::hashandstd::equal_tooroperator==. Alternative supply your ownHashandKeyEqualforunordered_set, but that probably doesn’t offer any benefit.