Skip to content

Commit 92886b9

Browse files
Add new Performance/ConcurrentMonotonicTime cop
Follow up to rails/rails#43502. This PR adds new `Performance/ConcurrentMonotonicTime` cop. This cop identifies places where `Concurrent.monotonic_time` can be replaced by `Process.clock_gettime(Process::CLOCK_MONOTONIC)`. ```ruby # bad Concurrent.monotonic_time # good Process.clock_gettime(Process::CLOCK_MONOTONIC) ``` Below is a benchmark. ```ruby % cat monotonic_time.rb #!/usr/local/bin/ruby p `ruby -v` require 'concurrent' require 'benchmark/ips' Benchmark.ips do |x| x.report('Concurrent.monotonic_time') { Concurrent.monotonic_time } x.report('Process.clock_gettime') { Process.clock_gettime(Process::CLOCK_MONOTONIC) } x.compare! end ``` ```console % ruby monotonic_time.rb "ruby 3.0.2p107 (2021-07-07 revision 0db68f0233) [x86_64-darwin19]\n" Warming up -------------------------------------- Concurrent.monotonic_time 607.064k i/100ms Process.clock_gettime 730.862k i/100ms Calculating ------------------------------------- Concurrent.monotonic_time 5.695M (± 4.4%) i/s - 28.532M in 5.019798s Process.clock_gettime 7.398M (± 2.5%) i/s - 37.274M in 5.041776s Comparison: Process.clock_gettime: 7397700.9 i/s Concurrent.monotonic_time: 5695385.0 i/s - 1.30x (± 0.00) slower ``` And this cop is compatible with ruby-concurrency/concurrent-ruby#915.
1 parent 71655f7 commit 92886b9

File tree

7 files changed

+123
-0
lines changed

7 files changed

