
Copy-on-Write in C++ and Rust - ChrisSD
https://oribenshir.github.io/afternoon_rusting/blog/copy-on-write
======
devit
Note that Rust's "Cow" type is NOT an analogue of copy-on-write C++ strings.

The correct analogue in Rust are the Rc and Arc types, and the "make_mut"
method that clones the data if and only if there is more than a reference.

Also the claim that "Rust’s COW implements the Send and Sync traits, which due
to Rust safety requirements, means the implementation is designed to be used
in multi-threaded applications" is technically true, but highly misleading,
since Cow of course doesn't have interior mutability, and so can't be modified
if shared between multiple threads (unless put in a Mutex or equivalent
container) and it's Send and Sync because all "simple" types do, and not
because it's "designed" to be so.

Overall, the author doesn't seem very knowledgeable about Rust.

------
macgyverismo
I don't get what point is the author is trying to make. If it is to show that
Rust outperforms C++ when implementing COW, I think he is mistaken. COW is
intrinsically performance limited in multithreaded environments, this is not a
language defect. The examples shown are definitely not apples-to-apples
comparisons. The author compares C++ runtime-evaluated COW to a compile time
evaluated strategy in Rust. At least, that is my take on this. I'm interested
in learning Rust, but I feel either I am not seeing the whole picture, or Rust
is only an interesting option if you are not already a seasoned C++ engineer.
(with up-to-date practices on how to use the language)

------
zX41ZdbW
Example of generic COW implementation in C++:
[https://github.com/ClickHouse/ClickHouse/blob/master/dbms/sr...](https://github.com/ClickHouse/ClickHouse/blob/master/dbms/src/Common/COW.h)

------
hinkley
How does one detect ownership bottlenecks in Rust?

~~~
_bxg1
Ownership on its own doesn't incur any performance overhead. If what you mean
is the overhead incurred by using Rc and Arc, it's just something you have to
be aware of.

~~~
hinkley
> Ownership on its own doesn't incur any performance overhead.

That means barely anything.

The computational cost is paid at compile time for deciding who owns the
object, but the consequences are still felt at runtime.

As I understand it, no parallel thread of execution can do anything to an
object while I believe I need to write to it, by any mechanism presently in
Rust (or planned?). So if new computational complexity creeps into the middle
of my operation then the availability of that object begins to drop like a
rock. Correct?

> it's just something you have to be aware of.

Take any feature in any language, and this solution doesn't scale. The general
case is not, "I made the code slow and I should have been aware of what I was
doing." The general case is, "someone who wasn't aware of what they were doing
has broken something and now we need to find it when we don't even know who
did it." and, "how do we explain to people how to do better in the future
instead of just scolding them?" I have been living in this very space in
another language for the better part of a month, and I expect to be here again
later this year.

I have tools do find memory leaks, and performance bottlenecks. Is anyone
thinking of analogous solutions for constrained parallelism in Rust?

~~~
_bxg1
> constrained parallelism in Rust

I'm... unsure what you mean. As far as I know Rust doesn't require any extra
runtime overhead for parallelism that wouldn't be required in C++. Rust
ensures you use locking mechanisms at compile time, but you still need those
in C++, you just "have to be aware of it" in that case.

I suppose Rust does require a value shared across threads to be reference-
counted, where that might technically be optional in C++. But then you get to
do manual deallocations across threads, which just sounds like a nightmare.
And each thread only has to increment the counter once, so the overhead should
be minuscule compared to the overhead of creating a thread in the first place.
The only exception would be if you have bad lock coordination, but again, that
problem is no different in Rust than it is in C++.

~~~
steveklabnik
> I suppose Rust does require a value shared across threads to be reference-
> counted

It does not require it; that is one of many possible strategies, depending on
what you're doing.

~~~
_bxg1
What are the other strategies? Aside from making it static.

~~~
cyphar
Much like in C, you could use several other schemes such as a Mutex (or
RwLock). There are crates which implement more complicated memory management
primitives -- the most obvious example being crossbeam[1].

Crossbeam even has an implementation of an epoch-based memory manager[2] which
(in very loose terms) amortises the cost of reference counting each object by
tracking the epochs of each thread and freeing objects once all threads have
moved past the "death time" of an object.

[1]: [https://docs.rs/crossbeam/](https://docs.rs/crossbeam/) [2]:
[https://docs.rs/crossbeam/0.7.3/crossbeam/epoch/index.html](https://docs.rs/crossbeam/0.7.3/crossbeam/epoch/index.html)

~~~
_bxg1
Normal mutexes in Rust still have to be paired with a reference-counting
structure. Those are separate mechanisms.

I don't know anything about the epoch-based manager, but it does sound
interesting.

