atomic-ruby 0.15.0 → 0.15.2
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 +9 -9
- data/ext/atomic_ruby/atomic_ruby.c +75 -10
- data/ext/atomic_ruby/atomic_ruby.h +1 -0
- data/lib/atomic-ruby/atomic_thread_pool.rb +18 -12
- data/lib/atomic-ruby/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9c6210195ad6d82fbbb6a84f66bc8ce5faef54d50ed3227c3655ec43a996a9f6
|
|
4
|
+
data.tar.gz: eef94abbb8c8bfb1d9a54d36f8f8b0404d49210e6e6a109851fa014a6512aaa5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c89b7c62bda07206115c80c45088d1ae44e5c3aa84288932fae8fad51312d3490bf9510af559178fe6311e5d8ce4ecf87dca0da3420ced47a0113783e63a741d
|
|
7
|
+
data.tar.gz: a5c5ecee1ce3f96b5e2c525897c1c979fa3f99b7be6cd971d6882e2a43bf9b735f0de58bcf67dbf1179b0298a006a25ba45e910ee0c104f965c305e4af93f6be
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.15.2] - 2026-08-30
|
|
4
|
+
|
|
5
|
+
- Avoid thread event hook overhead for fixed thread pools
|
|
6
|
+
|
|
7
|
+
## [0.15.1] - 2026-08-30
|
|
8
|
+
|
|
9
|
+
- Prevent OS scheduling delays from stopping adaptive thread pool growth
|
|
10
|
+
|
|
3
11
|
## [0.15.0] - 2026-08-29
|
|
4
12
|
|
|
5
13
|
- Add adaptive scaling to `AtomicThreadPool` with `max_size:`
|
data/README.md
CHANGED
|
@@ -122,8 +122,8 @@ p results.sort #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
|
|
122
122
|
|
|
123
123
|
Pass `max_size` to let the pool temporarily add workers when queued work is
|
|
124
124
|
held up by blocking operations. The pool measures time spent blocked outside
|
|
125
|
-
the GVL and
|
|
126
|
-
is the bottleneck. It returns to `size` when the queue drains.
|
|
125
|
+
the GVL and CPU time used by its workers, so it does not add more threads when
|
|
126
|
+
Ruby execution is the bottleneck. It returns to `size` when the queue drains.
|
|
127
127
|
|
|
128
128
|
```ruby
|
|
129
129
|
pool = AtomicThreadPool.new(size: 4, max_size: 16)
|
|
@@ -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.6
|
|
271
|
-
atomic-ruby version: 0.15.
|
|
271
|
+
atomic-ruby version: 0.15.2
|
|
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.6
|
|
362
|
-
atomic-ruby version: 0.15.
|
|
362
|
+
atomic-ruby version: 0.15.2
|
|
363
363
|
|
|
364
364
|
Warming up --------------------------------------
|
|
365
365
|
Synchronized Boolean Toggle 157.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.
|
|
445
|
+
atomic-ruby version: 0.15.2
|
|
446
446
|
|
|
447
447
|
Warming up --------------------------------------
|
|
448
448
|
Synchronized Condition Variable Wait/Signal 3.977k 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.
|
|
520
|
+
atomic-ruby version: 0.15.2
|
|
521
521
|
|
|
522
522
|
Warming up --------------------------------------
|
|
523
523
|
Synchronized Queue Push/Pop 181.000 i/100ms
|
|
@@ -587,11 +587,11 @@ puts "Atomic Ruby Atomic Thread Pool: #{results[1].real.round(6)} seconds"
|
|
|
587
587
|
|
|
588
588
|
ruby version: ruby 4.0.6 (2026-07-14 revision 03b6d3f889) +YJIT +PRISM [arm64-darwin23]
|
|
589
589
|
concurrent-ruby version: 1.3.8
|
|
590
|
-
atomic-ruby version: 0.15.
|
|
590
|
+
atomic-ruby version: 0.15.2
|
|
591
591
|
|
|
592
592
|
Benchmark Results:
|
|
593
|
-
Concurrent Ruby Thread Pool: 5.
|
|
594
|
-
Atomic Ruby Atomic Thread Pool: 4.
|
|
593
|
+
Concurrent Ruby Thread Pool: 5.081233 seconds
|
|
594
|
+
Atomic Ruby Atomic Thread Pool: 4.737824 seconds
|
|
595
595
|
```
|
|
596
596
|
|
|
597
597
|
</details>
|
|
@@ -535,17 +535,39 @@ typedef struct {
|
|
|
535
535
|
_Atomic unsigned long long running_time;
|
|
536
536
|
_Atomic unsigned long long waiting_time;
|
|
537
537
|
_Atomic unsigned long long blocked_time;
|
|
538
|
+
_Atomic unsigned long long running_cpu_time;
|
|
539
|
+
bool active;
|
|
538
540
|
} atomic_ruby_thread_pool_monitor_t;
|
|
539
541
|
|
|
540
542
|
typedef struct {
|
|
541
543
|
atomic_ruby_thread_pool_monitor_t *monitor;
|
|
542
544
|
atomic_ruby_thread_pool_worker_phase_t phase;
|
|
543
545
|
unsigned long long phase_started_at;
|
|
546
|
+
unsigned long long running_cpu_started_at;
|
|
544
547
|
} atomic_ruby_thread_pool_worker_state_t;
|
|
545
548
|
|
|
546
549
|
static rb_internal_thread_specific_key_t atomic_ruby_thread_pool_worker_key;
|
|
550
|
+
static rb_nativethread_lock_t atomic_ruby_thread_pool_monitor_lock;
|
|
551
|
+
static unsigned int atomic_ruby_thread_pool_monitor_count;
|
|
552
|
+
static rb_internal_thread_event_hook_t *atomic_ruby_thread_pool_event_hook;
|
|
553
|
+
|
|
554
|
+
static void atomic_ruby_thread_pool_event_callback(rb_event_flag_t event, const rb_internal_thread_event_data_t *event_data, void *user_data);
|
|
555
|
+
|
|
556
|
+
static void atomic_ruby_thread_pool_monitor_stop(atomic_ruby_thread_pool_monitor_t *monitor) {
|
|
557
|
+
rb_nativethread_lock_lock(&atomic_ruby_thread_pool_monitor_lock);
|
|
558
|
+
if (monitor->active) {
|
|
559
|
+
monitor->active = false;
|
|
560
|
+
atomic_ruby_thread_pool_monitor_count--;
|
|
561
|
+
if (atomic_ruby_thread_pool_monitor_count == 0) {
|
|
562
|
+
rb_internal_thread_remove_event_hook(atomic_ruby_thread_pool_event_hook);
|
|
563
|
+
atomic_ruby_thread_pool_event_hook = NULL;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
rb_nativethread_lock_unlock(&atomic_ruby_thread_pool_monitor_lock);
|
|
567
|
+
}
|
|
547
568
|
|
|
548
569
|
static void atomic_ruby_thread_pool_monitor_free(void *ptr) {
|
|
570
|
+
atomic_ruby_thread_pool_monitor_stop(ptr);
|
|
549
571
|
xfree(ptr);
|
|
550
572
|
}
|
|
551
573
|
|
|
@@ -569,14 +591,27 @@ static unsigned long long atomic_ruby_monotonic_time(void) {
|
|
|
569
591
|
return (unsigned long long)time.tv_sec * 1000000000ULL + (unsigned long long)time.tv_nsec;
|
|
570
592
|
}
|
|
571
593
|
|
|
594
|
+
static unsigned long long atomic_ruby_thread_cpu_time(void) {
|
|
595
|
+
struct timespec time;
|
|
596
|
+
if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &time) == -1) return 0;
|
|
597
|
+
|
|
598
|
+
return (unsigned long long)time.tv_sec * 1000000000ULL + (unsigned long long)time.tv_nsec;
|
|
599
|
+
}
|
|
600
|
+
|
|
572
601
|
static void atomic_ruby_thread_pool_worker_leave_phase(atomic_ruby_thread_pool_worker_state_t *state, unsigned long long now) {
|
|
573
602
|
unsigned long long elapsed = now != 0 && state->phase_started_at != 0 && now >= state->phase_started_at ? now - state->phase_started_at : 0;
|
|
574
603
|
|
|
575
604
|
switch (state->phase) {
|
|
576
|
-
case ATOMIC_RUBY_THREAD_POOL_WORKER_RUNNING:
|
|
605
|
+
case ATOMIC_RUBY_THREAD_POOL_WORKER_RUNNING: {
|
|
606
|
+
unsigned long long cpu_now = atomic_ruby_thread_cpu_time();
|
|
607
|
+
unsigned long long cpu_elapsed = cpu_now != 0 && state->running_cpu_started_at != 0 && cpu_now >= state->running_cpu_started_at
|
|
608
|
+
? cpu_now - state->running_cpu_started_at
|
|
609
|
+
: 0;
|
|
577
610
|
atomic_fetch_sub_explicit(&state->monitor->running_count, 1, memory_order_relaxed);
|
|
578
611
|
atomic_fetch_add_explicit(&state->monitor->running_time, elapsed, memory_order_relaxed);
|
|
612
|
+
atomic_fetch_add_explicit(&state->monitor->running_cpu_time, cpu_elapsed, memory_order_relaxed);
|
|
579
613
|
break;
|
|
614
|
+
}
|
|
580
615
|
case ATOMIC_RUBY_THREAD_POOL_WORKER_WAITING:
|
|
581
616
|
atomic_fetch_sub_explicit(&state->monitor->waiting_count, 1, memory_order_relaxed);
|
|
582
617
|
atomic_fetch_add_explicit(&state->monitor->waiting_time, elapsed, memory_order_relaxed);
|
|
@@ -597,6 +632,7 @@ static void atomic_ruby_thread_pool_worker_enter_phase(atomic_ruby_thread_pool_w
|
|
|
597
632
|
switch (phase) {
|
|
598
633
|
case ATOMIC_RUBY_THREAD_POOL_WORKER_RUNNING:
|
|
599
634
|
atomic_fetch_add_explicit(&state->monitor->running_count, 1, memory_order_relaxed);
|
|
635
|
+
state->running_cpu_started_at = atomic_ruby_thread_cpu_time();
|
|
600
636
|
break;
|
|
601
637
|
case ATOMIC_RUBY_THREAD_POOL_WORKER_WAITING:
|
|
602
638
|
atomic_fetch_add_explicit(&state->monitor->waiting_count, 1, memory_order_relaxed);
|
|
@@ -638,9 +674,38 @@ static VALUE rb_cThreadPoolMonitor_allocate(VALUE klass) {
|
|
|
638
674
|
atomic_init(&monitor->running_time, 0);
|
|
639
675
|
atomic_init(&monitor->waiting_time, 0);
|
|
640
676
|
atomic_init(&monitor->blocked_time, 0);
|
|
677
|
+
atomic_init(&monitor->running_cpu_time, 0);
|
|
678
|
+
monitor->active = false;
|
|
641
679
|
return obj;
|
|
642
680
|
}
|
|
643
681
|
|
|
682
|
+
static VALUE rb_cThreadPoolMonitor_start(VALUE self) {
|
|
683
|
+
atomic_ruby_thread_pool_monitor_t *monitor;
|
|
684
|
+
TypedData_Get_Struct(self, atomic_ruby_thread_pool_monitor_t, &atomic_ruby_thread_pool_monitor_type, monitor);
|
|
685
|
+
rb_nativethread_lock_lock(&atomic_ruby_thread_pool_monitor_lock);
|
|
686
|
+
if (!monitor->active) {
|
|
687
|
+
monitor->active = true;
|
|
688
|
+
if (atomic_ruby_thread_pool_monitor_count++ == 0) {
|
|
689
|
+
atomic_ruby_thread_pool_event_hook = rb_internal_thread_add_event_hook(
|
|
690
|
+
atomic_ruby_thread_pool_event_callback,
|
|
691
|
+
RUBY_INTERNAL_THREAD_EVENT_READY |
|
|
692
|
+
RUBY_INTERNAL_THREAD_EVENT_RESUMED |
|
|
693
|
+
RUBY_INTERNAL_THREAD_EVENT_SUSPENDED,
|
|
694
|
+
NULL
|
|
695
|
+
);
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
rb_nativethread_lock_unlock(&atomic_ruby_thread_pool_monitor_lock);
|
|
699
|
+
return Qnil;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
static VALUE rb_cThreadPoolMonitor_stop(VALUE self) {
|
|
703
|
+
atomic_ruby_thread_pool_monitor_t *monitor;
|
|
704
|
+
TypedData_Get_Struct(self, atomic_ruby_thread_pool_monitor_t, &atomic_ruby_thread_pool_monitor_type, monitor);
|
|
705
|
+
atomic_ruby_thread_pool_monitor_stop(monitor);
|
|
706
|
+
return Qnil;
|
|
707
|
+
}
|
|
708
|
+
|
|
644
709
|
static VALUE rb_cThreadPoolMonitor_register_worker(VALUE self) {
|
|
645
710
|
atomic_ruby_thread_pool_monitor_t *monitor;
|
|
646
711
|
TypedData_Get_Struct(self, atomic_ruby_thread_pool_monitor_t, &atomic_ruby_thread_pool_monitor_type, monitor);
|
|
@@ -650,6 +715,7 @@ static VALUE rb_cThreadPoolMonitor_register_worker(VALUE self) {
|
|
|
650
715
|
state->monitor = monitor;
|
|
651
716
|
state->phase = ATOMIC_RUBY_THREAD_POOL_WORKER_INACTIVE;
|
|
652
717
|
state->phase_started_at = 0;
|
|
718
|
+
state->running_cpu_started_at = 0;
|
|
653
719
|
rb_internal_thread_specific_set(thread, atomic_ruby_thread_pool_worker_key, state);
|
|
654
720
|
return Qnil;
|
|
655
721
|
}
|
|
@@ -683,13 +749,14 @@ static VALUE rb_cThreadPoolMonitor_snapshot(VALUE self) {
|
|
|
683
749
|
TypedData_Get_Struct(self, atomic_ruby_thread_pool_monitor_t, &atomic_ruby_thread_pool_monitor_type, monitor);
|
|
684
750
|
|
|
685
751
|
return rb_ary_new_from_args(
|
|
686
|
-
|
|
752
|
+
7,
|
|
687
753
|
UINT2NUM(atomic_load_explicit(&monitor->running_count, memory_order_relaxed)),
|
|
688
754
|
UINT2NUM(atomic_load_explicit(&monitor->waiting_count, memory_order_relaxed)),
|
|
689
755
|
UINT2NUM(atomic_load_explicit(&monitor->blocked_count, memory_order_relaxed)),
|
|
690
756
|
ULL2NUM(atomic_load_explicit(&monitor->running_time, memory_order_relaxed)),
|
|
691
757
|
ULL2NUM(atomic_load_explicit(&monitor->waiting_time, memory_order_relaxed)),
|
|
692
|
-
ULL2NUM(atomic_load_explicit(&monitor->blocked_time, memory_order_relaxed))
|
|
758
|
+
ULL2NUM(atomic_load_explicit(&monitor->blocked_time, memory_order_relaxed)),
|
|
759
|
+
ULL2NUM(atomic_load_explicit(&monitor->running_cpu_time, memory_order_relaxed))
|
|
693
760
|
);
|
|
694
761
|
}
|
|
695
762
|
|
|
@@ -737,15 +804,13 @@ RUBY_FUNC_EXPORTED void Init_atomic_ruby(void) {
|
|
|
737
804
|
rb_define_private_method(rb_cAtomicConditionVariable, "_waiter_count", rb_cAtomicConditionVariable_waiter_count, 0);
|
|
738
805
|
|
|
739
806
|
atomic_ruby_thread_pool_worker_key = rb_internal_thread_specific_key_create();
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
RUBY_INTERNAL_THREAD_EVENT_RESUMED |
|
|
744
|
-
RUBY_INTERNAL_THREAD_EVENT_SUSPENDED,
|
|
745
|
-
NULL
|
|
746
|
-
);
|
|
807
|
+
rb_nativethread_lock_initialize(&atomic_ruby_thread_pool_monitor_lock);
|
|
808
|
+
atomic_ruby_thread_pool_monitor_count = 0;
|
|
809
|
+
atomic_ruby_thread_pool_event_hook = NULL;
|
|
747
810
|
VALUE rb_cThreadPoolMonitor = rb_define_class_under(rb_mAtomicRuby, "ThreadPoolMonitor", rb_cObject);
|
|
748
811
|
rb_define_alloc_func(rb_cThreadPoolMonitor, rb_cThreadPoolMonitor_allocate);
|
|
812
|
+
rb_define_method(rb_cThreadPoolMonitor, "start", rb_cThreadPoolMonitor_start, 0);
|
|
813
|
+
rb_define_method(rb_cThreadPoolMonitor, "stop", rb_cThreadPoolMonitor_stop, 0);
|
|
749
814
|
rb_define_method(rb_cThreadPoolMonitor, "register_worker", rb_cThreadPoolMonitor_register_worker, 0);
|
|
750
815
|
rb_define_method(rb_cThreadPoolMonitor, "unregister_worker", rb_cThreadPoolMonitor_unregister_worker, 0);
|
|
751
816
|
rb_define_method(rb_cThreadPoolMonitor, "start_work", rb_cThreadPoolMonitor_start_work, 0);
|
|
@@ -69,7 +69,8 @@ module AtomicRuby
|
|
|
69
69
|
#
|
|
70
70
|
# When `max_size` is greater than `size`, the pool adds temporary workers
|
|
71
71
|
# if work remains queued while its active workers spend most of their time
|
|
72
|
-
# blocked outside the GVL, but not when
|
|
72
|
+
# blocked outside the GVL, but not when Ruby execution is using the
|
|
73
|
+
# available CPU.
|
|
73
74
|
# Temporary workers leave after the queue remains empty, returning the pool
|
|
74
75
|
# to its baseline size. Omitting `max_size` creates a fixed-size pool.
|
|
75
76
|
#
|
|
@@ -124,6 +125,7 @@ module AtomicRuby
|
|
|
124
125
|
@autoscale_available = AtomicConditionVariable.new
|
|
125
126
|
@trim_requested = AtomicBoolean.new(false)
|
|
126
127
|
@thread_pool_monitor = ThreadPoolMonitor.new
|
|
128
|
+
@thread_pool_monitor.start
|
|
127
129
|
end
|
|
128
130
|
|
|
129
131
|
start
|
|
@@ -263,6 +265,7 @@ module AtomicRuby
|
|
|
263
265
|
@work_available.broadcast
|
|
264
266
|
@autoscaler&.join
|
|
265
267
|
@threads.each(&:join)
|
|
268
|
+
@thread_pool_monitor&.stop
|
|
266
269
|
end
|
|
267
270
|
|
|
268
271
|
private
|
|
@@ -403,7 +406,7 @@ module AtomicRuby
|
|
|
403
406
|
pressure_started_at = now
|
|
404
407
|
phase_samples.fill(0)
|
|
405
408
|
elsif now - pressure_started_at >= AUTOSCALE_WINDOW
|
|
406
|
-
if should_grow?(previous_snapshot, snapshot, phase_samples)
|
|
409
|
+
if should_grow?(previous_snapshot, snapshot, phase_samples, now - pressure_started_at)
|
|
407
410
|
growth_samples += 1
|
|
408
411
|
if growth_samples >= AUTOSCALE_GROWTH_SAMPLES
|
|
409
412
|
spawn_worker(temporary: true)
|
|
@@ -427,25 +430,28 @@ module AtomicRuby
|
|
|
427
430
|
# Returns whether another worker is likely to improve throughput.
|
|
428
431
|
#
|
|
429
432
|
# Requiring workers to spend a majority of their time blocked outside the
|
|
430
|
-
# GVL recognizes blocking operations. The pool only grows
|
|
431
|
-
#
|
|
433
|
+
# GVL recognizes blocking operations. The pool only grows while its workers
|
|
434
|
+
# use less than half of one CPU, avoiding extra threads when Ruby
|
|
435
|
+
# execution is the bottleneck without mistaking OS scheduling delays for
|
|
436
|
+
# GVL contention.
|
|
432
437
|
#
|
|
433
438
|
# @param previous_snapshot [Array<Integer>] previous GVL state snapshot
|
|
434
439
|
# @param snapshot [Array<Integer>] current GVL state snapshot
|
|
435
440
|
# @param phase_samples [Array<Integer>] sampled current GVL states
|
|
441
|
+
# @param elapsed_time [Float] seconds covered by the snapshots
|
|
436
442
|
# @return [true, false]
|
|
437
443
|
#
|
|
438
|
-
# @rbs (Array[Integer] previous_snapshot, Array[Integer] snapshot, Array[Integer] phase_samples) -> bool
|
|
439
|
-
def should_grow?(previous_snapshot, snapshot, phase_samples)
|
|
444
|
+
# @rbs (Array[Integer] previous_snapshot, Array[Integer] snapshot, Array[Integer] phase_samples, Float elapsed_time) -> bool
|
|
445
|
+
def should_grow?(previous_snapshot, snapshot, phase_samples, elapsed_time)
|
|
440
446
|
@threads.select!(&:alive?)
|
|
441
447
|
workers = @threads
|
|
442
448
|
return false if workers.length >= @max_size
|
|
443
449
|
return false if @active_thread_count.value < workers.length
|
|
444
450
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
451
|
+
running_time = snapshot[3] - previous_snapshot[3]
|
|
452
|
+
waiting_time = snapshot[4] - previous_snapshot[4]
|
|
453
|
+
blocked_time = snapshot[5] - previous_snapshot[5]
|
|
454
|
+
running_cpu_time = snapshot[6] - previous_snapshot[6]
|
|
449
455
|
total_time = running_time + waiting_time + blocked_time
|
|
450
456
|
|
|
451
457
|
minimum_measured_time = (AUTOSCALE_WINDOW * 1_000_000_000 * workers.length / 2).to_i
|
|
@@ -455,8 +461,8 @@ module AtomicRuby
|
|
|
455
461
|
end
|
|
456
462
|
|
|
457
463
|
total_time.positive? &&
|
|
458
|
-
|
|
459
|
-
|
|
464
|
+
blocked_time * 2 > total_time &&
|
|
465
|
+
running_cpu_time * 2 < elapsed_time * 1_000_000_000
|
|
460
466
|
end
|
|
461
467
|
end
|
|
462
468
|
end
|
data/lib/atomic-ruby/version.rb
CHANGED