rails_benchmark_suite 0.2.7 β 0.2.9
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:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c48ce5faaa5348bcd47cdd9c88ba9b1441d0f7bcd0a6fa601d84b36014673d1a
|
|
4
|
+
data.tar.gz: f39f188c55fe6749a5b9ba4ae9c5c44e926bd5d6588737f689192ab841deda92
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 28f32ff03be6cbd83aabb1c3f8750316f5c3e2cb69de127885f96a4ef27841b12ee916999c2dea59c314605a6755f0aee34d0c610338c596bbff2789120a7ce0
|
|
7
|
+
data.tar.gz: 2e4f5a17c66e0caed9a95a46cdc3e0049783eea603f9cd263d699f492d49440f7ceaab45bf4c469b8b05fc316ca999052d12705dfe29b0edf54b032160fc2966
|
data/README.md
CHANGED
|
@@ -1,23 +1,44 @@
|
|
|
1
|
-
# Rails Benchmark Suite
|
|
1
|
+
# Rails Benchmark Suite π
|
|
2
|
+
|
|
3
|
+
**Standardized Hardware Benchmarking for Rails 8.1+**
|
|
2
4
|
|
|
3
5
|
A standardized performance suite designed to measure the "Heft" of a machine using realistic, high-throughput Rails 8+ workloads.
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
## π What is this?
|
|
8
|
+
|
|
9
|
+
Think of this as a **"Test Track" for Rails servers**. Unlike profilers that measure your specific application code, this gem runs a **fixed, standardized set of Rails operations** (Active Record object allocation, SQL query complexity, ActionView rendering, and background job throughput) to measure the raw performance of your server and Ruby configuration.
|
|
10
|
+
|
|
11
|
+
To ensure a level playing field, the gem boots an **isolated, in-memory SQLite environment**. It creates its own schema and records, meaning it **never touches your production data** and returns comparable results across any machine.
|
|
6
12
|
|
|
7
13
|
## π The "Heft" Score
|
|
8
14
|
|
|
9
|
-
The Heft Score is a weighted metric representing a machine's ability to handle Rails tasks.
|
|
10
|
-
|
|
11
|
-
|
|
15
|
+
The Heft Score is a weighted metric representing a machine's ability to handle Rails tasks.
|
|
16
|
+
* **Baseline:** A score of **100** is calibrated to represent an **AWS c6g.large** (ARM) instance.
|
|
17
|
+
* **Objective:** To provide a simple, comparable number for evaluating different computing platforms (Cloud VMs, bare-metal, or local dev rigs).
|
|
12
18
|
|
|
13
19
|
### Baseline Comparisons
|
|
20
|
+
|
|
14
21
|
| Score | Classification | Comparable Hardware |
|
|
15
22
|
| :--- | :--- | :--- |
|
|
16
|
-
|
|
|
17
|
-
| 60 | π Capable | Standard Cloud VM (c5.large/standard) |
|
|
18
|
-
| **100** |
|
|
19
|
-
| 150
|
|
20
|
-
| 300
|
|
23
|
+
| **< 40** | π’ Sluggish | Older Intel Macs, Entry-level VPS |
|
|
24
|
+
| **60** | π Capable | Standard Cloud VM (c5.large/standard) |
|
|
25
|
+
| **100** | ποΈ Baseline | AWS c6g.large (2 vCPU ARM) |
|
|
26
|
+
| **150+** | π High Performance | Apple M-series Pro/Max, Ryzen 5000+ |
|
|
27
|
+
| **300+** | β‘ Blazing | Server-grade Metal, M3 Ultra |
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## π Quick Start
|
|
32
|
+
|
|
33
|
+
Ensure you are in your Rails root directory and run:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
ruby --yjit -S bundle exec rails_benchmark_suite
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**Note:** `bundle exec` is mandatory for Rails environment stability and to prevent Minitest version conflicts.
|
|
40
|
+
|
|
41
|
+
---
|
|
21
42
|
|
|
22
43
|
## π Technical Philosophy
|
|
23
44
|
|
|
@@ -26,28 +47,16 @@ Rails Benchmark Suite prioritizes **Benchmarking** (via `benchmark-ips`) over **
|
|
|
26
47
|
* **Benchmarking:** Focuses on macro-throughputβ"How many iterations can the hardware handle?" This provides the final Heft Score.
|
|
27
48
|
* **Why no Profiling?** Profiling tools (like `StackProf` or `Vernier`) introduce instrumentation overhead that skews hardware metrics. We aim for "Conceptual Compression"βone clear number to inform infrastructure decisions.
|
|
28
49
|
|
|
50
|
+
---
|
|
51
|
+
|
|
29
52
|
## π Installation & Usage
|
|
30
53
|
|
|
31
54
|
### Requirements
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
### Prerequisites
|
|
36
|
-
* **Ruby:** 3.4.1+ (Recommended for latest YJIT/Prism performance)
|
|
55
|
+
* **Ruby:** 3.3+ (Ruby with YJIT support highly recommended)
|
|
56
|
+
* **Rails:** 8.1+
|
|
37
57
|
* **Database:** SQLite3
|
|
38
58
|
|
|
39
|
-
### Standalone Usage
|
|
40
|
-
If you want to test hardware performance without an existing application:
|
|
41
|
-
|
|
42
|
-
```bash
|
|
43
|
-
git clone https://github.com/overnet/rails_benchmark_suite.git
|
|
44
|
-
cd rails_benchmark_suite
|
|
45
|
-
bundle install
|
|
46
|
-
bin/rails_benchmark_suite
|
|
47
|
-
```
|
|
48
|
-
|
|
49
59
|
### Use within a Rails Application
|
|
50
|
-
Rails Benchmark Suite is "Rails-aware." Adding it to your app allows you to benchmark your specific configuration and custom suites.
|
|
51
60
|
|
|
52
61
|
Add to your Gemfile:
|
|
53
62
|
|
|
@@ -55,84 +64,69 @@ Add to your Gemfile:
|
|
|
55
64
|
gem "rails_benchmark_suite", group: :development
|
|
56
65
|
```
|
|
57
66
|
|
|
58
|
-
|
|
67
|
+
### Usage Flags
|
|
68
|
+
* `--yjit`: Enables the Ruby JIT compiler (significant for Rails 8+ performance).
|
|
69
|
+
* `-S`: Corrects the path to look for the executable in your current bundle.
|
|
70
|
+
* `--json`: For programmatic consumption of results.
|
|
71
|
+
* `--skip-rails`: To ignore the host application and run in isolated mode.
|
|
59
72
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
```bash
|
|
63
|
-
ruby --yjit -S bundle exec rails_benchmark_suite
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
**Why these flags?**
|
|
67
|
-
- `--yjit`: Enables the Ruby JIT compiler (significant for Rails 8+ performance).
|
|
68
|
-
- `-S`: Corrects the path to look for the executable in your current bundle.
|
|
69
|
-
- `bundle exec`: Prevents version conflicts (e.g., Minitest) between the gem and your host application.
|
|
73
|
+
### Standalone Usage
|
|
70
74
|
|
|
71
|
-
|
|
72
|
-
If running outside a Rails project:
|
|
75
|
+
If you want to test hardware performance without an existing application:
|
|
73
76
|
|
|
74
77
|
```bash
|
|
78
|
+
git clone https://github.com/overnet/rails_benchmark_suite.git
|
|
79
|
+
cd rails_benchmark_suite
|
|
80
|
+
bundle install
|
|
75
81
|
bin/rails_benchmark_suite
|
|
76
82
|
```
|
|
77
83
|
|
|
78
|
-
|
|
79
|
-
For programmatic consumption:
|
|
80
|
-
|
|
81
|
-
```bash
|
|
82
|
-
ruby --yjit -S bundle exec rails_benchmark_suite --json
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
> **Note:** Use `--skip-rails` to ignore the host application and run in isolated mode.
|
|
84
|
+
---
|
|
86
85
|
|
|
87
|
-
##
|
|
86
|
+
## π§ͺ The "Heft" Suites
|
|
88
87
|
|
|
89
|
-
|
|
88
|
+
The gem measures performance across critical Rails subsystems using a dedicated, isolated schema:
|
|
90
89
|
|
|
91
|
-
|
|
90
|
+
* **Active Record Heft:** Standardized CRUD: Creation, indexing, and complex querying.
|
|
91
|
+
* **Cache Heft:** High-frequency read/writes to the Rails memory store.
|
|
92
|
+
* **Solid Queue Heft:** Background job enqueuing and database-backed polling stress.
|
|
93
|
+
* **View Heft:** Partial rendering overhead and ActionView throughput.
|
|
94
|
+
* **Image Heft:** Image processing performance (requires libvips).
|
|
92
95
|
|
|
93
|
-
|
|
96
|
+
---
|
|
94
97
|
|
|
95
|
-
## Troubleshooting
|
|
98
|
+
## β οΈ Troubleshooting
|
|
96
99
|
|
|
97
100
|
### YJIT Shows "Disabled"
|
|
98
101
|
|
|
99
|
-
If you see `YJIT: Disabled
|
|
102
|
+
If you see `YJIT: Disabled`, it means your Ruby was not compiled with YJIT support.
|
|
100
103
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
```bash
|
|
104
|
-
# Using rbenv
|
|
105
|
-
RUBY_CONFIGURE_OPTS="--enable-yjit" rbenv install 3.4.1
|
|
106
|
-
|
|
107
|
-
# Using rvm
|
|
108
|
-
rvm install 3.4.1 --enable-yjit
|
|
109
|
-
```
|
|
110
|
-
3. Verify YJIT is available: `ruby --yjit -e "puts RubyVM::YJIT.enabled?"`
|
|
104
|
+
* **Fix (rbenv):** `RUBY_CONFIGURE_OPTS="--enable-yjit" rbenv install 3.4.1`
|
|
105
|
+
* **Fix (rvm):** `rvm install 3.4.1 --enable-yjit`
|
|
111
106
|
|
|
112
107
|
### SQLite Lock Errors
|
|
113
108
|
|
|
114
|
-
|
|
109
|
+
Version 0.2.9+ includes surgical connection resets and randomized backoffs to handle SQLite concurrency. If issues persist, ensure no other processes are accessing the benchmark database.
|
|
115
110
|
|
|
116
|
-
|
|
117
|
-
- Automatic retry logic with sleep backoff
|
|
118
|
-
- Per-thread unique identifiers to prevent conflicts
|
|
119
|
-
- Optimized busy timeout settings (10 seconds)
|
|
120
|
-
|
|
121
|
-
If issues persist, try reducing concurrency or ensuring no other processes are accessing the benchmark database.
|
|
111
|
+
---
|
|
122
112
|
|
|
123
113
|
## π Architecture
|
|
114
|
+
|
|
124
115
|
* **Engine:** Built on `benchmark-ips`.
|
|
125
|
-
* **Database:** Uses In-Memory SQLite with `cache=shared` for multi-threaded accuracy.
|
|
126
|
-
* **Isolation:** Uses transactional rollbacks
|
|
127
|
-
* **Threading:** Supports 1-thread and 4-thread scaling tests
|
|
128
|
-
|
|
116
|
+
* **Database:** Uses In-Memory SQLite with `cache=shared` and a 50-connection pool for multi-threaded accuracy.
|
|
117
|
+
* **Isolation:** Uses transactional rollbacks and Mutex-wrapped schema creation.
|
|
118
|
+
* **Threading:** Supports 1-thread and 4-thread scaling tests.
|
|
119
|
+
|
|
120
|
+
---
|
|
129
121
|
|
|
130
122
|
## π Credits
|
|
131
|
-
This project is a functional implementation of the performance benchmark vision discussed in the Rails community.
|
|
132
123
|
|
|
133
124
|
* **Vision:** Inspired by @dhh in [rails/rails#50451](https://github.com/rails/rails/issues/50451).
|
|
134
125
|
* **Initial Roadmap:** Based on suggestions by @JoeDupuis.
|
|
135
126
|
* **Implementation:** The Rails Community.
|
|
136
127
|
|
|
128
|
+
---
|
|
129
|
+
|
|
137
130
|
## π License
|
|
131
|
+
|
|
138
132
|
The gem is available as open source under the terms of the MIT License.
|
|
@@ -17,12 +17,12 @@ module RailsBenchmarkSuite
|
|
|
17
17
|
SETUP_MUTEX = Mutex.new
|
|
18
18
|
|
|
19
19
|
def run
|
|
20
|
-
#
|
|
20
|
+
# Ultimate Hardening: Massive pool and timeout for zero lock contention (v0.2.9)
|
|
21
21
|
ActiveRecord::Base.establish_connection(
|
|
22
22
|
adapter: "sqlite3",
|
|
23
23
|
database: "file:heft_db?mode=memory&cache=shared",
|
|
24
|
-
pool:
|
|
25
|
-
timeout:
|
|
24
|
+
pool: 50,
|
|
25
|
+
timeout: 30000
|
|
26
26
|
)
|
|
27
27
|
|
|
28
28
|
# The 'Busy Timeout' Hammer - force it directly on the raw connection
|
|
@@ -36,8 +36,10 @@ module RailsBenchmarkSuite
|
|
|
36
36
|
end
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
-
# High-Performance Pragmas
|
|
40
|
-
ActiveRecord::Base.connection.raw_connection.execute("PRAGMA
|
|
39
|
+
# High-Performance Pragmas for WAL + NORMAL sync
|
|
40
|
+
ActiveRecord::Base.connection.raw_connection.execute("PRAGMA journal_mode = WAL")
|
|
41
|
+
ActiveRecord::Base.connection.raw_connection.execute("PRAGMA synchronous = NORMAL")
|
|
42
|
+
ActiveRecord::Base.connection.raw_connection.execute("PRAGMA mmap_size = 268435456") # 256MB - reduce disk I/O
|
|
41
43
|
|
|
42
44
|
puts "Running RailsBenchmarkSuite Benchmarks..." unless @json_output
|
|
43
45
|
puts system_report unless @json_output
|
|
@@ -57,7 +59,7 @@ module RailsBenchmarkSuite
|
|
|
57
59
|
|
|
58
60
|
# Single Threaded
|
|
59
61
|
x.report("#{suite[:name]} (1 thread)") do
|
|
60
|
-
suite[:block].call
|
|
62
|
+
with_retries { suite[:block].call }
|
|
61
63
|
end
|
|
62
64
|
|
|
63
65
|
# Multi Threaded (4 threads)
|
|
@@ -66,7 +68,7 @@ module RailsBenchmarkSuite
|
|
|
66
68
|
Thread.new do
|
|
67
69
|
# Ensure each thread gets a dedicated connection
|
|
68
70
|
ActiveRecord::Base.connection_pool.with_connection do
|
|
69
|
-
suite[:block].call
|
|
71
|
+
with_retries { suite[:block].call }
|
|
70
72
|
end
|
|
71
73
|
end
|
|
72
74
|
end
|
|
@@ -95,6 +97,19 @@ module RailsBenchmarkSuite
|
|
|
95
97
|
|
|
96
98
|
private
|
|
97
99
|
|
|
100
|
+
def with_retries
|
|
101
|
+
yield
|
|
102
|
+
rescue ActiveRecord::StatementInvalid => e
|
|
103
|
+
if e.message =~ /locked/i
|
|
104
|
+
# Specifically drop the lock for THIS connection only
|
|
105
|
+
ActiveRecord::Base.connection.reset!
|
|
106
|
+
sleep(rand(0.01..0.05))
|
|
107
|
+
retry
|
|
108
|
+
else
|
|
109
|
+
raise e
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
98
113
|
def system_report
|
|
99
114
|
info = RailsBenchmarkSuite::Reporter.system_info
|
|
100
115
|
yjit_status = if defined?(RubyVM::YJIT) && RubyVM::YJIT.enabled?
|
|
@@ -6,39 +6,30 @@ require "active_record"
|
|
|
6
6
|
RailsBenchmarkSuite.register_suite("Active Record Heft", weight: 0.4) do
|
|
7
7
|
# Workload: Create User with Posts, Join Query, Update
|
|
8
8
|
# Use transaction rollback to keep the DB clean and avoid costly destroy callbacks
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
user.posts.create!(title: "Post #{i}", body: "Content " * 50)
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
# 3. Complex Query (Join + Order)
|
|
23
|
-
# Unloading the relation to force execution
|
|
24
|
-
RailsBenchmarkSuite::Models::User.joins(:posts)
|
|
25
|
-
.where(users: { id: user.id })
|
|
26
|
-
.where("posts.views >= ?", 0)
|
|
27
|
-
.order("posts.created_at DESC")
|
|
28
|
-
.to_a
|
|
29
|
-
|
|
30
|
-
# 4. Update
|
|
31
|
-
user.update!(name: "Updated User")
|
|
32
|
-
|
|
33
|
-
# Rollback everything to leave the DB clean for next iteration
|
|
34
|
-
raise ActiveRecord::Rollback
|
|
35
|
-
end
|
|
36
|
-
rescue ActiveRecord::StatementInvalid => e
|
|
37
|
-
if e.message =~ /locked/i
|
|
38
|
-
sleep(0.005)
|
|
39
|
-
retry
|
|
40
|
-
else
|
|
41
|
-
raise e
|
|
9
|
+
ActiveRecord::Base.transaction do
|
|
10
|
+
# 1. Create - with unique email per thread
|
|
11
|
+
user = RailsBenchmarkSuite::Models::User.create!(
|
|
12
|
+
name: "Benchmark User",
|
|
13
|
+
email: "test-#{Thread.current.object_id}@example.com"
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
# 2. Create associated records (simulate some weight)
|
|
17
|
+
10.times do |i|
|
|
18
|
+
user.posts.create!(title: "Post #{i}", body: "Content " * 50)
|
|
42
19
|
end
|
|
20
|
+
|
|
21
|
+
# 3. Complex Query (Join + Order)
|
|
22
|
+
# Unloading the relation to force execution
|
|
23
|
+
RailsBenchmarkSuite::Models::User.joins(:posts)
|
|
24
|
+
.where(users: { id: user.id })
|
|
25
|
+
.where("posts.views >= ?", 0)
|
|
26
|
+
.order("posts.created_at DESC")
|
|
27
|
+
.to_a
|
|
28
|
+
|
|
29
|
+
# 4. Update
|
|
30
|
+
user.update!(name: "Updated User")
|
|
31
|
+
|
|
32
|
+
# Rollback everything to leave the DB clean for next iteration
|
|
33
|
+
raise ActiveRecord::Rollback
|
|
43
34
|
end
|
|
44
35
|
end
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
# Check if vips is available at registration time
|
|
3
4
|
begin
|
|
4
5
|
require "image_processing/vips"
|
|
5
6
|
require "fileutils"
|
|
@@ -9,6 +10,7 @@ begin
|
|
|
9
10
|
FileUtils.mkdir_p(ASSET_DIR)
|
|
10
11
|
SAMPLE_IMAGE = File.join(ASSET_DIR, "sample.jpg")
|
|
11
12
|
|
|
13
|
+
# Only register if vips is actually available
|
|
12
14
|
RailsBenchmarkSuite.register_suite("Image Heft", weight: 0.1) do
|
|
13
15
|
# Gracefully handle missing dependencies
|
|
14
16
|
if File.exist?(SAMPLE_IMAGE)
|
|
@@ -22,13 +24,8 @@ begin
|
|
|
22
24
|
end
|
|
23
25
|
end
|
|
24
26
|
|
|
25
|
-
rescue LoadError, StandardError
|
|
26
|
-
#
|
|
27
|
-
|
|
28
|
-
@warned ||= begin
|
|
29
|
-
warn "β οΈ [RailsBenchmarkSuite] ImageHeft skipped: #{e.message}. Install libvips to enable."
|
|
30
|
-
true
|
|
31
|
-
end
|
|
32
|
-
end
|
|
27
|
+
rescue LoadError, StandardError
|
|
28
|
+
# Don't register the suite at all if vips is unavailable
|
|
29
|
+
puts "β οΈ Skipping Image Heft: libvips not available. Install with: brew install vips (macOS)"
|
|
33
30
|
end
|
|
34
31
|
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rails_benchmark_suite
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.9
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- RailsBenchmarkSuite Contributors
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-01-
|
|
10
|
+
date: 2026-01-03 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: benchmark-ips
|