atomic-ruby 0.15.5 → 0.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +11 -11
- data/lib/atomic-ruby/atomic_thread_pool.rb +29 -5
- data/lib/atomic-ruby/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b17a39b18e427bf9b4467457831fd5acf724af9d86b4ca35a3ef10582726b74e
|
|
4
|
+
data.tar.gz: 3285f13f8e5ef021f7fdbcbdcf52b17ab6ff171fc70106543799c0f09f577979
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0b99a0e2100707aada701ed45c044cc06b6319bffe5a68ff996b8e536a42e1c1cd0ca36c092e9dfe6e24cfea03dd739cc1338b08926479ba2424d59a6a216221
|
|
7
|
+
data.tar.gz: 51d042574a1c3b9c8eb6b7606eaa3e6512ad97f9dbb72a38f349d40bc14eaa5b271e6953bac796ece002192ebd3831ac28ce6538e70297721eaa724cf12ab3bf
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.16.1] - 2026-10-18
|
|
4
|
+
|
|
5
|
+
- Grow `AtomicThreadPool` until Ruby execution uses three quarters of one CPU
|
|
6
|
+
|
|
7
|
+
## [0.16.0] - 2026-10-18
|
|
8
|
+
|
|
9
|
+
- Add `AtomicThreadPool#wait_for_capacity`
|
|
10
|
+
|
|
3
11
|
## [0.15.5] - 2026-09-03
|
|
4
12
|
|
|
5
13
|
- Adapt `AtomicThreadPool` scaling to workload pressure
|
data/README.md
CHANGED
|
@@ -270,7 +270,7 @@ puts "Atomic Ruby Atomic Bank Account: #{results[2].real.round(6)} seconds"
|
|
|
270
270
|
|
|
271
271
|
ruby version: ruby 4.0.6 (2026-07-14 revision 03b6d3f889) +YJIT +PRISM [arm64-darwin23]
|
|
272
272
|
concurrent-ruby version: 1.3.8
|
|
273
|
-
atomic-ruby version: 0.
|
|
273
|
+
atomic-ruby version: 0.16.1
|
|
274
274
|
|
|
275
275
|
Balances:
|
|
276
276
|
Synchronized Bank Account Balance: 975
|
|
@@ -361,7 +361,7 @@ end
|
|
|
361
361
|
|
|
362
362
|
ruby version: ruby 4.0.6 (2026-07-14 revision 03b6d3f889) +YJIT +PRISM [arm64-darwin23]
|
|
363
363
|
concurrent-ruby version: 1.3.8
|
|
364
|
-
atomic-ruby version: 0.
|
|
364
|
+
atomic-ruby version: 0.16.1
|
|
365
365
|
|
|
366
366
|
Warming up --------------------------------------
|
|
367
367
|
Synchronized Boolean Toggle 165.000 i/100ms
|
|
@@ -444,7 +444,7 @@ end
|
|
|
444
444
|
> bundle exec rake clobber && bundle exec rake compile && bundle exec ruby examples/atomic_condition_variable_benchmark.rb
|
|
445
445
|
|
|
446
446
|
ruby version: ruby 4.0.6 (2026-07-14 revision 03b6d3f889) +YJIT +PRISM [arm64-darwin23]
|
|
447
|
-
atomic-ruby version: 0.
|
|
447
|
+
atomic-ruby version: 0.16.1
|
|
448
448
|
|
|
449
449
|
Warming up --------------------------------------
|
|
450
450
|
Synchronized Condition Variable Wait/Signal 4.062k i/100ms
|
|
@@ -519,7 +519,7 @@ end
|
|
|
519
519
|
> bundle exec rake clobber && bundle exec rake compile && bundle exec ruby examples/atomic_queue_benchmark.rb
|
|
520
520
|
|
|
521
521
|
ruby version: ruby 4.0.6 (2026-07-14 revision 03b6d3f889) +YJIT +PRISM [arm64-darwin23]
|
|
522
|
-
atomic-ruby version: 0.
|
|
522
|
+
atomic-ruby version: 0.16.1
|
|
523
523
|
|
|
524
524
|
Warming up --------------------------------------
|
|
525
525
|
Synchronized Queue Push/Pop 184.000 i/100ms
|
|
@@ -620,17 +620,17 @@ puts "Atomic Ruby Adaptive Thread Pool: #{adaptive_results[1].real.round(6)} sec
|
|
|
620
620
|
```
|
|
621
621
|
> bundle exec rake clobber && bundle exec rake compile && bundle exec ruby examples/atomic_thread_pool_benchmark.rb
|
|
622
622
|
|
|
623
|
-
ruby version: ruby 4.0.
|
|
623
|
+
ruby version: ruby 4.0.7 (2026-09-15 revision 229531a6cf) +YJIT +PRISM [arm64-darwin23]
|
|
624
624
|
concurrent-ruby version: 1.3.8
|
|
625
|
-
atomic-ruby version: 0.
|
|
625
|
+
atomic-ruby version: 0.16.1
|
|
626
626
|
|
|
627
627
|
Fixed Pool Results:
|
|
628
|
-
Concurrent Ruby Fixed Thread Pool: 5.
|
|
629
|
-
Atomic Ruby Fixed Thread Pool: 4.
|
|
628
|
+
Concurrent Ruby Fixed Thread Pool: 5.147226 seconds
|
|
629
|
+
Atomic Ruby Fixed Thread Pool: 4.743301 seconds
|
|
630
630
|
|
|
631
631
|
Adaptive Pool Results:
|
|
632
|
-
Concurrent Ruby IO Thread Pool: 0.
|
|
633
|
-
Atomic Ruby Adaptive Thread Pool: 0.
|
|
632
|
+
Concurrent Ruby IO Thread Pool: 0.217971 seconds
|
|
633
|
+
Atomic Ruby Adaptive Thread Pool: 0.227701 seconds
|
|
634
634
|
```
|
|
635
635
|
|
|
636
636
|
</details>
|
|
@@ -684,7 +684,7 @@ puts "Atomic Ruby Atomic Count Down Latch: #{results[1].total.round(6)} CPU seco
|
|
|
684
684
|
|
|
685
685
|
ruby version: ruby 4.0.6 (2026-07-14 revision 03b6d3f889) +YJIT +PRISM [arm64-darwin23]
|
|
686
686
|
concurrent-ruby version: 1.3.8
|
|
687
|
-
atomic-ruby version: 0.
|
|
687
|
+
atomic-ruby version: 0.16.1
|
|
688
688
|
|
|
689
689
|
Benchmark Results:
|
|
690
690
|
Concurrent Ruby Count Down Latch: 0.000511 CPU seconds, 2.001465 elapsed seconds
|
|
@@ -55,7 +55,8 @@ module AtomicRuby
|
|
|
55
55
|
AUTOSCALE_INITIAL_WINDOW = 0.002
|
|
56
56
|
AUTOSCALE_MAX_WINDOW = 0.05
|
|
57
57
|
AUTOSCALE_SAMPLES_PER_WINDOW = 5
|
|
58
|
-
|
|
58
|
+
AUTOSCALE_RUBY_CPU_SHARE = 0.75
|
|
59
|
+
private_constant :AUTOSCALE_IDLE_TIME, :AUTOSCALE_INITIAL_WINDOW, :AUTOSCALE_MAX_WINDOW, :AUTOSCALE_SAMPLES_PER_WINDOW, :AUTOSCALE_RUBY_CPU_SHARE
|
|
59
60
|
|
|
60
61
|
class Error < StandardError; end
|
|
61
62
|
|
|
@@ -118,6 +119,7 @@ module AtomicRuby
|
|
|
118
119
|
@queue = AtomicQueue.new
|
|
119
120
|
@shutdown = AtomicBoolean.new(false)
|
|
120
121
|
@work_available = AtomicConditionVariable.new
|
|
122
|
+
@capacity_changed = AtomicConditionVariable.new
|
|
121
123
|
@alive_thread_count = Atom.new(0)
|
|
122
124
|
@active_thread_count = Atom.new(0)
|
|
123
125
|
@threads = []
|
|
@@ -238,6 +240,25 @@ module AtomicRuby
|
|
|
238
240
|
@active_thread_count.value
|
|
239
241
|
end
|
|
240
242
|
|
|
243
|
+
# Blocks until the given block returns a truthy value, checking it again
|
|
244
|
+
# whenever a worker takes or finishes work.
|
|
245
|
+
#
|
|
246
|
+
# Useful for applying backpressure, such as holding off on accepting more
|
|
247
|
+
# work until the pool has capacity for it.
|
|
248
|
+
#
|
|
249
|
+
# @yieldreturn [untyped] Truthy to stop waiting, falsy to keep waiting
|
|
250
|
+
# @return [untyped] The first truthy value returned by the block
|
|
251
|
+
#
|
|
252
|
+
# @example Wait until no work is queued
|
|
253
|
+
# pool = AtomicThreadPool.new(size: 2)
|
|
254
|
+
# 5.times { pool << proc { sleep(0.1) } }
|
|
255
|
+
# pool.wait_for_capacity { pool.queue_length.zero? } #=> true
|
|
256
|
+
#
|
|
257
|
+
# @rbs () { () -> untyped } -> untyped
|
|
258
|
+
def wait_for_capacity(&block)
|
|
259
|
+
@capacity_changed.wait(&block)
|
|
260
|
+
end
|
|
261
|
+
|
|
241
262
|
# Gracefully shuts down the thread pool.
|
|
242
263
|
#
|
|
243
264
|
# This method:
|
|
@@ -351,6 +372,7 @@ module AtomicRuby
|
|
|
351
372
|
break if should_exit
|
|
352
373
|
|
|
353
374
|
@active_thread_count.swap { |current_count| current_count + 1 }
|
|
375
|
+
@capacity_changed.broadcast unless @capacity_changed.waiter_count.zero?
|
|
354
376
|
if temporary
|
|
355
377
|
work_started_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
356
378
|
running_cpu_time_at_start = @thread_pool_monitor.snapshot[6]
|
|
@@ -368,10 +390,11 @@ module AtomicRuby
|
|
|
368
390
|
ensure
|
|
369
391
|
@thread_pool_monitor&.stop_work
|
|
370
392
|
@active_thread_count.swap { |current_count| current_count - 1 }
|
|
393
|
+
@capacity_changed.broadcast unless @capacity_changed.waiter_count.zero?
|
|
371
394
|
if temporary && !@queue.empty?
|
|
372
395
|
elapsed_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) - work_started_at
|
|
373
396
|
running_cpu_time = @thread_pool_monitor.snapshot[6] - running_cpu_time_at_start
|
|
374
|
-
if running_cpu_time
|
|
397
|
+
if running_cpu_time >= elapsed_time * 1_000_000_000 * AUTOSCALE_RUBY_CPU_SHARE
|
|
375
398
|
@ruby_cpu_trim_requested.make_true
|
|
376
399
|
@work_available.broadcast
|
|
377
400
|
end
|
|
@@ -465,8 +488,9 @@ module AtomicRuby
|
|
|
465
488
|
#
|
|
466
489
|
# Requiring workers to spend a majority of their time blocked outside the
|
|
467
490
|
# GVL recognizes blocking operations. The pool grows while Ruby execution
|
|
468
|
-
# uses less than
|
|
469
|
-
# the bottleneck, without mistaking OS scheduling delays for GVL
|
|
491
|
+
# uses less than three quarters of one CPU and shrinks when Ruby execution
|
|
492
|
+
# becomes the bottleneck, without mistaking OS scheduling delays for GVL
|
|
493
|
+
# contention.
|
|
470
494
|
#
|
|
471
495
|
# @param previous_snapshot [Array<Integer>] previous GVL state snapshot
|
|
472
496
|
# @param snapshot [Array<Integer>] current GVL state snapshot
|
|
@@ -492,7 +516,7 @@ module AtomicRuby
|
|
|
492
516
|
total_time = running_time + waiting_time + blocked_time
|
|
493
517
|
end
|
|
494
518
|
|
|
495
|
-
ruby_cpu_bound = running_cpu_time
|
|
519
|
+
ruby_cpu_bound = running_cpu_time >= elapsed_time * 1_000_000_000 * AUTOSCALE_RUBY_CPU_SHARE
|
|
496
520
|
blocking = total_time.positive? && blocked_time * 2 > total_time && !ruby_cpu_bound
|
|
497
521
|
|
|
498
522
|
return :up if blocking && workers.length < @max_size
|
data/lib/atomic-ruby/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: atomic-ruby
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.16.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Joshua Young
|
|
@@ -52,7 +52,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
53
|
version: '0'
|
|
54
54
|
requirements: []
|
|
55
|
-
rubygems_version: 4.0.
|
|
55
|
+
rubygems_version: 4.0.20
|
|
56
56
|
specification_version: 4
|
|
57
57
|
summary: Atomic primitives for Ruby
|
|
58
58
|
test_files: []
|