Skip to content

Commit 6218c1f

Browse files
authored
Simple nightly benchmark (#235)
1 parent 6b4631a commit 6218c1f

File tree

4 files changed

+194
-0
lines changed

4 files changed

+194
-0
lines changed

.github/workflows/nightly.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
name: Nightly
2+
3+
on:
4+
schedule:
5+
# (12 AM PST)
6+
- cron: "00 07 * * *"
7+
8+
jobs:
9+
nightly:
10+
uses: ./.github/workflows/run-bench.yml

.github/workflows/run-bench.yml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
name: Run Bench
2+
on:
3+
workflow_call:
4+
workflow_dispatch:
5+
6+
jobs:
7+
run-bench:
8+
runs-on: ubuntu-latest-4-cores
9+
defaults:
10+
run:
11+
working-directory: ./temporalio
12+
steps:
13+
- name: Checkout repository
14+
uses: actions/checkout@v4
15+
with:
16+
submodules: recursive
17+
18+
- name: Setup Ruby and Rust
19+
uses: oxidize-rb/actions/setup-ruby-and-rust@v1
20+
with:
21+
ruby-version: "3.4"
22+
bundler-cache: true
23+
cargo-cache: true
24+
working-directory: ./temporalio
25+
26+
- name: Install bundle
27+
run: bundle install
28+
29+
- name: Compile
30+
run: bundle exec rake compile
31+
32+
# Run a bunch of bench tests. We run multiple times since results vary.
33+
34+
- run: bundle exec ruby extra/simple_bench.rb --workflow-count 100 --max-cached-workflows 100 --max-concurrent 100
35+
- run: bundle exec ruby extra/simple_bench.rb --workflow-count 100 --max-cached-workflows 100 --max-concurrent 100
36+
- run: bundle exec ruby extra/simple_bench.rb --workflow-count 100 --max-cached-workflows 100 --max-concurrent 100
37+
38+
- run: bundle exec ruby extra/simple_bench.rb --workflow-count 1000 --max-cached-workflows 1000 --max-concurrent 1000
39+
- run: bundle exec ruby extra/simple_bench.rb --workflow-count 1000 --max-cached-workflows 1000 --max-concurrent 1000
40+
- run: bundle exec ruby extra/simple_bench.rb --workflow-count 1000 --max-cached-workflows 1000 --max-concurrent 1000
41+
42+
- run: bundle exec ruby extra/simple_bench.rb --workflow-count 1000 --max-cached-workflows 100 --max-concurrent 100
43+
- run: bundle exec ruby extra/simple_bench.rb --workflow-count 1000 --max-cached-workflows 100 --max-concurrent 100
44+
- run: bundle exec ruby extra/simple_bench.rb --workflow-count 1000 --max-cached-workflows 100 --max-concurrent 100
45+
46+
- run: bundle exec ruby extra/simple_bench.rb --workflow-count 10000 --max-cached-workflows 10000 --max-concurrent 10000
47+
- run: bundle exec ruby extra/simple_bench.rb --workflow-count 10000 --max-cached-workflows 10000 --max-concurrent 10000
48+
49+
- run: bundle exec ruby extra/simple_bench.rb --workflow-count 10000 --max-cached-workflows 1000 --max-concurrent 1000
50+
- run: bundle exec ruby extra/simple_bench.rb --workflow-count 10000 --max-cached-workflows 1000 --max-concurrent 1000

temporalio/extra/simple_bench.rb

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
# frozen_string_literal: true
2+
3+
# rubocop:disable Style/Documentation, Style/DocumentationMethod
4+
5+
require_relative '../lib/temporalio/activity'
6+
require_relative '../lib/temporalio/client'
7+
require_relative '../lib/temporalio/testing'
8+
require_relative '../lib/temporalio/worker'
9+
require_relative '../lib/temporalio/workflow'
10+
11+
require 'async'
12+
require 'async/barrier'
13+
require 'logger'
14+
require 'optparse'
15+
require 'securerandom'
16+
17+
module SimpleBench
18+
class BenchActivity < Temporalio::Activity::Definition
19+
def execute(name)
20+
"Hello, #{name}!"
21+
end
22+
end
23+
24+
class BenchWorkflow < Temporalio::Workflow::Definition
25+
def execute(name)
26+
Temporalio::Workflow.execute_activity(BenchActivity, name, start_to_close_timeout: 30)
27+
end
28+
end
29+
end
30+
31+
def run_bench(workflow_count:, max_cached_workflows:, max_concurrent:)
32+
logger = Logger.new($stdout)
33+
34+
# Track mem
35+
stop_track_mem = false
36+
track_mem_task = Async do
37+
max_mem_mib = 0
38+
until stop_track_mem
39+
sleep(0.8)
40+
curr_mem = `ps -o rss= -p #{Process.pid}`.to_i / 1024
41+
max_mem_mib = curr_mem if curr_mem > max_mem_mib
42+
end
43+
max_mem_mib
44+
end
45+
46+
logger.info('Starting local environment')
47+
Temporalio::Testing::WorkflowEnvironment.start_local(logger:) do |env|
48+
task_queue = "tq-#{SecureRandom.uuid}"
49+
50+
# Create a bunch of workflows
51+
logger.info("Starting #{workflow_count} workflows")
52+
start_begin = Process.clock_gettime(Process::CLOCK_MONOTONIC)
53+
handle_tasks = workflow_count.times.map do |i|
54+
Async do
55+
env.client.start_workflow(SimpleBench::BenchWorkflow, "user-#{i}", id: "wf-#{SecureRandom.uuid}", task_queue:)
56+
end
57+
end
58+
handles = handle_tasks.map(&:wait)
59+
start_seconds = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_begin).round(3)
60+
61+
# Start a worker to run them all
62+
logger.info('Starting worker')
63+
result_seconds = Temporalio::Worker.new(
64+
client: env.client,
65+
task_queue:,
66+
activities: [SimpleBench::BenchActivity],
67+
workflows: [SimpleBench::BenchWorkflow],
68+
tuner: Temporalio::Worker::Tuner.create_fixed(
69+
workflow_slots: max_concurrent,
70+
activity_slots: max_concurrent,
71+
local_activity_slots: max_concurrent
72+
),
73+
max_cached_workflows:
74+
).run do
75+
# Wait for all workflows
76+
result_begin = Process.clock_gettime(Process::CLOCK_MONOTONIC)
77+
handles.map(&:result)
78+
(Process.clock_gettime(Process::CLOCK_MONOTONIC) - result_begin).round(3)
79+
end
80+
81+
# Report results
82+
stop_track_mem = true
83+
puts 'Results:', {
84+
workflow_count:,
85+
max_cached_workflows:,
86+
max_concurrent:,
87+
max_mem_mib: track_mem_task.wait,
88+
start_seconds:,
89+
result_seconds:,
90+
workflows_per_second: (workflow_count / result_seconds).round(3)
91+
}
92+
end
93+
ensure
94+
stop_track_mem = true
95+
logger.close
96+
end
97+
98+
def track_mem(&)
99+
stop = false
100+
thread = Thread.new do
101+
max_mem = 0
102+
until stop
103+
sleep(0.8)
104+
curr_mem = `ps -o rss= -p #{Process.pid}`.to_i
105+
max_mem = curr_mem if curr_mem > max_mem
106+
end
107+
max_mem
108+
end
109+
yield
110+
stop = true
111+
thread.value
112+
ensure
113+
stop = true
114+
end
115+
116+
# Parse options
117+
parser = OptionParser.new
118+
workflow_count = 0
119+
max_cached_workflows = 0
120+
max_concurrent = 0
121+
parser.on('--workflow-count WORKFLOW_COUNT') { |v| workflow_count = v.to_i }
122+
parser.on('--max-cached-workflows MAX_CACHED_WORKFLOWS') { |v| max_cached_workflows = v.to_i }
123+
parser.on('--max-concurrent MAX_CONCURRENT') { |v| max_concurrent = v.to_i }
124+
parser.parse!
125+
if workflow_count.zero? || max_cached_workflows.zero? || max_concurrent.zero?
126+
puts parser
127+
raise 'Missing one or more arguments'
128+
end
129+
130+
# Run
131+
Sync { run_bench(workflow_count:, max_cached_workflows:, max_concurrent:) }
132+
133+
# rubocop:enable Style/Documentation, Style/DocumentationMethod

temporalio/temporalio.gemspec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,5 @@ Gem::Specification.new do |spec|
2525
spec.metadata['rubygems_mfa_required'] = 'true'
2626

2727
spec.add_dependency 'google-protobuf', '>= 3.25.0'
28+
spec.add_dependency 'logger'
2829
end

0 commit comments

Comments
 (0)