sus-fixtures-benchmark 0.1.0 → 0.2.1
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
- checksums.yaml.gz.sig +0 -0
- data/context/getting-started.md +127 -0
- data/lib/sus/fixtures/benchmark/repeats.rb +49 -6
- data/lib/sus/fixtures/benchmark/time.rb +1 -1
- data/lib/sus/fixtures/benchmark/version.rb +1 -1
- data/lib/sus/fixtures/benchmark.rb +7 -2
- data/readme.md +10 -2
- data/releases.md +8 -0
- data.tar.gz.sig +0 -0
- metadata +5 -4
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: de94d05fdae13b52e4e260d2f2fc5fc65792436d6756409fb332e77e4bc64b81
|
4
|
+
data.tar.gz: c0a75a615f5021dbd5ae2f26bb6d5a1eb4ac7c9d5ae22955bffe6573106d260b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0212ff21fd9e1dc27a2ee42d08bce8e1a2a713bdb204c869789f37a651fa828ff9047d4c2b4f4c85cf1322c822ce232fa2691da90a8d1808e1e7b94636eeb9e4
|
7
|
+
data.tar.gz: 72b37243db4d7c841924e17458dae86e8c028f1327f27711ece5603cb21f87831399498756fe2e1b4ad09be3119db714208c918c99d22a8742bb9efff1c47d0d
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
@@ -0,0 +1,127 @@
|
|
1
|
+
# Getting Started
|
2
|
+
|
3
|
+
This guide explains how to use the `sus-fixtures-benchmark` gem to measure and benchmark code performance within your Sus test suite.
|
4
|
+
|
5
|
+
## Quick Start
|
6
|
+
|
7
|
+
### Project Structure
|
8
|
+
|
9
|
+
We recommend organizing your benchmarks in a dedicated `benchmark/` directory:
|
10
|
+
|
11
|
+
```
|
12
|
+
your-project/
|
13
|
+
├── benchmark/
|
14
|
+
│ ├── database_performance.rb
|
15
|
+
│ ├── algorithm_comparison.rb
|
16
|
+
│ └── memory_usage.rb
|
17
|
+
├── test/
|
18
|
+
├── lib/
|
19
|
+
└── Gemfile
|
20
|
+
```
|
21
|
+
|
22
|
+
This keeps your benchmarks separate from your regular tests and makes them easy to find and run.
|
23
|
+
|
24
|
+
### Basic Measurement
|
25
|
+
|
26
|
+
The simplest way to measure code performance is using the `measure` method:
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
# benchmark/array_performance.rb
|
30
|
+
require "sus/fixtures/benchmark"
|
31
|
+
|
32
|
+
describe "Array Performance" do
|
33
|
+
include Sus::Fixtures::Benchmark
|
34
|
+
|
35
|
+
let(:data) {(1..1000).to_a}
|
36
|
+
|
37
|
+
measure "array iteration" do |repeats|
|
38
|
+
repeats.times do
|
39
|
+
data.each{|i| i * 2}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
```
|
44
|
+
|
45
|
+
This will automatically run your code until statistical convergence is reached and report the results.
|
46
|
+
|
47
|
+
**Run with:** `sus --verbose benchmark/array_performance.rb`
|
48
|
+
|
49
|
+
### Understanding the Output
|
50
|
+
|
51
|
+
When you run your tests with `sus --verbose`, you'll see output like:
|
52
|
+
|
53
|
+
```
|
54
|
+
measure array iteration 34 samples, mean: 0.15ms, standard deviation: 0.02ms, standard error: 0.003ms
|
55
|
+
```
|
56
|
+
|
57
|
+
**Important:** You must run `sus --verbose` to see the benchmark output. Without the verbose flag, the benchmark results will not be displayed.
|
58
|
+
|
59
|
+
This tells you:
|
60
|
+
- **34 samples**: How many times the code was executed.
|
61
|
+
- **mean: 0.15ms**: Average execution time.
|
62
|
+
- **standard deviation: 0.02ms**: How much variation there was between runs.
|
63
|
+
- **standard error: 0.003ms**: How reliable the mean estimate is.
|
64
|
+
|
65
|
+
## Measurement Modes
|
66
|
+
|
67
|
+
### Automatic Convergence (Default)
|
68
|
+
|
69
|
+
By default, the benchmark runs until it achieves statistical convergence:
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
measure "database query" do |repeats|
|
73
|
+
repeats.times do
|
74
|
+
User.where(active: true).count
|
75
|
+
end
|
76
|
+
end
|
77
|
+
```
|
78
|
+
|
79
|
+
The system will automatically determine when it has enough samples to provide a reliable measurement based on:
|
80
|
+
- **Confidence level**: 95% by default
|
81
|
+
- **Margin of error**: 2% of the mean by default (0.02)
|
82
|
+
- **Minimum samples**: 8 by default
|
83
|
+
|
84
|
+
### Fixed Number of Iterations
|
85
|
+
|
86
|
+
If you need a specific number of iterations, use the `exactly` method:
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
measure "quick test" do |repeats|
|
90
|
+
repeats.exactly(10).times do
|
91
|
+
# Your code here
|
92
|
+
end
|
93
|
+
end
|
94
|
+
```
|
95
|
+
|
96
|
+
This is useful for:
|
97
|
+
- Quick smoke tests.
|
98
|
+
- When you know the exact number of iterations needed.
|
99
|
+
- Debugging or development scenarios.
|
100
|
+
|
101
|
+
## Configuration Options
|
102
|
+
|
103
|
+
### Customizing the Sampler
|
104
|
+
|
105
|
+
You can customize the statistical parameters directly in the `measure` method:
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
measure "high precision", minimum: 20, confidence: 0.98, margin_of_error: 0.01 do |repeats|
|
109
|
+
repeats.times do
|
110
|
+
# Your code here
|
111
|
+
end
|
112
|
+
end
|
113
|
+
```
|
114
|
+
|
115
|
+
### Parameter Reference
|
116
|
+
|
117
|
+
You should try to avoid deviating from the defaults.
|
118
|
+
|
119
|
+
- **`minimum`** (Integer): Minimum samples before convergence (default: 8).
|
120
|
+
- **Lower `minimum`**: Quick development feedback (e.g., `minimum: 3`)
|
121
|
+
- **Higher `minimum`**: High-variance operations (e.g., `minimum: 30`)
|
122
|
+
- **`confidence`** (Float): Confidence level 0.0-1.0 (default: 0.95). High confidence (above 0.98) may take an extremely long time to converge.
|
123
|
+
- **Lower `confidence`**: Faster results (e.g., `confidence: 0.90`)
|
124
|
+
- **Higher `confidence`**: Production guarantees (e.g., `confidence: 0.98`)
|
125
|
+
- **`margin_of_error`** (Float): Acceptable error as a fraction of the mean (default: 0.02 = 2%).
|
126
|
+
- **Lower `margin_of_error`**: High precision (e.g., `margin_of_error: 0.01`)
|
127
|
+
- **Higher `margin_of_error`**: Rough estimates (e.g., `margin_of_error: 0.05`)
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
#
|
4
3
|
# Released under the MIT License.
|
5
4
|
# Copyright, 2025, by Samuel Williams.
|
6
5
|
|
@@ -12,27 +11,71 @@ module Sus
|
|
12
11
|
# Represents a benchmarking helper that executes a block multiple times, collecting timing samples until statistical convergence is reached.
|
13
12
|
class Repeats
|
14
13
|
# Initializes a new {Repeats} object with a sampler to collect timing data.
|
15
|
-
# @parameter
|
16
|
-
def initialize(
|
17
|
-
@
|
14
|
+
# @parameter sampler [Sampler] The sampler object to collect timing data.
|
15
|
+
def initialize(sampler)
|
16
|
+
@sampler = sampler
|
18
17
|
end
|
19
18
|
|
19
|
+
# @attribute [Sampler] The sampler that collects timing data.
|
20
|
+
attr :sampler
|
21
|
+
|
20
22
|
# Samples the execution time of the given block and adds the result to the sampler.
|
21
23
|
# @parameter block [Proc] The block to benchmark.
|
22
24
|
private def sample!(block)
|
23
25
|
time = Benchmark::Time.measure do
|
24
26
|
block.call
|
25
27
|
end
|
26
|
-
@
|
28
|
+
@sampler.add(time.real)
|
27
29
|
end
|
28
30
|
|
29
31
|
# Repeatedly executes the block until the sampler reports convergence.
|
30
32
|
# @parameter block [Proc] The block to benchmark.
|
31
33
|
def times(&block)
|
32
|
-
until @
|
34
|
+
until @sampler.converged?
|
33
35
|
sample!(block)
|
34
36
|
end
|
35
37
|
end
|
38
|
+
|
39
|
+
# Represents a benchmarking helper that executes a block a fixed number of times.
|
40
|
+
class Exactly
|
41
|
+
# Initializes a new {Exactly} object with a sampler and a fixed count.
|
42
|
+
# @parameter sampler [Sampler] The sampler object to collect timing data.
|
43
|
+
# @parameter count [Integer] The exact number of times to execute the block.
|
44
|
+
def initialize(sampler, count)
|
45
|
+
@sampler = sampler
|
46
|
+
@count = count
|
47
|
+
end
|
48
|
+
|
49
|
+
# @attribute [Sampler] The sampler that collects timing data.
|
50
|
+
attr :sampler
|
51
|
+
|
52
|
+
# @attribute [Integer] The number of times to execute the block.
|
53
|
+
attr :count
|
54
|
+
|
55
|
+
# Samples the execution time of the given block and adds the result to the sampler.
|
56
|
+
# @parameter block [Proc] The block to benchmark.
|
57
|
+
private def sample!(block)
|
58
|
+
time = Benchmark::Time.measure do
|
59
|
+
block.call
|
60
|
+
end
|
61
|
+
@sampler.add(time.real)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Executes the block exactly the specified number of times.
|
65
|
+
# @parameter block [Proc] The block to benchmark.
|
66
|
+
def times(&block)
|
67
|
+
@count.times do
|
68
|
+
sample!(block)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Sets a fixed number of times to execute the block, returning a new {Exactly} instance.
|
74
|
+
# @parameter count [Integer] The exact number of times to execute the block.
|
75
|
+
# @returns [Exactly] A new instance that will execute the block exactly the specified number of times.
|
76
|
+
def exactly(count)
|
77
|
+
Exactly.new(@sampler, count)
|
78
|
+
end
|
36
79
|
end
|
37
80
|
end
|
38
81
|
end
|
@@ -26,15 +26,19 @@ module Sus
|
|
26
26
|
# @parameter parent [Class] The parent test context class.
|
27
27
|
# @parameter description [String] The description of the measure.
|
28
28
|
# @parameter unique [Boolean] Whether the measure should have a unique identity.
|
29
|
+
# @parameter **options [Hash] Options to pass to the Sampler constructor.
|
29
30
|
# @parameter block [Proc] The block to execute for the measure.
|
30
31
|
# @returns [Class] The new measure class.
|
31
|
-
def self.build(parent, description, unique: true, &block)
|
32
|
+
def self.build(parent, description, unique: true, **options, &block)
|
32
33
|
base = Class.new(parent)
|
33
34
|
base.extend(self)
|
34
35
|
base.description = description
|
35
36
|
base.identity = Identity.nested(parent.identity, base.description, unique: unique)
|
36
37
|
base.set_temporary_name("#{self}[#{description}]")
|
37
38
|
|
39
|
+
# Store sampler options for later use
|
40
|
+
base.define_singleton_method(:sampler_options) {options}
|
41
|
+
|
38
42
|
if block_given?
|
39
43
|
base.define_method(:run, &block)
|
40
44
|
end
|
@@ -67,7 +71,8 @@ module Sus
|
|
67
71
|
assertions.nested(self, identity: self.identity, isolated: true, measure: true) do |assertions|
|
68
72
|
instance = self.new(assertions)
|
69
73
|
|
70
|
-
|
74
|
+
# Create sampler with options
|
75
|
+
samples = Sampler.new(**self.sampler_options)
|
71
76
|
repeats = Repeats.new(samples)
|
72
77
|
|
73
78
|
instance.around do
|
data/readme.md
CHANGED
@@ -12,11 +12,19 @@ bundle add sus-fixtures-benchmark
|
|
12
12
|
|
13
13
|
## Usage
|
14
14
|
|
15
|
-
Please see the [project documentation](https://
|
15
|
+
Please see the [project documentation](https://socketry.github.io/sus-fixtures-benchmark/) for more details.
|
16
16
|
|
17
17
|
## Releases
|
18
18
|
|
19
|
-
Please see the [project releases](https://
|
19
|
+
Please see the [project releases](https://socketry.github.io/sus-fixtures-benchmark/releases/index) for all releases.
|
20
|
+
|
21
|
+
### v0.2.1
|
22
|
+
|
23
|
+
- Fix links and add context.
|
24
|
+
|
25
|
+
### v0.2.0
|
26
|
+
|
27
|
+
- Added `exactly(count)` method to `Sus::Fixtures::Benchmark::Repeats` which returns an `Exactly` instance for fixed-count benchmarking.
|
20
28
|
|
21
29
|
### v0.1.0
|
22
30
|
|
data/releases.md
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
# Releases
|
2
2
|
|
3
|
+
## v0.2.1
|
4
|
+
|
5
|
+
- Fix links and add context.
|
6
|
+
|
7
|
+
## v0.2.0
|
8
|
+
|
9
|
+
- Added `exactly(count)` method to `Sus::Fixtures::Benchmark::Repeats` which returns an `Exactly` instance for fixed-count benchmarking.
|
10
|
+
|
3
11
|
## v0.1.0
|
4
12
|
|
5
13
|
- Added `Sus::Fixtures::Benchmark::Repeats` which is not an integer, but an instance that allows the block to be executed multiple times until the benchmark converges.
|
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sus-fixtures-benchmark
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
@@ -57,6 +57,7 @@ extensions: []
|
|
57
57
|
extra_rdoc_files: []
|
58
58
|
files:
|
59
59
|
- agent.md
|
60
|
+
- context/getting-started.md
|
60
61
|
- lib/sus/fixtures/benchmark.rb
|
61
62
|
- lib/sus/fixtures/benchmark/repeats.rb
|
62
63
|
- lib/sus/fixtures/benchmark/sampler.rb
|
@@ -65,13 +66,13 @@ files:
|
|
65
66
|
- license.md
|
66
67
|
- readme.md
|
67
68
|
- releases.md
|
68
|
-
homepage: https://github.com/
|
69
|
+
homepage: https://github.com/socketry/sus-fixtures-benchmark
|
69
70
|
licenses:
|
70
71
|
- MIT
|
71
72
|
metadata:
|
72
|
-
documentation_uri: https://
|
73
|
+
documentation_uri: https://socketry.github.io/sus-fixtures-benchmark/
|
73
74
|
funding_uri: https://github.com/sponsors/ioquatix/
|
74
|
-
source_code_uri: https://github.com/
|
75
|
+
source_code_uri: https://github.com/socketry/sus-fixtures-benchmark.git
|
75
76
|
rdoc_options: []
|
76
77
|
require_paths:
|
77
78
|
- lib
|
metadata.gz.sig
CHANGED
Binary file
|