atomic-ruby 0.15.3 → 0.15.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a50be9a8594152f8d07b2338588a01be6aecadf55f993ea6cc4c2431e80e2b76
4
- data.tar.gz: eb9c45d8dff70d0697d3d62ba5a539e3d71e42c21ea7ab6b646d44fde69f39ac
3
+ metadata.gz: e7c0136cf37fe5133affc1f090fa517636f5bfda0f5f43564a4877eee15bae3f
4
+ data.tar.gz: 0ae3a3aadabcc2bb69621df62188800451289a46032fa57a49c1af9326bcc31e
5
5
  SHA512:
6
- metadata.gz: abde2bc85010be3634caeaf66d262acf1c5c70c50af5e9e420a43c87071965bd543a475b01b5d1b0d1569682802595e936a9d224e636896a681e5c3c85d98a77
7
- data.tar.gz: e93d7b35c3ccdba9c82906587c24455c248655ffbe3f9d4141e4cb029705306f80c13b36f6a0ce7193a33701758ad0a9c2f77a7aa0c94ad92e49392ba899c055
6
+ metadata.gz: 1786faa43a25379cbe68c9de2372c6168a62ab225436c45bbf5049791c39191e3ed75955ab2f5f6385069ddeb86640aa11ba431ce9f2ab54335005a61bd42092
7
+ data.tar.gz: 704c7e7224c9eec351aa0174f597c10c992f4e1594921b8ca91e9ad2df43a6b3eb2c260b9b25ab2d47f8fb0934afa9739ed5312fb00d5d7519d9283c6bf68170
data/CHANGELOG.md CHANGED
@@ -1,6 +1,11 @@
1
1
  ## [Unreleased]
2
2
 
3
- ## [0.15.2] - 2026-08-31
3
+ ## [0.15.4] - 2026-09-01
4
+
5
+ - Keep temporary `AtomicThreadPool` workers alive for five seconds
6
+ - Speed up `AtomicThreadPool` scaling for blocking work
7
+
8
+ ## [0.15.3] - 2026-08-31
4
9
 
5
10
  - Prevent enqueues from racing `AtomicThreadPool#shutdown`
6
11
  - Remove interrupted waiters from `AtomicConditionVariable`
data/README.md CHANGED
@@ -268,7 +268,7 @@ puts "Atomic Ruby Atomic Bank Account: #{results[2].real.round(6)} seconds"
268
268
 
269
269
  ruby version: ruby 4.0.6 (2026-07-14 revision 03b6d3f889) +YJIT +PRISM [arm64-darwin23]
270
270
  concurrent-ruby version: 1.3.8
271
- atomic-ruby version: 0.15.3
271
+ atomic-ruby version: 0.15.4
272
272
 
273
273
  Balances:
274
274
  Synchronized Bank Account Balance: 975
@@ -359,7 +359,7 @@ end
359
359
 
360
360
  ruby version: ruby 4.0.6 (2026-07-14 revision 03b6d3f889) +YJIT +PRISM [arm64-darwin23]
361
361
  concurrent-ruby version: 1.3.8
362
- atomic-ruby version: 0.15.3
362
+ atomic-ruby version: 0.15.4
363
363
 
364
364
  Warming up --------------------------------------
365
365
  Synchronized Boolean Toggle 165.000 i/100ms
@@ -442,7 +442,7 @@ end
442
442
  > bundle exec rake clobber && bundle exec rake compile && bundle exec ruby examples/atomic_condition_variable_benchmark.rb
443
443
 
444
444
  ruby version: ruby 4.0.6 (2026-07-14 revision 03b6d3f889) +YJIT +PRISM [arm64-darwin23]
445
- atomic-ruby version: 0.15.3
445
+ atomic-ruby version: 0.15.4
446
446
 
447
447
  Warming up --------------------------------------
448
448
  Synchronized Condition Variable Wait/Signal 4.062k i/100ms
@@ -517,7 +517,7 @@ end
517
517
  > bundle exec rake clobber && bundle exec rake compile && bundle exec ruby examples/atomic_queue_benchmark.rb
