https://redmine.ruby-lang.org/https://redmine.ruby-lang.org/favicon.ico?17113305112016-07-21T05:04:32ZRuby Issue Tracking SystemRuby master - Feature #12607: Ruby needs an atomic integerhttps://redmine.ruby-lang.org/issues/12607?journal_id=597342016-07-21T05:04:32Zshyouhei (Shyouhei Urabe)shyouhei@ruby-lang.org
<ul><li><strong>Related to</strong> <i><a class="issue tracker-2 status-6 priority-4 priority-default closed" href="/issues/12463">Feature #12463</a>: ruby lacks plus-plus</i> added</li></ul> Ruby master - Feature #12607: Ruby needs an atomic integerhttps://redmine.ruby-lang.org/issues/12607?journal_id=597362016-07-21T05:29:46Zsawa (Tsuyoshi Sawada)
<ul></ul><p>Do we want to have another integer variant just after having <code>Fixnum</code> and <code>Bignum</code> been excluded in favor of the <code>Integer</code> class?</p> Ruby master - Feature #12607: Ruby needs an atomic integerhttps://redmine.ruby-lang.org/issues/12607?journal_id=597412016-07-21T12:26:39Zrosenfeld (Rodrigo Rosenfeld Rosas)rr.rosas@gmail.com
<ul></ul><p>I guess the proper way of helping users to write concurrent code in Ruby is to provide concurrent classes in stdlib rather than relying on third-party gems.</p>
<p>I don't think it makes sense to write something like 1.+=(value) since 1 is immutable. It makes more sense to provide some Counter class to stdlib which would be thread-safe. And a ConcurrentHash and ConcurrentArray. Those alone would already help a lot.</p> Ruby master - Feature #12607: Ruby needs an atomic integerhttps://redmine.ruby-lang.org/issues/12607?journal_id=598992016-08-03T13:58:41Zsawa (Tsuyoshi Sawada)
<ul></ul><p>Sorry, I do not clearly understand why the original Integer class cannot be made atomic. If it is not possible or if there would be any problem, can someone explain why that is?</p>
<p>Ideally, I think it would be better if the original Integer class can be modified in some way rather than additional classes being introduced.</p> Ruby master - Feature #12607: Ruby needs an atomic integerhttps://redmine.ruby-lang.org/issues/12607?journal_id=599012016-08-03T18:57:46Zrosenfeld (Rodrigo Rosenfeld Rosas)rr.rosas@gmail.com
<ul></ul><p>This is not really about Integer handling +=. there's no "+=" method. It's a short-hand syntax: a += 1 is currently expanded to something like "a = a + 1". One proposal is to change it to be expanded to something like "synchronized(a = a + 1)".</p>
<p>Other proposals could offer other concurrent classes so that one would write something like "counter = Counter.new(initial_value)" (0 by default) and "counter.inc(step)" (1 by default). But it doesn't make sense to me to call "count = Integer.new(initial_value)" since Integer instances are constants.</p> Ruby master - Feature #12607: Ruby needs an atomic integerhttps://redmine.ruby-lang.org/issues/12607?journal_id=600382016-08-10T04:14:57Zshyouhei (Shyouhei Urabe)shyouhei@ruby-lang.org
<ul><li><strong>Status</strong> changed from <i>Open</i> to <i>Assigned</i></li><li><strong>Assignee</strong> set to <i>ko1 (Koichi Sasada)</i></li></ul><p>I heard from Koichi that he has an opinion on this. Please respond.</p> Ruby master - Feature #12607: Ruby needs an atomic integerhttps://redmine.ruby-lang.org/issues/12607?journal_id=603612016-09-04T16:35:44Zjwmittag (Jörg W Mittag)Ruby-Lang@JoergWMittag.De
<ul></ul><p>Tsuyoshi Sawada wrote:</p>
<blockquote>
<p>Do we want to have another integer variant just after having <code>Fixnum</code> and <code>Bignum</code> been excluded in favor of the <code>Integer</code> class?</p>
</blockquote>
<p>Well, it's not really another integer variant. It's really not a number at all. It's a concurrency primitive (that happens to be a number).</p> Ruby master - Feature #12607: Ruby needs an atomic integerhttps://redmine.ruby-lang.org/issues/12607?journal_id=612982016-11-05T10:00:43Zko1 (Koichi Sasada)
<ul></ul><p>At first, we need a box of integer, not an atomic integer.</p>
<p>Like that:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">box</span> <span class="o">=</span> <span class="no">IntegerBox</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="n">box</span><span class="p">.</span><span class="nf">increment</span>
<span class="n">box</span><span class="p">.</span><span class="nf">increment</span>
<span class="n">box</span><span class="p">.</span><span class="nf">to_i</span> <span class="c1">#=> 2</span>
</code></pre>
<p>This IntegerBox can be implemented by the following code:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">IntegerBox</span>
<span class="k">def</span> <span class="nf">initialize</span> <span class="n">n</span>
<span class="vi">@n</span> <span class="o">=</span> <span class="n">n</span>
<span class="vi">@m</span> <span class="o">=</span> <span class="no">Mutex</span><span class="p">.</span><span class="nf">new</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">increment</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">1</span>
<span class="vi">@m</span><span class="p">.</span><span class="nf">synchonization</span><span class="p">{</span> <span class="vi">@n</span> <span class="o">+=</span> <span class="n">i</span> <span class="p">}</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">to_i</span>
<span class="vi">@n</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<p>I'm not sure such small part should be include in Ruby's core library.</p>
<p>BTW, concurrent-ruby supports AtomicFixnum <a href="http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/AtomicFixnum.html" class="external">http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/AtomicFixnum.html</a>.</p>
<pre><code>af = Concurrent::AtomicFixnum.new(0)
af.increment
af.increment
af.value #=> 2
</code></pre>
<p>It has <code>update</code> method to set new value. It seems useful.</p>
<pre><code>v = af.value
new_v = v + 5
af.update{ new_v }
</code></pre>
<p>But this small code doesn't work as intended.<br>
This is why I think thread-programming is not easy, even if there is cool threading tools.</p> Ruby master - Feature #12607: Ruby needs an atomic integerhttps://redmine.ruby-lang.org/issues/12607?journal_id=612992016-11-05T10:01:02Zko1 (Koichi Sasada)
<ul><li><strong>Status</strong> changed from <i>Assigned</i> to <i>Feedback</i></li></ul> Ruby master - Feature #12607: Ruby needs an atomic integerhttps://redmine.ruby-lang.org/issues/12607?journal_id=613002016-11-05T12:11:02Zshyouhei (Shyouhei Urabe)shyouhei@ruby-lang.org
<ul></ul><p>So you mean we don't need atomic integer because concurrent-ruby sucks? I don't think that's a valid reason to reject this.</p> Ruby master - Feature #12607: Ruby needs an atomic integerhttps://redmine.ruby-lang.org/issues/12607?journal_id=613032016-11-05T13:27:29Zko1 (Koichi Sasada)
<ul></ul><blockquote>
<p>So you mean we don't need atomic integer because concurrent-ruby sucks? I don't think that's a valid reason to reject this.</p>
</blockquote>
<p>I didn't mean that. After "BTW" is only my impression about threading.</p> Ruby master - Feature #12607: Ruby needs an atomic integerhttps://redmine.ruby-lang.org/issues/12607?journal_id=613072016-11-05T13:55:43Zshyouhei (Shyouhei Urabe)shyouhei@ruby-lang.org
<ul></ul><p>OK, then let's discuss the needs of this class.</p>
<p>An obvious benefit of this class to be in core is that we don't even need Mutex in theory. Because we have GVL, if we do this in core we can just increment the backend integer and that should suffice. No overheads must be there. Even when we give up GVL someday in a future, we could still write this using LOCK CMPXCHG or something equivalent. Or JRuby people might want to implement this class using java.util.concurrent.atomic.AtomicLong. Either way, the resulting implementation must be much smarter than the mutex-synchronized pure-ruby implementation.</p> Ruby master - Feature #12607: Ruby needs an atomic integerhttps://redmine.ruby-lang.org/issues/12607?journal_id=613112016-11-05T14:14:31Zko1 (Koichi Sasada)
<ul></ul><p>I agree with performance advantages.</p>
<p>I'm negative to introduce this feature because it is too hard.</p>
<p>People who know well about thread programming can use this feature using concurrent-ruby and I don't want to recommend it for people who don't know about threading well. Also I think such convenient tools can lead misuse. I think using Mutex explicitly is more easy to understand the behavior of application. So I don't want to encourage these by introducing ruby std libs.</p> Ruby master - Feature #12607: Ruby needs an atomic integerhttps://redmine.ruby-lang.org/issues/12607?journal_id=716202018-04-24T03:06:03Zshyouhei (Shyouhei Urabe)shyouhei@ruby-lang.org
<ul><li><strong>Has duplicate</strong> <i><a class="issue tracker-2 status-5 priority-4 priority-default closed" href="/issues/14706">Feature #14706</a>: Atomic Integer incr/decr</i> added</li></ul> Ruby master - Feature #12607: Ruby needs an atomic integerhttps://redmine.ruby-lang.org/issues/12607?journal_id=716232018-04-24T12:22:11Zrosenfeld (Rodrigo Rosenfeld Rosas)rr.rosas@gmail.com
<ul></ul><p>Should this issue be assigned to Matz?</p> Ruby master - Feature #12607: Ruby needs an atomic integerhttps://redmine.ruby-lang.org/issues/12607?journal_id=716602018-04-26T21:04:15Zmperham (Mike Perham)mperham@gmail.com
<ul></ul><p>I don't like pulling in the entire concurrent-ruby gem only for a few lines of code. I've implemented my own slow but thread-safe Counter here:</p>
<p><a href="https://github.com/mperham/sidekiq/blob/b58c505df3501d8c1de5207552d68dd2d9abea31/lib/sidekiq/processor.rb#L189" class="external">https://github.com/mperham/sidekiq/blob/b58c505df3501d8c1de5207552d68dd2d9abea31/lib/sidekiq/processor.rb#L189</a></p>
<p>I would love to see an optimized atomic implementation in stdlib!</p> Ruby master - Feature #12607: Ruby needs an atomic integerhttps://redmine.ruby-lang.org/issues/12607?journal_id=716652018-04-27T02:11:59Zshyouhei (Shyouhei Urabe)shyouhei@ruby-lang.org
<ul></ul><p>We have already shown the benefits of atomic integers very well. What Ko1 says is the downside of it ("it is too hard for mere mortals"). Now is the time for us to tell him it's not that bad. Showing another benefits of concurrency does not help.</p> Ruby master - Feature #12607: Ruby needs an atomic integerhttps://redmine.ruby-lang.org/issues/12607?journal_id=716702018-04-27T07:14:12ZStudent (Nathan Zook)blogger@pierian-spring.net
<ul></ul><p>I think that you might not have understood his concern. Getting multi-threaded code right is hard, no matter the primitives available. "People who know well about thread programming can use this feature using concurrent-ruby and I don't want to recommend it for people who don't know about threading well." The issue is about setting traps for the unwary.</p>
<p>I think that it is interesting that the original discussing was around doing an atomic "++" -- because "++" and even "+" are not atomic in C.</p>
<p>I spent a few years doing assembler, mostly in PowerPC. PowerPC (as of 10 years ago) did not have any instruction comparable to the X86 compare and exchange. It does make sense, strictly from a performance standpoint, to have a mutex capability that can be dropped down to a few integer instructions. But doing that requires that you understand exactly how those instructions work in the major architectures. Given that the Ruby Integer class can spill any cache line, it is nonsensical to talk about fast locks using it. You would want a register-sized value in some sort of wrapping class.</p>
<p>In my mind, it is a matter of boosting performance at the risk of creating traps for unwary programmers. So far as I'm concerned, ruby's metaprogramming capacity has already filled the wind in those sails. Therefore, I would be in favor of a robust, fast atomic capacity. But not with the Integer class. You really need to go with a 32-bit integer to maintain compatibility.</p>
<p>If you need more than 32 bits in your concurrency primitives, you had BETTER be brewing your own extensions. ;)</p> Ruby master - Feature #12607: Ruby needs an atomic integerhttps://redmine.ruby-lang.org/issues/12607?journal_id=716842018-04-27T18:07:24Zrosenfeld (Rodrigo Rosenfeld Rosas)rr.rosas@gmail.com
<ul></ul><p>Just to make it clearer, since I was mentioned by Shyouhey in the issue's description, performance has never been the reason why I asked for support for atomic operations such as incrementing an integer. It might seem like it is the case because the mentioned ticket (<a class="issue tracker-2 status-6 priority-4 priority-default closed" title="Feature: ruby lacks plus-plus (Rejected)" href="https://redmine.ruby-lang.org/issues/12463">#12463</a>) is about performance but my comment there was just incidental as it mentioned a new syntax change to Ruby and I felt it would be interested if the new syntax wasn't used for performance issues only, but that could be a chance to improve a common operation which is an atomic incrementing operation. Not a performance improvement, but an improvement in the sense that writing such operations would be greatly simplified by not requiring us to write an explicit mutex block for those operations.</p>
<p>I think we shouldn't put too much focus on the performance subject while discussing such an operator introduction. Mike Perham, on the other hand, didn't suggest a new syntax as far I as could understand his request. While "i++" is certainly shorter than "count = Thread::Count.new; count.inc" the latter doesn't involve any changes to the language syntax and it's certainly much shorter than using a mutex or having to create a counter class ourselves for every project where we need an atomic increment operation.</p>
<p>So, I guess we all agree that it's possible to improve atomic increments, but Koichi is concerned about people doing wrong multi-thread programming just because Ruby would introduce some Thread-safe classes or something like that.</p>
<p>I don't really understand that being a reason to not implement something a lot of people would find useful. For example, one should always use the block version when opening a file so that it automatically closes it after running the block whenever it's possible. However Ruby allows people to open and close the file in separate actions and that too could lead to bugs, but that didn't prevent Ruby from providing an open variation without a block (requiring an explicit close).</p>
<p>Finally, I think this issue is mostly about changing Ruby's syntax while Mike's is about adding a new Thread::Counter core class. Those are two different and interesting proposals but they should be probably discussed independently as they don't really conflict with each other, even if Koichi's concerns apply to both of them.</p>
<p>I just don't think Ruby can protect developers from themselves so it shouldn't even try that. It doesn't work IMHO but it prevents those who are familiar with thread programming to do it with less cerimony in Ruby.</p>
<p>Anyway, I just suggested that maybe Matz could give us his opinion on the subject. That's because Shyouhei assigned this ticket to Koichi and set the state to "Feedback" and Koichi has already provided his feedback. So, if it's decided already, this issue's state should be changed to Rejected, but if it's still open to discussion and since Koichi has already specified his opinion, maybe we should assign it to Matz now that Koichi has already provided feedback on this.</p>
<p>But I'd probably leave the performance reason off the table when discussing both tickets. They don't seem as much relevant as the easiness of writing multi-threaded application, but maybe that's just my opinion.</p> Ruby master - Feature #12607: Ruby needs an atomic integerhttps://redmine.ruby-lang.org/issues/12607?journal_id=716852018-04-27T18:08:07Zrosenfeld (Rodrigo Rosenfeld Rosas)rr.rosas@gmail.com
<ul><li><strong>Tracker</strong> changed from <i>Bug</i> to <i>Feature</i></li><li><strong>Backport</strong> deleted (<del><i>2.1: UNKNOWN, 2.2: UNKNOWN, 2.3: UNKNOWN</i></del>)</li></ul><p>I'm not sure if type was set to Bug on purpose, so I'm changing it to Feature instead, but feel free to change it back to Bug if you really think it's a bug.</p> Ruby master - Feature #12607: Ruby needs an atomic integerhttps://redmine.ruby-lang.org/issues/12607?journal_id=716872018-04-27T18:19:13Zrosenfeld (Rodrigo Rosenfeld Rosas)rr.rosas@gmail.com
<ul></ul><p>Oh, sorry, I just noticed this issue isn't about changing Ruby's syntax, it was just my comment on the other related issue that mentioned that :P So this is basically the same request as Mike's.</p> Ruby master - Feature #12607: Ruby needs an atomic integerhttps://redmine.ruby-lang.org/issues/12607?journal_id=898092021-01-06T12:06:57ZEregon (Benoit Daloze)
<ul></ul><p>FWIW, I think an atomic integer makes sense in core, because it's easier to optimize it that way.<br>
But, since it seems a lot nicer to support fixnums and bignums (not just fixnums), and notably the Integer class is now used for both, I think the better thing to expose is an AtomicReference.<br>
As an added benefit, that has other usages and notably adds a general compare-and-swap operation.</p>
<p>TruffleRuby for instance has such a class, which is currently used by concurrent-ruby:<br>
<a href="https://github.com/oracle/truffleruby/blob/07d62320c0d83efd6f7f99a805d6f61d1e03d60d/doc/user/truffleruby-additions.md#atomic-references" class="external">https://github.com/oracle/truffleruby/blob/07d62320c0d83efd6f7f99a805d6f61d1e03d60d/doc/user/truffleruby-additions.md#atomic-references</a><br>
<a href="https://github.com/oracle/truffleruby/blob/07d62320c0d83efd6f7f99a805d6f61d1e03d60d/src/main/ruby/truffleruby/core/truffle/truffleruby.rb#L23-L51" class="external">https://github.com/oracle/truffleruby/blob/07d62320c0d83efd6f7f99a805d6f61d1e03d60d/src/main/ruby/truffleruby/core/truffle/truffleruby.rb#L23-L51</a></p> Ruby master - Feature #12607: Ruby needs an atomic integerhttps://redmine.ruby-lang.org/issues/12607?journal_id=898152021-01-06T15:17:27Zdsisnero (Dominic Sisneros)dsisnero@gmail.com
<ul></ul><p>I agree this should be added to core because of ractor and <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: Expose atomic operation macros with RUBY prefix (Closed)" href="https://redmine.ruby-lang.org/issues/17433">#17433</a>.</p> Ruby master - Feature #12607: Ruby needs an atomic integerhttps://redmine.ruby-lang.org/issues/12607?journal_id=898782021-01-12T07:54:48Zko1 (Koichi Sasada)
<ul></ul><p>I believe introducing STM <a href="https://bugs.ruby-lang.org/issues/17261" class="external">https://bugs.ruby-lang.org/issues/17261</a> is more general for this purpose.</p> Ruby master - Feature #12607: Ruby needs an atomic integerhttps://redmine.ruby-lang.org/issues/12607?journal_id=898852021-01-12T11:13:16ZEregon (Benoit Daloze)
<ul></ul><p>ko1 (Koichi Sasada) wrote in <a href="#note-24">#note-24</a>:</p>
<blockquote>
<p>I believe introducing STM <a href="https://bugs.ruby-lang.org/issues/17261" class="external">https://bugs.ruby-lang.org/issues/17261</a> is more general for this purpose.</p>
</blockquote>
<p>True, but an atomic integer remains useful even if there is an STM.<br>
An STM is overkill and much slower if all someone wants is atomic counters.</p> Ruby master - Feature #12607: Ruby needs an atomic integerhttps://redmine.ruby-lang.org/issues/12607?journal_id=899122021-01-13T07:32:12Zko1 (Koichi Sasada)
<ul></ul><p>Eregon (Benoit Daloze) wrote in <a href="#note-25">#note-25</a>:</p>
<blockquote>
<p>True, but an atomic integer remains useful even if there is an STM.<br>
An STM is overkill and much slower if all someone wants is atomic counters.</p>
</blockquote>
<p>TVar proposed in <a href="https://bugs.ruby-lang.org/issues/17261" class="external">https://bugs.ruby-lang.org/issues/17261</a> has <code>#increment</code> method and it is enough fast.</p> Ruby master - Feature #12607: Ruby needs an atomic integerhttps://redmine.ruby-lang.org/issues/12607?journal_id=899312021-01-13T18:05:18ZDan0042 (Daniel DeLorme)
<ul></ul><p>What is the use case for this atomic integer? I sometimes have a use for an atomic monotonically increasing counter, but that could be satisfied with a very simple API like <code>n = MyCounter.next</code><br>
On the other hand I've never felt the need for decrement or reset or in fact any integer atomic operations other than <code>+= 1</code><br>
2¢</p> Ruby master - Feature #12607: Ruby needs an atomic integerhttps://redmine.ruby-lang.org/issues/12607?journal_id=899342021-01-13T21:45:08Zchrisseaton (Chris Seaton)chris@chrisseaton.com
<ul></ul><blockquote>
<p>What is the use case for this atomic integer?</p>
</blockquote>
<p>For example for issuing unique IDs across multiple Ractors.</p>
<blockquote>
<p>or in fact any integer atomic operations other than += 1</p>
</blockquote>
<p>For example a CAS to update a bank balance is a common requirement.</p> Ruby master - Feature #12607: Ruby needs an atomic integerhttps://redmine.ruby-lang.org/issues/12607?journal_id=899352021-01-13T22:40:00Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<ul></ul><p>chrisseaton (Chris Seaton) wrote in <a href="#note-28">#note-28</a>:</p>
<blockquote>
<blockquote>
<p>What is the use case for this atomic integer?</p>
</blockquote>
<p>For example for issuing unique IDs across multiple Ractors.</p>
</blockquote>
<p>You can achieve something similar with <code>Object.new.object_id</code></p>
<blockquote>
<blockquote>
<p>or in fact any integer atomic operations other than += 1</p>
</blockquote>
<p>For example a CAS to update a bank balance is a common requirement.</p>
</blockquote>
<p>Why atomic though... This can all be achieved with a Ractor.</p> Ruby master - Feature #12607: Ruby needs an atomic integerhttps://redmine.ruby-lang.org/issues/12607?journal_id=899362021-01-13T23:45:47Zchrisseaton (Chris Seaton)chris@chrisseaton.com
<ul></ul><blockquote>
<p>You can achieve something similar with Object.new.object_id</p>
</blockquote>
<p>You may want them to be consecutive.</p>
<blockquote>
<p>Why atomic though... This can all be achieved with a Ractor.</p>
</blockquote>
<p>A Ractor send has relatively high synchronisation overhead - an atomic integer is conventionally implemented at the cache-coherence level and is lock-free.</p>
<p>I can only offer that I use atomic integers for many things fairly frequently in my working life.</p> Ruby master - Feature #12607: Ruby needs an atomic integerhttps://redmine.ruby-lang.org/issues/12607?journal_id=899382021-01-14T02:25:01ZDan0042 (Daniel DeLorme)
<ul></ul><p>chrisseaton (Chris Seaton) wrote in <a href="#note-28">#note-28</a>:</p>
<blockquote>
<p>For example for issuing unique IDs across multiple Ractors.</p>
</blockquote>
<p>That's the "monotonically increasing counter" case I was talking about, and for that I really think a generator (like the above <code>MyCounter.next</code>) is a more appropriate tool than an atomic integer.</p>
<blockquote>
<p>For example a CAS to update a bank balance is a common requirement.</p>
</blockquote>
<p>Can you elaborate? In my experience financial transactions will be handled at the DB level. Not to mention you usually need more intricate synchronization such as atomic update of the balances of <em>two</em> accounts. I don't quite see how an atomic integer is a realistic tool for money stuff.</p>
<blockquote>
<p>I can only offer that I use atomic integers for many things fairly frequently in my working life.</p>
</blockquote>
<p>That's great, so you should easily be able to give a concrete example <em>other</em> than generating unique sequential IDs. I'm quite curious, so looking forward to a nice realistic use case.</p> Ruby master - Feature #12607: Ruby needs an atomic integerhttps://redmine.ruby-lang.org/issues/12607?journal_id=899392021-01-14T02:34:04Zchrisseaton (Chris Seaton)chris@chrisseaton.com
<ul></ul><blockquote>
<p>In my experience financial transactions will be handled at the DB level.</p>
</blockquote>
<p>So imagine you're implementing the DB then.</p>
<blockquote>
<p>Can you elaborate?</p>
</blockquote>
<p>If you want to perform an arbitrary operation on a counter you can do that atomically, without a lock, by doing a CAS between the value as it currently is and the value you want to set it to, even if it takes you a long time to compute that. Atomics let you do this in a lock-free way.</p>
<blockquote>
<p>so you should easily be able to give a concrete example other than generating unique sequential IDs</p>
</blockquote>
<p>An use-case I was working on earlier today was profiling operations like coverage and sampling tools. These need to be reset as well if they're in danger of over-flowing a range that the machine can natively implement with low-overhead.</p> Ruby master - Feature #12607: Ruby needs an atomic integerhttps://redmine.ruby-lang.org/issues/12607?journal_id=899412021-01-14T03:41:05ZDan0042 (Daniel DeLorme)
<ul></ul><p>chrisseaton (Chris Seaton) wrote in <a href="#note-32">#note-32</a>:</p>
<blockquote>
<p>An use-case I was working on earlier today was profiling operations like coverage and sampling tools.</p>
</blockquote>
<p>Ah yes, beautiful example! Accumulating metrics require both atomicity and performance.</p> Ruby master - Feature #12607: Ruby needs an atomic integerhttps://redmine.ruby-lang.org/issues/12607?journal_id=899482021-01-14T13:06:37ZEregon (Benoit Daloze)
<ul></ul><p>There is also the well-known example of metrics in Sidekiq, and all these:<br>
<a href="https://github.com/search?l=Ruby&q=AtomicFixnum&type=Code" class="external">https://github.com/search?l=Ruby&q=AtomicFixnum&type=Code</a></p>
<blockquote>
<p>I really think a generator (like the above MyCounter.next) is a more appropriate tool than an atomic integer.</p>
</blockquote>
<p>And how do you implement that efficiently, and in a way that's thread-safe?<br>
To be clear, Enumerator.new {} is not efficient, so a generator is not good enough, and Fibers can't be resumed across threads.</p>
<p>ko1 (Koichi Sasada) wrote in <a href="#note-26">#note-26</a>:</p>
<blockquote>
<p>TVar proposed in <a href="https://bugs.ruby-lang.org/issues/17261" class="external">https://bugs.ruby-lang.org/issues/17261</a> has <code>#increment</code> method and it is enough fast.</p>
</blockquote>
<p>I think that cannot be as efficient as an atomic integer.<br>
Reasoning: TVar#increment needs to also be atomic with other changes, including <code>Thread.atomically { tv.value = tv.value * 2 }</code>, for decent STM semantics.<br>
That implies extra tracking for TVar#increment besides just a single fetch-and-add, isn't it?<br>
For instance, it would be incorrect to execute that <code>atomically</code> block in parallel with the fetch-and-add (might result in the increment being lost). So TVar#increment needs to sync somehow with <code>Thread.atomically</code> and that's the overhead.</p> Ruby master - Feature #12607: Ruby needs an atomic integerhttps://redmine.ruby-lang.org/issues/12607?journal_id=899522021-01-14T14:43:09ZDan0042 (Daniel DeLorme)
<ul></ul><p>Eregon (Benoit Daloze) wrote in <a href="#note-34">#note-34</a>:</p>
<blockquote>
<p>And how do you implement that efficiently, and in a way that's thread-safe?<br>
To be clear, Enumerator.new {} is not efficient, so a generator is not good enough, and Fibers can't be resumed across threads.</p>
</blockquote>
<p>By "a generator" I did not mean the <code>Enumerator::Generator</code> class. I meant a new generator-type class (say, <code>AtomicSequence</code>) that is implemented with atomic CAS operations. No one here is disputing the usefulness of atomic integers <em>in general</em>, just their relevance as a concurrency primitive in <em>ruby core</em>.</p>
<p>A naive implementation of a monotonic sequence using an atomic integer class might look like this:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="no">SEQUENCE</span><span class="p">.</span><span class="nf">increment</span>
<span class="n">guid</span> <span class="o">=</span> <span class="no">SEQUENCE</span><span class="p">.</span><span class="nf">to_i</span>
</code></pre>
<p>oops, wrong! When running concurrently this will produce duplicate guids. The correct usage would be <code>guid = SEQUENCE.increment</code> but you have to depend on the developer to understand the gotcha with <code>SEQUENCE.to_i</code>. In comparison, <code>guid = SEQUENCE.next</code> is always correct; you don't even need to be <em>aware</em> of concurrency issues.</p>
<p>What about a naive implementation of metrics/statistics using an atomic integer class...</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">t0</span> <span class="o">=</span> <span class="no">Time</span><span class="p">.</span><span class="nf">now</span>
<span class="k">yield</span>
<span class="n">t</span> <span class="o">=</span> <span class="p">((</span><span class="no">Time</span><span class="p">.</span><span class="nf">now</span> <span class="o">-</span> <span class="n">t0</span><span class="p">)</span> <span class="o">*</span> <span class="mi">1000000</span><span class="p">).</span><span class="nf">round</span> <span class="c1">#microsecond</span>
<span class="no">TIME</span><span class="p">.</span><span class="nf">increment</span><span class="p">(</span><span class="n">t</span><span class="p">)</span>
<span class="no">NB</span><span class="p">.</span><span class="nf">increment</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="n">avg</span> <span class="o">=</span> <span class="no">TIME</span><span class="p">.</span><span class="nf">to_i</span> <span class="o">/</span> <span class="no">NB</span><span class="p">.</span><span class="nf">to_i</span>
</code></pre>
<p>oops, wrong again! When running concurrently the <code>avg</code> might be incorrect.</p>
<p>Maybe a more useful abstraction might be an <code>AtomicVector</code>? If such a thing is possible...</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="no">TIME_NB</span> <span class="o">=</span> <span class="no">AtomicVector</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
<span class="no">TIME_NB</span><span class="p">.</span><span class="nf">increment</span><span class="p">(</span><span class="n">t</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="n">avg</span> <span class="o">=</span> <span class="no">TIME_NB</span><span class="p">.</span><span class="nf">to_a</span><span class="p">.</span><span class="nf">inject</span><span class="p">(</span><span class="ss">:/</span><span class="p">)</span>
</code></pre>
<p>Basically all I'm trying to say is there's a clear benefit to choosing abstractions that produce the correct result <em>even when used naively</em>. Like Ractor. So I understand why ko1 is reluctant about this.</p> Ruby master - Feature #12607: Ruby needs an atomic integerhttps://redmine.ruby-lang.org/issues/12607?journal_id=901462021-01-29T08:59:31Zko1 (Koichi Sasada)
<ul></ul><blockquote>
<p>ko1 (Koichi Sasada) wrote in <a href="#note-26">#note-26</a>:</p>
<blockquote>
<p>TVar proposed in <a href="https://bugs.ruby-lang.org/issues/17261" class="external">https://bugs.ruby-lang.org/issues/17261</a> has <code>#increment</code> method and it is enough fast.</p>
</blockquote>
<p>I think that cannot be as efficient as an atomic integer.</p>
</blockquote>
<p>Yes. It is slower than single purpose atomic integers with hardware instruction with JIT.<br>
But (I didn't measured yet) increment method call on VM needs method invocation overhead and it is relatively higher than <code>TVar#increment</code> overhead. Again, I didn't measure and it can be wrong.</p> Ruby master - Feature #12607: Ruby needs an atomic integerhttps://redmine.ruby-lang.org/issues/12607?journal_id=901542021-01-29T12:28:31ZEregon (Benoit Daloze)
<ul></ul><p>ko1 (Koichi Sasada) wrote in <a href="#note-36">#note-36</a>:</p>
<blockquote>
<p>But (I didn't measured yet) increment method call on VM needs method invocation overhead and it is relatively higher than <code>TVar#increment</code> overhead. Again, I didn't measure and it can be wrong.</p>
</blockquote>
<p>Why would the invocation of AtomicInteger#increment be more expensive than the invocation of TVar#increment?<br>
They are both method calls, and TVar doesn't doesn't use <code>Primitive</code> since it is a separate gem (currently at least).</p>
<p>In general, method calls have no cost when inlined with a JIT that can see through the core library, e.g. on TruffleRuby, or I think with MJIT and leaf annotated methods, so I think for best performance AtomicInteger#increment is a clear winner.</p> Ruby master - Feature #12607: Ruby needs an atomic integerhttps://redmine.ruby-lang.org/issues/12607?journal_id=984532022-07-25T13:14:10ZEregon (Benoit Daloze)
<ul></ul><p>It'd be good to revisit this (e.g. see <a href="https://twitter.com/_byroot/status/1550580128723476480" class="external">https://twitter.com/_byroot/status/1550580128723476480</a>).</p>
<p>Ractor has little need for this. But with Thread there is a clear need for this.<br>
Also the STM work was not merged, so that's not a replacement either.<br>
I think @ko1's concern of "too hard to use" should be no blocker: Rubyists frequently use Threads, almost all Ruby webservers and Rails use threads nowadays, there is no point to deny that.<br>
And it is completely unrealistic to think Ractor will ever replace Threads, Ractor can only support a small subset of Ruby gems, and it will never be all or as powerful as threads.<br>
For instance I don't see Rails being able to use Ractor for parallel requests anytime soon.</p>
<p>Regarding that example it's easy to fix, and such conditions are well understood by anyone knowing about compare-and-swap or this kind of concurrency tools.<br>
It can also be documented on the <code>update</code> method.</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">v</span> <span class="o">=</span> <span class="n">af</span><span class="p">.</span><span class="nf">value</span>
<span class="n">af</span><span class="p">.</span><span class="nf">update</span><span class="p">{</span> <span class="o">|</span><span class="n">v</span><span class="o">|</span> <span class="n">v</span> <span class="o">+</span> <span class="mi">5</span> <span class="p">}</span>
</code></pre>
<p>So, let's add AtomicInteger or AtomicReference (since it's more general) because it's convenient and needed by existing gems?<br>
Having it in concurrent-ruby works but it's obviously less practical, and less optimized than it could be.</p>
<p>Also there is a trap when using Mutex as a compare-and-swap way with <code>try_lock</code>, because that leaves the Mutex locked and when the thread dies it unlocks all Mutex it had, which can be pretty slow (I found that out when reimplementing the timeout gem).</p> Ruby master - Feature #12607: Ruby needs an atomic integerhttps://redmine.ruby-lang.org/issues/12607?journal_id=984542022-07-25T14:22:31Zchrisseaton (Chris Seaton)chris@chrisseaton.com
<ul></ul><p>Yes, I think a few low-level concurrency primitives should move from Concurrent Ruby to core. As we add more concurrency to Ruby, and add JIT compilers, ideally the VM should understand these primitives, rather than being opaque extension code.</p>