+123
-0
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* [#267](https://github.com/rubocop/rubocop-performance/pull/267): Add new `Performance/ConcurrentMonotonicTime` cop. ([@koic][])

config/default.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,12 @@ Performance/CompareWithBlock:
7676
Enabled: true
7777
VersionAdded: '0.46'
7878

79+
Performance/ConcurrentMonotonicTime:
80+
Description: 'Use `Process.clock_gettime(Process::CLOCK_MONOTONIC)` instead of `Concurrent.monotonic_time`.'
81+
Reference: 'https://github.com/rails/rails/pull/43502'
82+
Enabled: pending
83+
VersionAdded: '1.12'
84+
7985
Performance/ConstantRegexp:
8086
Description: 'Finds regular expressions with dynamic components that are all constants.'
8187
Enabled: pending

docs/modules/ROOT/pages/cops.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ Performance cops optimization analysis for your projects.
2323
* xref:cops_performance.adoc#performancechainarrayallocation[Performance/ChainArrayAllocation]
2424
* xref:cops_performance.adoc#performancecollectionliteralinloop[Performance/CollectionLiteralInLoop]
2525
* xref:cops_performance.adoc#performancecomparewithblock[Performance/CompareWithBlock]
26+
* xref:cops_performance.adoc#performanceconcurrentmonotonictime[Performance/ConcurrentMonotonicTime]
2627
* xref:cops_performance.adoc#performanceconstantregexp[Performance/ConstantRegexp]
2728
* xref:cops_performance.adoc#performancecount[Performance/Count]
2829
* xref:cops_performance.adoc#performancedeleteprefix[Performance/DeletePrefix]

docs/modules/ROOT/pages/cops_performance.adoc

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,36 @@ array.min_by(&:foo)
458458
array.sort_by { |a| a[:foo] }
459459
----
460460

461+
== Performance/ConcurrentMonotonicTime
462+
463+
|===
464+
| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed
465+
466+
| Pending
467+
| Yes
468+
| Yes
469+
| 1.12
470+
| -
471+
|===
472+
473+
This cop identifies places where `Concurrent.monotonic_time`
474+
can be replaced by `Process.clock_gettime(Process::CLOCK_MONOTONIC)`.
475+
476+
=== Examples
477+
478+
[source,ruby]
479+
----
480+
# bad
481+
Concurrent.monotonic_time
482+
483+
# good
484+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
485+
----
486+
487+
=== References
488+
489+
* https://github.com/rails/rails/pull/43502
490+
461491
== Performance/ConstantRegexp
462492

463493
|===
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# frozen_string_literal: true
2+
3+
module RuboCop
4+
module Cop
5+
module Performance
6+
# This cop identifies places where `Concurrent.monotonic_time`
7+
# can be replaced by `Process.clock_gettime(Process::CLOCK_MONOTONIC)`.
8+
#
9+
# @example
10+
#
11+
# # bad
12+
# Concurrent.monotonic_time
13+
#
14+
# # good
15+
# Process.clock_gettime(Process::CLOCK_MONOTONIC)
16+
#
17+
class ConcurrentMonotonicTime < Base
18+
extend AutoCorrector
19+
20+
MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
21+
RESTRICT_ON_SEND = %i[monotonic_time].freeze
22+
23+
def_node_matcher :concurrent_monotonic_time?, <<~PATTERN
24+
(send
25+
(const {nil? cbase} :Concurrent) :monotonic_time ...)
26+
PATTERN
27+
28+
def on_send(node)
29+
return unless concurrent_monotonic_time?(node)
30+
31+
optional_unit_parameter = ", #{node.first_argument.source}" if node.first_argument
32+
prefer = "Process.clock_gettime(Process::CLOCK_MONOTONIC#{optional_unit_parameter})"
33+
message = format(MSG, prefer: prefer, current: node.source)
34+
35+
add_offense(node, message: message) do |corrector|
36+
corrector.replace(node, prefer)
37+
end
38+
end
39+
end
40+
end
41+
end
42+
end

lib/rubocop/cop/performance_cops.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
require_relative 'performance/casecmp'
1414
require_relative 'performance/collection_literal_in_loop'
1515
require_relative 'performance/compare_with_block'
16+
require_relative 'performance/concurrent_monotonic_time'
1617
require_relative 'performance/constant_regexp'
1718
require_relative 'performance/count'
1819
require_relative 'performance/delete_prefix'
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# frozen_string_literal: true
2+
3+
RSpec.describe RuboCop::Cop::Performance::ConcurrentMonotonicTime, :config do
4+
it 'registers an offense when using `Concurrent.monotonic_time`' do
5+
expect_offense(<<~RUBY)
6+
Concurrent.monotonic_time
7+
^^^^^^^^^^^^^^^^^^^^^^^^^ Use `Process.clock_gettime(Process::CLOCK_MONOTONIC)` instead of `Concurrent.monotonic_time`.
8+
RUBY
9+
10+
expect_correction(<<~RUBY)
11+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
12+
RUBY
13+
end
14+
15+
it 'registers an offense when using `::Concurrent.monotonic_time`' do
16+
expect_offense(<<~RUBY)
17+
::Concurrent.monotonic_time
18+
^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `Process.clock_gettime(Process::CLOCK_MONOTONIC)` instead of `::Concurrent.monotonic_time`.
19+
RUBY
20+
21+
expect_correction(<<~RUBY)
22+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
23+
RUBY
24+
end
25+
26+
it 'registers an offense when using `Concurrent.monotonic_time` with an optional unit parameter' do
27+
expect_offense(<<~RUBY)
28+
Concurrent.monotonic_time(:float_second)
29+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_second)` instead of `Concurrent.monotonic_time(:float_second)`.
30+
RUBY
31+
32+
expect_correction(<<~RUBY)
33+
Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_second)
34+
RUBY
35+
end
36+
37+
it 'does not register an offense when using `Process.clock_gettime(Process::CLOCK_MONOTONIC)`' do
38+
expect_no_offenses(<<~RUBY)
39+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
40+
RUBY
41+
end
42+
end

0 commit comments

Comments
 (0)