518
518
 
519
519
  ruby version: ruby 4.0.6 (2026-07-14 revision 03b6d3f889) +YJIT +PRISM [arm64-darwin23]
520
- atomic-ruby version: 0.15.3
520
+ atomic-ruby version: 0.15.4
521
521
 
522
522
  Warming up --------------------------------------
523
523
  Synchronized Queue Push/Pop 184.000 i/100ms
@@ -547,7 +547,7 @@ require "benchmark"
547
547
  require "concurrent-ruby"
548
548
  require_relative "../lib/atomic-ruby"
549
549
 
550
- results = []
550
+ fixed_results = []
551
551
 
552
552
  2.times do |idx|
553
553
  result = Benchmark.measure do
@@ -569,7 +569,36 @@ results = []
569
569
  pool.wait_for_termination if idx == 0
570
570
  end
571
571
 
572
- results << result
572
+ fixed_results << result
573
+ end
574
+
575
+ adaptive_results = []
576
+
577
+ 2.times do |idx|
578
+ result = Benchmark.measure do
579
+ pool = case idx
580
+ when 0 then Concurrent.new_io_executor
581
+ when 1 then AtomicThreadPool.new(size: 1, max_size: Float::INFINITY)
582
+ end
583
+ latch = case idx
584
+ when 0 then Concurrent::CountDownLatch.new(100)
585
+ when 1 then AtomicCountDownLatch.new(100)
586
+ end
587
+
588
+ 100.times do
589
+ pool << proc do
590
+ sleep(0.2)
591
+ latch.count_down
592
+ end
593
+ end
594
+
595
+ latch.wait
596
+ pool.shutdown
597
+ # concurrent-ruby's #shutdown does not wait for threads to terminate
598
+ pool.wait_for_termination if idx == 0
599
+ end
600
+
601
+ adaptive_results << result
573
602
  end
574
603
 
575
604
  puts "\n"
@@ -577,9 +606,13 @@ puts "ruby version: #{RUBY_DESCRIPTION}"
577
606
  puts "concurrent-ruby version: #{Concurrent::VERSION}"
578
607
  puts "atomic-ruby version: #{AtomicRuby::VERSION}"
579
608
  puts "\n"
580
- puts "Benchmark Results:"
581
- puts "Concurrent Ruby Thread Pool: #{results[0].real.round(6)} seconds"
582
- puts "Atomic Ruby Atomic Thread Pool: #{results[1].real.round(6)} seconds"
609
+ puts "Fixed Pool Results:"
610
+ puts "Concurrent Ruby Fixed Thread Pool: #{fixed_results[0].real.round(6)} seconds"
611
+ puts "Atomic Ruby Fixed Thread Pool: #{fixed_results[1].real.round(6)} seconds"
612
+ puts "\n"
613
+ puts "Adaptive Pool Results:"
614
+ puts "Concurrent Ruby IO Thread Pool: #{adaptive_results[0].real.round(6)} seconds"
615
+ puts "Atomic Ruby Adaptive Thread Pool: #{adaptive_results[1].real.round(6)} seconds"
583
616
  ```
584
617
 
585
618
  ```
@@ -587,11 +620,15 @@ puts "Atomic Ruby Atomic Thread Pool: #{results[1].real.round(6)} seconds"
587
620
 
588
621
  ruby version: ruby 4.0.6 (2026-07-14 revision 03b6d3f889) +YJIT +PRISM [arm64-darwin23]
589
622
  concurrent-ruby version: 1.3.8
590
- atomic-ruby version: 0.15.3
623
+ atomic-ruby version: 0.15.4
591
624
 
