Some static table optimisations#21
Conversation
Since all keys in the map are two-character strings, it makes sense to pack the two char32_t values into a uint64_t: - faster key hashes and comparisons. - smaller key objects (allocated heap space goes from 137kiB to 92kiB on MSVC x64). - in the (unlikely) case that short string optimisation is not available, avoids heap allocations for the string data.
Memory profiling indicates that initialising this map used 315kiB of heap memory, distributed through 5,982 allocations. Switching to a sorted static array allows the data to be directly mapped from .rodata, requiring no allocations. It can be a minor runtime performance tradeoff because std::lower_bound uses binary search, whereas unordered_map uses hashing then linear lookup through a linked list. There is no clear winner here.
…dered_map There was no particular reason to use a multimap for this data.
|
@samhocevar Thanks for the PR! Could you measure how much memory and performance improvement this approach can achieve? |
|
I will try to make some meaningful measurements. In the meantime, I found a more obvious problem with the initialisation of these variables, addressed in #22. |
|
For future reference, here is already a breakdown of allocation count and total heap space for the initialisation of the constant maps and vectors. This is for MSVC on Windows x64, but Clang for x64 has pretty similar numbers.
|
|
@samhocevar thanks for this. I have merged #22 first, it made a conflict with this PR. Could you please resolve it? Sorry about that. |
|
I think I will restart this PR from scratch and the changes will be rather different. Shall we keep it open for discussion or do you want to close it? |
|
@samhocevar if you want to restart, I don't mind closing it and you can open another PR. |
I had a look at the initialisation cost of cpp-unicodelib. It’s not a massive problem, but using
unordered_mapdoes cause a lot of allocations and pointer chasing. In this PR there are two examples of possible improvements:If you think this is the right direction, I can finish the work with all other tables and ensure that cpp-unicodelib does no heap allocation for its data.
There are also ways to make the data structures extremely efficient, using e.g. perfect hashing, but it would add complexity to the Python side of table generation. The Frozen library provides a drop-in replacement for
unordered_mapthat isconstexpr, but then again I suppose it is not desirable to add such a dependency. Fascinating project, though.