10

One of the selling points of immutable data structures is that they are automatically parallelizable. If no mutation is going on, then references to a functional data structure can be passed around between threads without any locking.

I got to thinking about how functional data structures would be implemented in c++. Suppose that we have a reference count on each node of our data structure. (Functional data structures share structure between old and updated members of a data structure, so nodes would not belong uniquely to one particular data structure.)

The problem is that if reference counts are being updated in different threads, then our data structure is no longer thread safe. And attaching a mutex to every single node is both expensive and defeats the purpose of using immutable data structures for concurrency.

Is there a way to make concurrent immutable data structures work in c++ (and other non-garbage collected environments)?

Rob Lachlan
  • 14,289
  • 5
  • 49
  • 99
  • I'm no expert in this, but you could store reference counts per-thread, and only place a lock on them to check if all threads have reached zero reference on a node. But I'm sure there are more elegant solutions than this or maybe reference counting in general. – sinelaw Aug 31 '11 at 06:49
  • Here is a popular immutable data structure for C++ http://www.sgi.com/tech/stl/Rope.html – Yakov Galka Aug 31 '11 at 06:51
  • You could look at implementations of `shared_ptr`, that faces exactly the same issue with needing an efficient thread-safe reference count. – Steve Jessop Aug 31 '11 at 08:37
  • @Rob Lachlan shared_ptr reference counting is already thread safe. You can have a look at thread http://codereview.stackexchange.com/questions/26080/immutable-c-stack-thoughts-performance for an immutable stack implementation (which has disadvantages compared to garbage collected implementations) – Ghita May 12 '13 at 17:23

2 Answers2

5

There are lock-free reference counting algorithms, see, e.g. Lock-Free Reference Counting, Atomic Reference Counting Pointers.

Also note that C++0x (which will probably become C++11 soon) contains atomic integer types especially for purposes like this one.

Christopher Creutzig
  • 8,656
  • 35
  • 45
  • I'm not sure I'm right here, but wouldn't using reference counting degrade performance from O(log n) to O(n), because you'd have to update refcounts of all nodes your newly created "offspring" of the collection reuses? This defeats the purpose of an immutable collection for most use cases I guess. But please correct me if I'm wrong! – le_me Jul 15 '16 at 11:51
  • @le_me I have no idea what data structure Rob was trying to have, so I cannot say anything about its timing characteristics. Could be O(log n), could be O(n^3), could be O(exp(n)) or anything else. The effect of reference counting would require a second analysis, depending on usage pattern. Also, having reference counts was his idea, not mine. I may have replied to the wrong part of an XY problem, of course, but I still think your comment would be better placed on the question, where reference counting was suggested. – Christopher Creutzig Jul 15 '16 at 12:08
  • I just wanted to add information to the specific solution you presented. I think it is important to note that reference counting has a performance penalty with possibly higher time complexity than the collection itself. Eg. [Microsofts Immutable Collections seem to have O(log n) complexity](https://blogs.msdn.microsoft.com/bclteam/2012/12/18/preview-of-immutable-collections-released-on-nuget/) or less for any operation. So in that case reference counting would impact performance severely for large data sets. – le_me Jul 15 '16 at 13:48
  • @le_me The only thing I don't understand is where you think I started presenting to have reference counting. That's in the question. – Christopher Creutzig Jul 19 '16 at 08:22
  • yes they bring it up as an example, but the question is how one would implement immutable datastructures in c++. you say that there are lockfree refcounting algorithms, but I think that's not a good answer, as it hurts performance too much. – le_me Jul 20 '16 at 09:13
4

Well, garbage collected languages also have the issue of multi-threaded environments (and it is not easy for mutable structures).

You have forgotten that unlike arbitrary data, counters can be incremented and decremented atomically, so a mutex would be unnecessary. It still means that cache synchro between processors need be maintained, which may cost badly if a single node keeps being updated.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • 2
    Look at sloppy counters (http://www.usenix.org/events/osdi10/tech/full_papers/Boyd-Wickizer.pdf) as one way to implement reference counters that don't cause as much cache contention. The referenced paper also mentions several other approaches to look at. – Karmastan Aug 31 '11 at 21:11
  • @Karmastan: thanks for the article! I am not sure if it would work though as per the article "This operation is expensive, so sloppy counters should only be used for objects that are relatively infrequently de-allocated.", but the idea itself is interesting. Another technic I had read about is to maintain the counters separate from the objects, and have a single thread update them, with TLS queues to buffer inc/dec operations. – Matthieu M. Sep 01 '11 at 06:29