592
- Benchmark Results:
593
- Concurrent Ruby Thread Pool: 5.012798 seconds
594
- Atomic Ruby Atomic Thread Pool: 4.755237 seconds
625
+ Fixed Pool Results:
626
+ Concurrent Ruby Fixed Thread Pool: 5.069464 seconds
627
+ Atomic Ruby Fixed Thread Pool: 4.791382 seconds
628
+
629
+ Adaptive Pool Results:
630
+ Concurrent Ruby IO Thread Pool: 0.22309 seconds
631
+ Atomic Ruby Adaptive Thread Pool: 0.61561 seconds
595
632
  ```
596
633
 
597
634
  </details>
@@ -645,7 +682,7 @@ puts "Atomic Ruby Atomic Count Down Latch: #{results[1].total.round(6)} CPU seco
645
682
 
646
683
  ruby version: ruby 4.0.6 (2026-07-14 revision 03b6d3f889) +YJIT +PRISM [arm64-darwin23]
647
684
  concurrent-ruby version: 1.3.8
648
- atomic-ruby version: 0.15.3
685
+ atomic-ruby version: 0.15.4
649
686
 
650
687
  Benchmark Results:
651
688
  Concurrent Ruby Count Down Latch: 0.000511 CPU seconds, 2.001465 elapsed seconds
@@ -51,11 +51,10 @@ module AtomicRuby
51
51
  # @note This class is NOT Ractor-safe as it contains mutable thread state
52
52
  # that cannot be safely shared across ractors.
53
53
  class AtomicThreadPool
54
- AUTOSCALE_GROWTH_SAMPLES = 3
55
- AUTOSCALE_IDLE_TIME = 1
54
+ AUTOSCALE_IDLE_TIME = 5
56
55
  AUTOSCALE_INTERVAL = 0.01
57
56
  AUTOSCALE_WINDOW = 0.05
58
- private_constant :AUTOSCALE_GROWTH_SAMPLES, :AUTOSCALE_IDLE_TIME, :AUTOSCALE_INTERVAL, :AUTOSCALE_WINDOW
57
+ private_constant :AUTOSCALE_IDLE_TIME, :AUTOSCALE_INTERVAL, :AUTOSCALE_WINDOW
59
58
 
60
59
  class Error < StandardError; end
61
60
 
@@ -71,7 +70,7 @@ module AtomicRuby
71
70
  # if work remains queued while its active workers spend most of their time
72
71
  # blocked outside the GVL, but not when Ruby execution is using the
73
72
  # available CPU.
74
- # Temporary workers leave after the queue remains empty, returning the pool
73
+ # Temporary workers remain available between bursts before the pool returns
75
74
  # to its baseline size. Omitting `max_size` creates a fixed-size pool.
76
75
  #
77
76
  # @param size [Integer] The baseline number of worker threads (must be positive)
@@ -379,7 +378,6 @@ module AtomicRuby
379
378
  previous_snapshot = @thread_pool_monitor.snapshot
380
379
  pressure_started_at = nil
381
380
  idle_started_at = nil
382
- growth_samples = 0
383
381
  phase_samples = [0, 0, 0]
384
382
 
385
383
  loop do
@@ -395,7 +393,6 @@ module AtomicRuby
395
393
  pressure_started_at = nil
396
394
  previous_snapshot = snapshot
397
395
  idle_started_at ||= now
398
- growth_samples = 0
399
396
  phase_samples.fill(0)
400
397
 
401
398
  if now - idle_started_at >= AUTOSCALE_IDLE_TIME
@@ -412,13 +409,9 @@ module AtomicRuby
412
409
  phase_samples.fill(0)
413
410
  elsif now - pressure_started_at >= AUTOSCALE_WINDOW
414
411
  if should_grow?(previous_snapshot, snapshot, phase_samples, now - pressure_started_at)
415
- growth_samples += 1
416
- if growth_samples >= AUTOSCALE_GROWTH_SAMPLES
417
- spawn_worker(temporary: true)
418
- growth_samples = 0
419
- end
420
- else
421
- growth_samples = 0
412
+ worker_count = @threads.length
413
+ workers_to_add = [worker_count, @queue.size, @max_size - worker_count].min
414
+ workers_to_add.times { spawn_worker(temporary: true) }
422
415
  end
423
416
  previous_snapshot = snapshot
424
417
  pressure_started_at = now
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AtomicRuby
4
- VERSION = "0.15.3"
4
+ VERSION = "0.15.4"
5
5
  end
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.15.3
4
+ version: 0.15.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joshua Young