arximboldi / immer

Postmodern immutable and persistent data structures for C++ — value semantics at scale
https://sinusoid.es/immer
Boost Software License 1.0
2.51k stars 185 forks source link

champ try_update #271

Open fabianbs96 opened 1 year ago

fabianbs96 commented 1 year ago

Story

As a user of immer, I want to efficiently build up nested map<set> structures in an incremental way until reaching a fixpoint, achieving maximum performance. This involves frequent updates of the nested sets inside the map. The obvious solution for this problem is using the immer::map::update function to update the inner sets. However, I have found that update always re-allocates -- even if the updated value is identical to the already present value leaving a lot of performance on the table. This for example happens if inserting an element to an inner set that has already been present.

As a fallback solution, I currently do the following:

const auto *SetPtr = map.find(Key);
if (!SetPtr)
  return map.set(std::move(Key), makeSingletonSet(std::move(Value));

auto NewSet = SetPtr->insert(std::move(Value));
if (NewSet == *OldSet)
  return map;

return map.set(std::move(Key), std::move(NewSet));

Whereas I really want to do:

return map.update(std::move(Key), [Value = std::move(Value)] (const auto &OldSet){
  return OldSet.insert(std::move(Value));
});

Solution Proposal

As a solution to above problem, I propose a new API try_update within immer::map that works similar to update, just adds an additional equality check on the result of the callback fn and in case of equality leaves the map unchanged.

This PR implements try_update on the champ and provides an according public API to immer::map. In addition, it fixes a minor issue that the champ::update function takes the key by const-ref, although the underlying do_update function can deal with perfectly forwarded keys.

Design decisions:

codecov-commenter commented 1 year ago

Codecov Report

Merging #271 (7eec5f9) into master (5875f77) will decrease coverage by 0.07%. The diff coverage is 87.40%.

:exclamation: Your organization needs to install the Codecov GitHub app to enable full functionality.

@@            Coverage Diff             @@
##           master     #271      +/-   ##
==========================================
- Coverage   90.53%   90.46%   -0.07%     
==========================================
  Files         119      119              
  Lines       12144    12379     +235     
==========================================
+ Hits        10994    11199     +205     
- Misses       1150     1180      +30     
Files Coverage Δ
immer/detail/util.hpp 82.60% <ø> (-0.25%) :arrow_down:
immer/map.hpp 99.09% <100.00%> (+0.07%) :arrow_up:
test/algorithm.cpp 89.69% <100.00%> (+0.67%) :arrow_up:
test/map/generic.ipp 99.23% <98.75%> (+0.10%) :arrow_up:
immer/detail/hamts/champ.hpp 86.16% <80.60%> (-1.00%) :arrow_down:

... and 1 file with indirect coverage changes

fabianbs96 commented 1 month ago

Hi @arximboldi, sorry for the late reply. Thanks for the suggestion, this sounds good. I will incorporate your requested changes. However, due to my own time constraints I cannot predict as of now, when this will be completed.

arximboldi commented 1 month ago

No worries. Thank you a lot in any case for the time that you've already put into this!