vernier 0.6.0 → 0.7.0
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
- data/Gemfile +1 -0
- data/README.md +20 -4
- data/examples/gvl_sleep.rb +62 -0
- data/examples/measure_overhead.rb +39 -0
- data/ext/vernier/vernier.cc +366 -147
- data/lib/vernier/collector.rb +12 -1
- data/lib/vernier/middleware.rb +32 -0
- data/lib/vernier/output/firefox.rb +26 -20
- data/lib/vernier/result.rb +12 -1
- data/lib/vernier/stack_table.rb +42 -0
- data/lib/vernier/version.rb +1 -1
- data/lib/vernier.rb +29 -16
- data/vernier.gemspec +1 -0
- metadata +21 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4988794520691870cb34178385be791e28fa4806eb1f6934b0d0abf52c8a070c
|
4
|
+
data.tar.gz: b1e63c6f5398f61fb68d634f17f33dc6aa7461344cb8acf446dd75ca52395818
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '082561b2a381f88c540e607d1dd9c0d42d841a0de82fc61471fb2d67754ee14a4fd1e87fa71896b586077664e61fd1db623fd250cd13064d198c9dc91d3f13ed'
|
7
|
+
data.tar.gz: a28c197d405e4f52d999db3ae73187047886e1fcfb7d353d13f26ad5ba4b61f0675daf617119b37d72ad3760bd0dffcae234f2dcb0a0e960a60046dfb87dd784
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -30,17 +30,33 @@ gem 'vernier'
|
|
30
30
|
|
31
31
|
## Usage
|
32
32
|
|
33
|
+
The output can be viewed in the web app at https://vernier.prof or locally using the [`profile-viewer` gem](https://github.com/tenderlove/profiler/tree/ruby) (both are lightly customized versions of the firefox profiler frontend, which profiles are also compatible with).
|
34
|
+
|
35
|
+
- **Flame Graph**: Shows proportionally how much time is spent within particular stack frames. Frames are grouped together, which means that x-axis / left-to-right order is not meaningful.
|
36
|
+
- **Stack Chart**: Shows the stack at each sample with the x-axis representing time and can be read left-to-right.
|
37
|
+
|
33
38
|
|
34
39
|
### Time
|
35
40
|
|
41
|
+
|
42
|
+
#### Command line
|
43
|
+
|
44
|
+
The easiest way to record a program or script is via the CLI
|
45
|
+
|
36
46
|
```
|
37
|
-
|
47
|
+
$ vernier run -- ruby -e 'sleep 1'
|
48
|
+
starting profiler with interval 500
|
49
|
+
#<Vernier::Result 1.001589 seconds, 1 threads, 2002 samples, 1 unique>
|
50
|
+
written to /tmp/profile20240328-82441-gkzffc.vernier.json
|
38
51
|
```
|
39
52
|
|
40
|
-
|
53
|
+
### Block of code
|
41
54
|
|
42
|
-
|
43
|
-
|
55
|
+
``` ruby
|
56
|
+
Vernier.run(out: "time_profile.json") do
|
57
|
+
some_slow_method
|
58
|
+
end
|
59
|
+
```
|
44
60
|
|
45
61
|
### Retained memory
|
46
62
|
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# Different (bad) ways to sleep
|
2
|
+
|
3
|
+
File.write("#{__dir__}/my_sleep.c", <<~EOF)
|
4
|
+
#include <time.h>
|
5
|
+
#include <sys/errno.h>
|
6
|
+
|
7
|
+
void my_sleep() {
|
8
|
+
struct timespec ts;
|
9
|
+
ts.tv_sec = 1;
|
10
|
+
ts.tv_nsec = 0;
|
11
|
+
|
12
|
+
int rc;
|
13
|
+
do {
|
14
|
+
rc = nanosleep(&ts, &ts);
|
15
|
+
} while (rc < 0 && errno == EINTR);
|
16
|
+
}
|
17
|
+
EOF
|
18
|
+
|
19
|
+
soext = RbConfig::CONFIG["SOEXT"]
|
20
|
+
system("gcc", "-shared", "-fPIC", "#{__dir__}/my_sleep.c", "-o", "#{__dir__}/my_sleep.#{soext}")
|
21
|
+
|
22
|
+
require "fiddle"
|
23
|
+
|
24
|
+
SLEEP_LIB = Fiddle.dlopen("./my_sleep.#{soext}")
|
25
|
+
|
26
|
+
def cfunc_sleep_gvl
|
27
|
+
Fiddle::Function.new(SLEEP_LIB['my_sleep'], [], Fiddle::TYPE_VOID, need_gvl: true).call
|
28
|
+
end
|
29
|
+
|
30
|
+
def cfunc_sleep_idle
|
31
|
+
Fiddle::Function.new(SLEEP_LIB['my_sleep'], [], Fiddle::TYPE_VOID, need_gvl: true).call
|
32
|
+
end
|
33
|
+
|
34
|
+
def ruby_sleep_gvl
|
35
|
+
target = Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond) + 1000
|
36
|
+
while Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond) < target
|
37
|
+
i = 0
|
38
|
+
while i < 1000
|
39
|
+
i += 1
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def sleep_idle
|
45
|
+
sleep 1
|
46
|
+
end
|
47
|
+
|
48
|
+
def run(name)
|
49
|
+
STDOUT.print "#{name}..."
|
50
|
+
STDOUT.flush
|
51
|
+
|
52
|
+
before = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
53
|
+
send(name)
|
54
|
+
after = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
55
|
+
|
56
|
+
STDOUT.puts " %.2fs" % (after - before)
|
57
|
+
end
|
58
|
+
|
59
|
+
run(:cfunc_sleep_gvl)
|
60
|
+
run(:cfunc_sleep_idle)
|
61
|
+
run(:ruby_sleep_gvl)
|
62
|
+
run(:sleep_idle)
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require "benchmark/ips"
|
2
|
+
require "vernier"
|
3
|
+
|
4
|
+
def compare(**options, &block)
|
5
|
+
block.call
|
6
|
+
|
7
|
+
Benchmark.ips do |x|
|
8
|
+
x.report "no profiler" do |n|
|
9
|
+
n.times do
|
10
|
+
block.call
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
x.report "vernier" do |n|
|
15
|
+
Vernier.profile(**options) do
|
16
|
+
n.times do
|
17
|
+
block.call
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
x.compare!
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
compare do
|
27
|
+
i = 0
|
28
|
+
while i < 10_000
|
29
|
+
i += 1
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
compare(allocation_sample_rate: 1000) do
|
34
|
+
Object.new
|
35
|
+
end
|
36
|
+
|
37
|
+
compare(allocation_sample_rate: 1) do
|
38
|
+
Object.new
|
39
|
+
end
|