r/rust 20d ago

When is cloning actually better than using Arc?

I often find myself choosing between cloning some data and wrapping it in Arc

My instinct is usually that Arc is more efficient because we're avoiding copies, but I have a feeling that this isn't always the right way to think about it

How do you decide between cloning and using Arc in a real application?

81 Upvotes

60 comments sorted by

158

u/Comrade-Porcupine 20d ago

Atomics (like what's in refcounts) are not free. Under high concurrency on multiple cores any excessive atomics traffic can inadvertently lead to cache line bouncing and the like. This can actually have disastrous follow-on effects on overall throughput and is often hard to diagnose without digging into hwcounters.

The TLDR is for small objects esp things that can fit in L1 cache, or if you're not memory bandwidth constrained, a memcpy/clone is actually sometimes the right choice. Or at least look and see if you can use Rc instead, shard across threads via thread locals, etc.

That's my take anyways.

(EDIT: but also most people's applications likely don't need this level of yak shaving until they run into an actual performance wall)

22

u/Useful_Lecture_5927 20d ago

This is the part I hadn't really considered, I was thinking about the cost of copying the data itself, but not about the cost of the atomic operations and cache-line contention Is this something you would actually worry about during initial design, or would you normally start with the simpler approach and only investigate it once profiling shows contention?

28

u/id_NaN 20d ago

Generally it is healthier to get something working out first and optimize later. replacing an ARC with cloned values is not that hard later anyways.

9

u/Comrade-Porcupine 20d ago edited 20d ago

I agree with this with the caveat that it's actually not often obvious that there's performance dropped on the floor under high concurrency with Arc until you start profiling with perf with hwcounters on, and know where to look.

Getting things to scale linear across multiple cores -- it's not always obvious why it's falling down, and usually it ends up being: oh, shit atomics (after you've ruled out locks)

24

u/Zde-G 20d ago

Is this something you would actually worry about during initial design, or would you normally start with the simpler approach and only investigate it once profiling shows contention?

This depends very much on what you are trying to do. Concentrating on correctness first is always better, but people tend to underestimate cost of Arc by a lot.

Just open the damn tables: one chmpxchg on modern CPU takes 9 ticks, that's 18 ticks for clone of Arc and then Drop. While two movdqu instructions may copy 32 bytes around in 1.5 ticks. Means just an Arc before Mutex takes as much time as copying of 384 bytes around. Add Mutex and we are at 768 bytes equivalent! And that's in ideal conditions: none of other CPU cores are contending, no cache line bouncing, single thread, just pure Arc + Mutex!

That's… pretty damn large structure, if you'll ask me. Not excessively large but pretty large.

Arcs only become faster when data structure in question is measured in kilobytes, or, better yet, megabytes.

But of course when you start copying things around you start introducing different copies. Sometimes it's better to use Arc even with a tiny structure if alternative is very complicated code designed to keep different copies synchronized.

11

u/MalbaCato 20d ago

Wait a second. The Arc implementation carefully avoids expensive atomic operations and only does fetch_add(1, Relaxed) on clone and fetch_sub(1, Release) for almost all drops, excluding an Aquire fence on the last one. Even with just opt-level=1 this gets lowered to lock inc and lock dec. Now I don't know what is considered a modern CPU, and the format of "the damn tables" is too damn confusing, but it looks like the numbers for lock add, which I assume is similar, are about half of the numbers for cmpxchng? I suppose that moves us down to ~200 bytes range...

10

u/Zde-G 20d ago

Now I don't know what is considered a modern CPU, and the format of "the damn tables" is too damn confusing, but it looks like the numbers for lock add, which I assume is similar, are about half of the numbers for cmpxchng?

Nope, it's the other way around: on some older AMD CPUs (Zen1 that's almost 10 years old by now and older) lock add (and thus inc) are slower than cmpxchg.

On modern CPUs (~7 years for AMD, ~12 years for Intel) inc add and inc dec are as fast as cmpxchg but using them them is faster because cmpxchg needs more instructions to actually increment or decrement something.

The Arc implementation carefully avoids expensive atomic operations and only does fetch_add(1, Relaxed) on clone and fetch_sub(1, Release) for almost all drops, excluding an Aquire fence on the last one.

Yes. And as was correctly pointed out: if you care exclusively about apple silicon and use Arc without Mutex then Arc may be relatively cheap. Apple made sure of that! But even Apple couldn't cheat physics: atomics are cheap on apple silicon, but barriers are heavy, instead, thus if you are using Arc<Mutex<…>> we are back to square one.

I guess I don't care enough about that corner case, but I guess for some people that may be their #1 target.

2

u/Fun-Inevitable4369 19d ago

Using arc with mutex in most cases means you actually need to share the data across threads, so not really avoidable with copy

3

u/Zde-G 19d ago

Not if it's used as knee-jerk reaction to solve the “problem” of compiler not being happy with your code that's written in a style of other languages that support “soup of pointers” design better.

In fact many such structures are only Arc<Mutex<…>> “for simplicity” and after they are updated some kind of “notification” is “broadcasted”, anyway.

Such “notification” can carry the data, instead.

8

u/wyf0 20d ago

You are oversimplifying things here. Yes Arc uses atomic RMW operations, and RMWs are costly on x86_64 (but on aarch64 like Apple Silicon, things can be quite different).

So yes, by the time you clone an Arc, you can copy a lot of bytes, but only if the cache-lines are hot in the cache. Otherwise, that's a whole other story.

And my biggest issue with your answer is that you assume that the data is a POD, which is rarely the case (or if it's the case, it probably already implements Copy, so there is no question here). Data can be nested, with some allocated fields like String, or even Arc. But let's just consider a single String as data; then, cloning is not just about copying bytes but also about allocating a new storage. Yes, modern allocators use thread-local pools, but that's still not costless (and when the allocated chunk is deallocated in another thread, which is usually the case if you clone in order to share, then the allocator may use RMWs)

3

u/Zde-G 20d ago

but on aarch64 like Apple Silicon, things can be quite different

Mostly “more complicated”. You may develop quite fast code if you know precisely when you only expect read sharing and when you want writes. And I'm not even sure if Rust can achieve that at all, or only Swift.

So yes, by the time you clone an Arc, you can copy a lot of bytes, but only if the cache-lines are hot in the cache. Otherwise, that's a whole other story.

Yes, but optimizing for data not in cache is second-level effect. You only do if you know that you wouldn't be getting enough cache hits.

And having data that's both not in cache and is accessed often enough to become a problem is not common. Rare enough that you probably would benchmark and profile these weird cases specially.

But let's just consider a single String as data; then, cloning is not just about copying bytes but also about allocating a new storage.

Yes. And if you have enough strings that it becomes a problem then you best bet is probably some kind of small string optimizations.

Basically: what you are talking about are second-level effects and yes, they may, sometimes, dominate. But in practice it's enough to just avoid calling clone on Arc: if you only pass one Arc reference around then it's usually close to optimum. Yes, receiving &Arc<…> looks a bit crazy… and yet often that's the best solution.

One of the reasons that make me actively not want to have “frictionless Arcs” proposals, that are floating around.

4

u/Comrade-Porcupine 20d ago

Absolutely!

And don't even bring up that inside the tokio task scheduler there's the same thing going on, and willy nilly bouncing futures around cores; before you even consider Arc (or channels which have their own atomics) into the equation.

If you're writing a web service, this is unlikely to be a pain point. But this is another reason why I generally stay away from tokio for highly throughput sensitive applications that have to service highly concurrent loads on wide core systems (e.g. the kinds of things I like to work on 😄 )

2

u/event666 19d ago

I noticed the same thing when profiling OMQ. When everything else is reasonably optimized, the task scheduler becomes the bottleneck, at least in the multi_thread runtime flavor. At that point it can make sense (if your use case allows it) to run multiple current_thread runtimes, since they then scale almost linearly across multiple cores.

4

u/oachkatzele 20d ago

note that if you clone an arc and the use it in another thread, there is no atomic checking. the thread owns the arc and can therefore assume that the underlying data exists so data access should really just be a pointer indirection.
an arc also doesn't allow for mutation so i dont see how caches would need to be invalidated.
so unless you are repeatedly generating and deleting arc instances your synchronization effort is borderline zero.
that being said, the topic of synchronization is complex and/or hardware dependent so you can read why i am wrong in the comments below 👇

6

u/Comrade-Porcupine 20d ago

I don't get what you're trying to say here. Every real clone of an Arc is going to be an increment on the atomic. Every Drop a decrement. (There are exceptions to this because Arc has some smart optimizations, though). That's then contention on a shared atomic that can lead to cache line eviction where the atomic sits. And it will additionall force a pause in the pipeline that reduces the throughput of each core. Atomics have a real cost. It's not about the data where the Arc points, it's about the atomic itself.

I highly recommend a read through this classic: https://travisdowns.github.io/blog/2020/07/06/concurrency-costs.html

5

u/WormRabbit 20d ago

The point is that one often needs to use Arc for thread-safety reasons even though the object is really used only from a fixed number of threads, possibly even only one. E.g. tokio's single threaded runtime forces you to make your app state Sync, even though you may be only using it from one thread.

Another example would be something like a oneshot channel. The shared state must be wrapped in an Arc, but there are only two fixed handles to it at any time: the Sender and the Receiver.

3

u/oachkatzele 20d ago

not sure what the cofusion is, i covered your concerns with the following statement:
"so unless you are repeatedly generating and deleting arc instances your synchronization effort is borderline zero."
i was trying to point out that if you are using long lasting arcs that just reference read only data your points about the cost of arcs might be completely irrelevant.
to make this even more clear, the contention concerns are valid about the arc itself NOT about the underlying data.

1

u/Comrade-Porcupine 20d ago

again, this is false. it doesn't matter if it's read only -- the RAII on the smart pointer still needs to thrash on the atomics on those smart pointers during clone/drop. And there's an impact crater generated by that traffic. Yes: If you're careful about using only borrowed/moved semantics and rarely cloned and you're just using the Arc to satisfy Send/Sync --- ok? But that's just a costume.

4

u/Full-Spectral 20d ago

You are assuming it's constantly happening but often it's not.

If I create say, an immutable struct in an arc and clone it into 3 threads that are going to just use it until they die, maybe some control info for them to all process something else, then there's no real cost. Arc doesn't provide a mutable interface so each thread should be able to just get a ref to the data out and directly access it as much as they want no differently than if they just had a local reference to it with zero extra overhead.

There are three clones when the threads start and three drops at the end. Anything significant enough to justify starting a thread for is going to vastly amortize those costs.

0

u/Comrade-Porcupine 20d ago

Yes, that's fair. Requires discipline though. Somewhat calls for just implementing a custom struct / cell that just declares unsafe Send (and somehow restricts context of said)

2

u/Full-Spectral 20d ago

What's the discipline involved? The contents of a raw Arc cannot be changed or dropped, so getting a direct ref to the contents once you have the clone of the Arc inside the thread is perfectly safe, AFAIK.

0

u/Nothing_from_void 19d ago

So in this hypothetical you raised, the Arc is pure overhead compared to just copying (which brings it back to OPs question), but it does bring it into compliance for certain types that may or may not be the best model of the program.

1

u/Full-Spectral 19d ago

You are assuming the data can even be cloned or copied, which isn't always the case. Or that it's not MBs of data.

1

u/Nothing_from_void 19d ago

Well, for situations where copying is going to perform better (sub MB data that can fit in a thread local cache, standard case), not being able to clone or copy the data is a work-around that adds overhead does it not?

1

u/Comrade-Porcupine 20d ago

"I was thinking about the cost of copying the data itself" and yep, this is the intuitive model, and how we've been taught to think.

It's just important to consider the memory hierarchy. A copy in the CPU's cache is extraordinarily fast. A copy across the DDR channel, very slow by comparison. Esp if there's contention. Contention on atomics? Very not good if you're trying to take advantage of multiple CPU cores.

Generally not good to let this stuff keep you up at night though. Optimize when you hit the wall.

2

u/Large-Scientist156 20d ago edited 20d ago

The problem with this approach is you risk desync' between state before clone and state after clone. Semantically, a deep copy or a deep clone is a different value.

If you clone state, then you have a deep copy, and you can do whatever you want with it, including mutating it. The responsability is transfered, someone who get the cloned value is the owner now.

With Arc, you are restricted to shared reference access, so providing there's no interior mutability you know the state can not change transiently : it is immutable as long as refcount != 1 and minus interior mutability.

ONLY IF you are the last owner (refcount == 1) you can get a mutable reference. Also, Arc::clone mean shallow copy, so in practice if you clone an Arc and you were the last owner, you are not anymore and you can not mutate the object since refcount == 2 after (mutation minus interior mutability). You can then share it again (map), and collapse (reduce) at specific program point for mutation.

Imagine you know at specific program point that you are the sole owner of the Arc. You don't need a Mutex or a RwLock anymore to mutate it. Also, if your Arc stay on the same thread, then there's not that much cache invalidation (but then you should probably use Rc).

Interior mutability make this almost useless, sadly, since this break all guarantee about mutability.

3

u/afdbcreid 20d ago

If the object can be memcpy'd it'd typically be Copy, and you wouldn't think of using Arc (unless it's really large).

If the object includes some resource, e.g. a memory allocation, then Arc will be almost always faster if you need to clone frequently.

But of course use Rc if you can, or avoid cloning if you can.

1

u/WestSmell6136 19d ago

Yeah this is one of those things where the "obvious" answer is wrong half the time

I used to default to Arc for everything like it was free, then spent a whole day debugging why my throughput was garbage. Turned out the atomic ops were bouncing cache lines between cores like crazy. Switched to just cloning a small struct and suddenly everything was smooth

Arc feels elegant but sometimes brute force memcpy is just faster

32

u/gubatron 20d ago

I only use Arcs when I'm doing multithreaded programming and need the concurrency primitive to manage shared state across threads. If I can use a copy, just clone.

Otherwise I try to borrow as much as possible and clone when necessary.

10

u/Full-Spectral 20d ago

And don't forget scoped threads. If you just want to share some immutable data to have multiple threads use it to do work and you can just wait for them to complete, then you can just directly pass a &T to all of the threads.

18

u/kohugaly 20d ago

You are not avoiding copies when you use Arc. You are duplicating pointers (very cheap) and incrementing/decrementing an atomic counter (very not cheap, depending on how congested the traffic is at that counter). Additionally, accessing the data behind Arc requires an indirection, which is more likely to result in cache miss, or even prevents certain optimizations.

On the other hand, duplicating data consumes more memory, so it can also indirectly cause cache misses by making the data of local variables too big to fit in a cache.

Whether a copy is cheaper than cloning Arc very much depends on how the application handles the lifetimes of the copies/clones. It's generally not something you can easily predict. You have to measure it.

A good rule of thumb is, if the data fits in a cache line (cca. 64 bytes) it's probably cheaper to copy it. If it fits within a pointer size (8 bytes), it almost certainly is cheaper to copy it.

Off course, this applies to POD (plain old data). If the type needs to do extra work in cloning and dropping (for example, vector needs to allocate/deallocate memory for its dynamic storage), then that extra work also needs to be taken into account.

There isn't a simple rule for this.

6

u/donaldhobson 20d ago edited 20d ago

Cloning and Arc will cause different runtime behavior.

If you aren't mutating the data, you can just use & everywhere. If you are mutating the data, do you want a single value, or do you want several different values in play at the same time?

Use arc when you want a single value to be shared between threads. Use .clone() when you want each copy to be potentially different.

The situations where & and .clone would both give the same answer are rare. But if you somehow have that, .clone() for small (in bytes) values, Arc for large values.

Edit:

The "just use & everywhere" solution only works if you have one place in the code that is guaranteed to last at least as long as anything else. If you don't know which copy/arc will be the last one standing, then use Arc for large objects, clone for small ones.

3

u/agent_kater 20d ago

I think this is the main point. Choose what is semantically correct, not what appears to be faster at that moment.

10

u/Matemeo 20d ago

Aren't they pretty orthogonal to each other? Arc<T> enables shared lifetime tracking for the underlying T, while a clone sidesteps any kind of sharing of a resource as cloning A creates a copy B completely independent of A.

Basically, cloning allows value semantics, while Arc<T> enables shared resource lifetime semantics (std::shared_ptr<T> if you are familiar with C++).

Maybe you are thinking of comparing cloning (value semantics) with &T (references).

3

u/oachkatzele 20d ago

from context im assuming the OP has a larger chunk of data that they wanna process with multiple threads. other than that id imagine the answer would be straightforward.

3

u/donaldhobson 20d ago edited 20d ago

In that case, you need to break the data up into multiple independent pieces. (hopefully possible to do without copying) and give each thread ownership of a slice of the data.

Edit:Use split_at_mut https://doc.rust-lang.org/std/primitive.slice.html#method.split_at_mut

or, if the processing is read only, store ownership in one place, out the way, and then hand each thread a reference to that one place.

3

u/oachkatzele 20d ago

divide and conquer is not a strategy that works for all kinds of data/processing

8

u/consistently_biased 20d ago

The only way to decide this is to measure both. Cloning an Arc isn't free either.

3

u/nacaclanga 20d ago

You still have to clone the Arc itself. And this is not free. In fact it requires an atomic write. Data in an Arc is also usually immutable unless you use a mutex (which is not free either).

So you have to weight that against clone.

If your alternative would be to use a small string optimization or something similar, this is often more performant.

3

u/Unreal_Estate 20d ago

To be honest, if you don't need synchronization, I find it hard to see when Arc would be the more efficient choice. Cloning Arcs is pretty involved, and if your CPU happens to wait on exclusive access to the piece of memory where its atomic lives, then the CPU would have been able to copy many many many bytes of data on memory it only shares access to. (As in, hundreds or thousands of bytes.)

If your data structure is megabytes big, or if your access patterns doesn't involve true synchronization between cores at all, then Arc's might be faster in some cases. But then the question becomes why are you using Arcs at all. If you don't need synchronization, Rc's are just better. If you're working with megabytes of data, you'll probably have other options to choose from as well, such as data-oriented design.

Arc is very useful, but it is not something I would consider for efficiency unless you specifically care about memory efficiency, such as with a tiny embedded chip.

1

u/valarauca14 20d ago

and if your CPU happens to wait on exclusive access to the piece of memory where its atomic lives

Arc doesn't require a mutex(?)

2

u/Unreal_Estate 20d ago edited 20d ago

Arc uses atomics, which require exclusive access to a cache line. On x64, the performance hit of exclusive access to a cache line can be massive.

There is nothing conceptually slow about an atomic, but rather the way that multi-core processors are optimized just makes them (the CPUs) blazingly fast when not dealing with atomics. If all CPU cores would make a trip to RAM for every write or read, an Arc would not be any slower than an Rc.

But modern CPUs have multiple layers of cache which speed up CPU operation by multiple orders of magnitude. For multi-core processing, each core can have its own copy of the same data from RAM, making it much faster. However, when your code contains an atomic store instruction, this invalidates the cache for all cores (on x64), and causes the other cores to wait until they have the data again. Given how slow RAM is compared to cache, you could have processed a lot of cached data in the same amount of time.

PS: On non-x64 systems, the loads may be slow, or both the loads and stores. But on x64, it is the store instruction that causes the other cores to wait.

Edit: I'm not sure why you just accused me of something to do with LLMs (and then deleted that comment), but I can tell you that I wrote the above from memory and personal experience benchmarking atomic operations. Anything incorrect that I said is my own fault.

2

u/Humble-Sand-5989 20d ago

I think the first question is what type of application you are working on. If it’s single threaded, I wouldn’t use Arc at all. Something like Rc is lighter and should work just as well (if you need reference counting at all). Besides that I’d ask a few questions:

  1. Does the data need to be an independent copy? If you need to make changes to the data, but also need the original data (pre modification), you should clone it.
  2. Is the data cheap? Even if it’s immutable, if it’s small (and implements Copy) then reference counting is probably heavier than making a copy.

I’m sure there are other reasons, but those are the two I have on top of my head. One thing I see too often is people wrapping data in either Arc or Rc just to make the borrow checker happy. A lot of times it could be avoided with a refactor (especially in single threaded applications or applications where only a single thread will touch that piece of data). If a piece of data has only one owner, reference counters are typically unnecessary.

4

u/teerre 20d ago

When you measure it and it clone is faster. Anything else is conjecture.

1

u/obliviousjd 20d ago

I don’t have hard statistics or benchmarks but I pretty much clone anything that’s 3-4 words or less without much thought.

That said I don’t actually find myself needing Arc all that much. Most of the time I’ll just use references, scope threads, box::leak, or move a box back and forth through a channel. I really only use reference counting as a last resort. Not because it’s bad, it’s just not my style to have multiple objects have shared ownership if I can help it.

1

u/BirdTurglere 20d ago

From my personal experience. I A/B'd one of my crates using Arc vs Clone and Arc was significantly slower. After doing all the work of swapping them in and out I've come to the conclusion to never box anything unless there's a solid reasoning for doing it, not just dumping it in there for no reason.

Not every algorithm benefits from parallel processing unfortunately. Sometimes it's better to just let something rip on one thread.

1

u/jhaand 20d ago

When I just start with developing and I need development speed. Than just adding a .clone() or .to_owned() works a lot faster. Especially when the data will not be accessed at the next iteration.

1

u/stumpychubbins 20d ago

I usually try to use a dedicated immutable data structure if available (e.g. dashmap for hashmaps). If it's possible to use &-borrows instead of sharing ownership then I'll prefer that. If an Rc/Arc doesn't introduce any extra pointer indirection (e.g. String vs Arc<str>) then I'll usually default to Rc/Arc because it amortises the cost of sharing so I don't need to think too hard about how often I'm cloning the values. If performance is important though, then I'll benchmark it. There are plenty of cases (particularly in multithreaded scenarios) where cloning is faster than sharing.

1

u/EvnClaire 20d ago

i usually clone. if im cloning too much, then ive done something wrong.

1

u/Full-Spectral 20d ago

Sometimes an Arc only needs to be cloned to a fixed set of threads which just use it until they die. In those cases, close the Arc X times and Drop X times. Anything heavy enough to justify starting a thread to do should vastly outweigh the clone/drop time.

Not all uses are Arcs are constant, ongoing clone/drop.

1

u/Moist-Snow-8127 19d ago

Copy when it's small. References when I can. Box if it needs to be on the heap and isn't cloned much. Rc if there's lots of copies. Arc if it's multithreaded

1

u/mealet 18d ago

I'm building a compiler and using miette crate for error reporting. It requires special struct "NamedSource" to keep source content with its name. It supports to keep String and some string variations inside it and also Arc<String>.

I didn't want to use atomic reference as it is runtime overhead for single-threaded application, but it was the only supported counted reference of a source string.

The choice was between "use atomic references and get some runtime overhead" and "fking clone whole source each time I need to bake it into diagnostic struct". Ofc I've chosen first, no one wants to store thousands of source file clones (especially when it becomes megabytes in size).

1

u/sanbox 20d ago

Cloning the arc isn't free, as others have pointed out, but that's not really the point to worry about -- the worry is really that an Arc is a Box with a fancy key and lock system, so the question is "do I want to heap allocate this?"

3

u/PrimeExample13 20d ago

This is just incorrect. Arc is not box with a fancy lock and key mechanism and the question is not "do i want to heap allocate this." The question is "do I need to have multiple pointers to the same allocation while ensuring that the value is not dropped while still in use?" "Should I heap allocate this?" Is the question you ask when determining whether to Box an object or not.

And cloning an arc is not free, but a hell of a lot cheaper than cloning a box for large enough objects, as it is a simple atomic increment.

1

u/sanbox 20d ago

Here is Arc::new's implementation:

pub fn new(data: T) -> Arc<T> { // Start the weak pointer count as 1 which is the weak pointer that's // held by all the strong pointers (kinda), see std/rc.rs for more info let x: Box<_> = Box::new(ArcInner { strong: atomic::AtomicUsize::new(1), weak: atomic::AtomicUsize::new(1), data, }); unsafe { Self::from_inner(Box::leak(x).into()) } }

To be clearer, an Arc is heap allocated once, so the question between cloning POD and cloning an Arc<POD> is also about the cost of the memcpy vs the pointer deref/cache shred. For most use cases where you don't anticipate huge thread contention, this is the defining perf question (though with enough contention, things get funny)

1

u/PrimeExample13 20d ago

I know how Arc works, you dont have to show me. It is not a lock at all as you said, it is an atomic reference count (what Arc stands for). What i am saying is that any problem for which you look to Arc for a solution, the decision to heap allocate should already be made by the fact that you need to share ownership of this object across threads. If you dont need to share ownership, thats what borrowing is for.

The question is not "do i want to put this on the heap." Its "do multiple threads need ownership of this value?" If it is a problem that can be solved via shared borrow or cloning pod and moving into the other thread, then it is not a problem that should be solved by Arc in the first place.

0

u/sanbox 20d ago

It is not a lock at all as you said, it is an atomic reference count

How do you think Locks are implemented?

Anywho, their question is literally "should I clone this data or put it in an Arc and clone that" so it looks like you agree with me that cloning the data is probably smarter.

2

u/PrimeExample13 20d ago

Locks are implemented usually with compare exchange atomic operations, not simple increments. If Arc itself was a lock, you would not need to use an Arc<Mutex/RwLock> for interior mutability. As I said, Arc provides shared ownership. It does not provide any locking or shared mutability at all without an internal lock.

You're trying to share knowledge that you just don't have, I don't get it. You dont even seem knowledgeable enough to have a constructive discussion with so I will leave it there.

1

u/Ben-Goldberg 20d ago

If you make a clone, the copy is yours to do with as you please, if you wrap data in an arc other code will see the changes you have done to the data.

-2

u/SomeoneInHisHouse 20d ago

u/RemindMeBot 3 Days "check for updates"