diff --git a/benchmarks.yml b/benchmarks.yml index 5b61fd2f..645c1e73 100644 --- a/benchmarks.yml +++ b/benchmarks.yml @@ -235,6 +235,14 @@ throw: single_file: true ractor: true +# +# Thread benchmarks +# +threads_io_with_cpu: + desc: Benchmark to test how long I/O takes to complete when there's a mix of IO-bound and CPU-bound threads. + category: thread + single_file: true + # # Ractor scaling benchmarks # diff --git a/benchmarks/threads_io_with_cpu.rb b/benchmarks/threads_io_with_cpu.rb new file mode 100644 index 00000000..d44c7ea7 --- /dev/null +++ b/benchmarks/threads_io_with_cpu.rb @@ -0,0 +1,69 @@ +require_relative '../harness/loader' + +require "socket" +num_cpu_threads = 1 +num_io_threads = 1 + +server_started = false + +def start_tcp_server(&started) + Thread.new do + server = TCPServer.new('localhost', 0) # random open port + started.call(server.local_address.ip_port) + + loop do + client = server.accept + client.close + # Don't get request or generate response, it makes the benchmark take too long + end + end +end + +def open_tcp_connection(host, port) + TCPSocket.open(host, port) { } +end + +def fib(n) + return n if n <= 1 + fib(n - 1) + fib(n - 2) +end + +server_started = false +port = nil +start_tcp_server do |server_port| + server_started = true + port = server_port +end +loop until server_started + +io_requests_threshold = 5 # per thread + +run_benchmark(5) do + io_threads = num_io_threads.times.map do + Thread.new do + io_requests_made = 0 + loop do + open_tcp_connection("localhost", port) + io_requests_made += 1 + if io_requests_made >= io_requests_threshold + break + end + end + end + end + + stop_looping = false + + cpu_threads = num_cpu_threads.times.map do + Thread.new do + loop do + fib(30) + break if stop_looping + end + end + end + + io_threads.each(&:join) + stop_looping = true + cpu_threads.join +end diff --git a/lib/argument_parser.rb b/lib/argument_parser.rb index e7a4cf4e..2429f5e1 100644 --- a/lib/argument_parser.rb +++ b/lib/argument_parser.rb @@ -68,7 +68,7 @@ def parse(argv) args.out_override = v end - opts.on("--category=headline,other,micro,ractor", "when given, only benchmarks with specified categories will run") do |v| + opts.on("--category=headline,other,micro,thread,ractor", "when given, only benchmarks with specified categories will run") do |v| args.categories += v.split(",") if args.categories == ["ractor"] args.harness = "harness-ractor" diff --git a/test/run_benchmarks_integration_test.rb b/test/run_benchmarks_integration_test.rb index 9cf5a888..73fd5cf2 100644 --- a/test/run_benchmarks_integration_test.rb +++ b/test/run_benchmarks_integration_test.rb @@ -56,7 +56,7 @@ it 'benchmarks.yml has valid category values' do require 'yaml' data = YAML.load_file(@benchmarks_yml) - valid_categories = ['headline', 'micro', 'other'] + valid_categories = ['headline', 'micro', 'thread', 'other'] data.each do |name, metadata| if metadata['category']