posix-spawn 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/COPYING +28 -0
- data/Gemfile +2 -0
- data/HACKING +26 -0
- data/README.md +228 -0
- data/Rakefile +36 -0
- data/TODO +23 -0
- data/bin/posix-spawn-benchmark +117 -0
- data/ext/extconf.rb +6 -0
- data/ext/posix-spawn.c +406 -0
- data/lib/posix-spawn.rb +1 -0
- data/lib/posix/spawn.rb +472 -0
- data/lib/posix/spawn/child.rb +212 -0
- data/lib/posix/spawn/version.rb +5 -0
- data/posix-spawn.gemspec +24 -0
- data/test/test_backtick.rb +36 -0
- data/test/test_child.rb +107 -0
- data/test/test_popen.rb +18 -0
- data/test/test_spawn.rb +360 -0
- data/test/test_system.rb +29 -0
- metadata +89 -0
data/.gitignore
ADDED
data/COPYING
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
Copyright (c) 2011 by Ryan Tomayko <r@tomayko.com>
|
2
|
+
and Aman Gupta <aman@tmm1.net>
|
3
|
+
|
4
|
+
Permission is hereby granted, free of charge, to any person ob-
|
5
|
+
taining a copy of this software and associated documentation
|
6
|
+
files (the "Software"), to deal in the Software without restric-
|
7
|
+
tion, including without limitation the rights to use, copy, modi-
|
8
|
+
fy, merge, publish, distribute, sublicense, and/or sell copies of
|
9
|
+
the Software, and to permit persons to whom the Software is fur-
|
10
|
+
nished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
17
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONIN-
|
18
|
+
FRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
20
|
+
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
21
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
22
|
+
SOFTWARE.
|
23
|
+
|
24
|
+
A small portion of the environ dup'ing code in ext/posix-spawn.c
|
25
|
+
was taken from glibc <http://www.gnu.org/s/libc/> and is maybe
|
26
|
+
Copyright (c) 2011 by The Free Software Foundation or maybe
|
27
|
+
by others mentioned in the glibc LICENSES file. glibc is
|
28
|
+
distributed under the terms of the LGPL license.
|
data/Gemfile
ADDED
data/HACKING
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
Clone the project:
|
2
|
+
|
3
|
+
git clone http://github.com/rtomayko/posix-spawn.git
|
4
|
+
cd posix-spawn
|
5
|
+
bundle install
|
6
|
+
|
7
|
+
Rake tasks can be run without further setup:
|
8
|
+
|
9
|
+
rake build
|
10
|
+
rake test
|
11
|
+
rake benchmark
|
12
|
+
|
13
|
+
Just `rake' builds the extension and runs the tests.
|
14
|
+
|
15
|
+
If you want to run the benchmark scripts or tests directly out of a
|
16
|
+
working copy, first setup your PATH and RUBYLIB environment:
|
17
|
+
|
18
|
+
PATH="$(pwd)/bin:$PATH"
|
19
|
+
RUBYLIB="$(pwd)/lib:$(pwd)/ext:$RUBYLIB"
|
20
|
+
export RUBYLIB
|
21
|
+
|
22
|
+
Or, use the following rbdev script to quickly setup your PATH and
|
23
|
+
RUBYLIB environment for this and other projects adhering to the
|
24
|
+
Ruby Packaging Standard:
|
25
|
+
|
26
|
+
https://github.com/rtomayko/dotfiles/blob/rtomayko/bin/rbdev
|
data/README.md
ADDED
@@ -0,0 +1,228 @@
|
|
1
|
+
# posix-spawn
|
2
|
+
|
3
|
+
`fork(2)` calls slow down as the parent process uses more memory due to the need
|
4
|
+
to copy page tables. In many common uses of fork(), where it is followed by one
|
5
|
+
of the exec family of functions to spawn child processes (`Kernel#system`,
|
6
|
+
`IO::popen`, `Process::spawn`, etc.), it's possible to remove this overhead by using
|
7
|
+
the use of special process spawning interfaces (`posix_spawn()`, `vfork()`, etc.)
|
8
|
+
|
9
|
+
The posix-spawn library aims to implement a subset of the Ruby 1.9 `Process::spawn`
|
10
|
+
interface in a way that takes advantage of fast process spawning interfaces when
|
11
|
+
available and provides sane fallbacks on systems that do not.
|
12
|
+
|
13
|
+
### FEATURES
|
14
|
+
|
15
|
+
- Fast, constant-time spawn times across a variety of platforms.
|
16
|
+
- A largish compatible subset of Ruby 1.9's `Process::spawn` interface and
|
17
|
+
enhanced versions of `Kernel#system`, <code>Kernel#`</code>, etc. under
|
18
|
+
Ruby >= 1.8.7 (currently MRI only).
|
19
|
+
- High level `POSIX::Spawn::Child` class for quick (but correct!)
|
20
|
+
non-streaming IPC scenarios.
|
21
|
+
|
22
|
+
## BENCHMARKS
|
23
|
+
|
24
|
+
The following benchmarks illustrate time needed to fork/exec a child process at
|
25
|
+
increasing resident memory sizes on Linux 2.6 and MacOS X. Tests were run using
|
26
|
+
the [`posix-spawn-benchmark`][pb] program included with the package.
|
27
|
+
|
28
|
+
[pb]: https://github.com/rtomayko/posix-spawn/tree/master/bin
|
29
|
+
|
30
|
+
### Linux
|
31
|
+
|
32
|
+
![](https://chart.googleapis.com/chart?chbh=a,5,25&chxr=1,0,36,7&chd=t:5.77,10.37,15.72,18.31,19.73,25.13,26.70,29.31,31.44,35.49|0.86,0.82,1.06,0.99,0.79,1.06,0.84,0.79,0.93,0.94&chxs=1N**%20secs&chs=900x200&chds=0,36&chxl=0:|50%20MB|100%20MB|150%20MB|200%20MB|250%20MB|300%20MB|350%20MB|400%20MB|450%20MB|500%20MB&cht=bvg&chdl=fspawn%20%28fork%2Bexec%29|pspawn%20%28posix_spawn%29&chtt=posix-spawn-benchmark%20--graph%20--count%20500%20--mem-size%20500%20%28x86_64-linux%29&chco=1f77b4,ff7f0e&chf=bg,s,f8f8f8&chxt=x,y#.png)
|
33
|
+
|
34
|
+
`posix_spawn` is faster than `fork+exec`, and executes in constant time when
|
35
|
+
used with `POSIX_SPAWN_USEVFORK`.
|
36
|
+
|
37
|
+
`fork+exec` is extremely slow for large parent processes.
|
38
|
+
|
39
|
+
### OSX
|
40
|
+
|
41
|
+
![](https://chart.googleapis.com/chart?chxl=0:|50%20MB|100%20MB|150%20MB|200%20MB|250%20MB|300%20MB|350%20MB|400%20MB|450%20MB|500%20MB&cht=bvg&chdl=fspawn%20%28fork%2Bexec%29|pspawn%20%28posix_spawn%29&chtt=posix-spawn-benchmark%20--graph%20--count%20500%20--mem-size%20500%20%28i686-darwin10.5.0%29&chco=1f77b4,ff7f0e&chf=bg,s,f8f8f8&chxt=x,y&chbh=a,5,25&chxr=1,0,3,0&chd=t:1.95,2.07,2.56,2.29,2.21,2.32,2.15,2.25,1.96,2.02|0.84,0.97,0.89,0.82,1.13,0.89,0.93,0.81,0.83,0.81&chxs=1N**%20secs&chs=900x200&chds=0,3#.png)
|
42
|
+
|
43
|
+
`posix_spawn` is faster than `fork+exec`, but neither is affected by the size of
|
44
|
+
the parent process.
|
45
|
+
|
46
|
+
## USAGE
|
47
|
+
|
48
|
+
This library includes two distinct interfaces: `POSIX::Spawn::spawn`, a lower
|
49
|
+
level process spawning interface based on the new Ruby 1.9 `Process::spawn`
|
50
|
+
method, and `POSIX::Spawn::Child`, a higher level class geared toward easy
|
51
|
+
spawning of processes with simple string based standard input/output/error
|
52
|
+
stream handling. The former is much more versatile, the latter requires much
|
53
|
+
less code for certain common scenarios.
|
54
|
+
|
55
|
+
### POSIX::Spawn::spawn
|
56
|
+
|
57
|
+
The `POSIX::Spawn` module (with help from the accompanying C extension)
|
58
|
+
implements a subset of the [Ruby 1.9 Process::spawn][ps] interface, largely
|
59
|
+
through the use of the [IEEE Std 1003.1 `posix_spawn(2)` systems interfaces][po].
|
60
|
+
These are widely supported by various UNIX operating systems.
|
61
|
+
|
62
|
+
[ps]: http://www.ruby-doc.org/core-1.9/classes/Process.html#M002230
|
63
|
+
[po]: http://pubs.opengroup.org/onlinepubs/009695399/functions/posix_spawn.html
|
64
|
+
|
65
|
+
In its simplest form, the `POSIX::Spawn::spawn` method can be used to execute a
|
66
|
+
child process similar to `Kernel#system`:
|
67
|
+
|
68
|
+
require 'posix/spawn'
|
69
|
+
pid = POSIX::Spawn::spawn('echo', 'hello world')
|
70
|
+
stat = Process::waitpid(pid)
|
71
|
+
|
72
|
+
The first line executes `echo` with a single argument and immediately returns
|
73
|
+
the new process's `pid`. The second line waits for the process to complete and
|
74
|
+
returns a `Process::Status` object. Note that `spawn` *does not* wait for the
|
75
|
+
process to finish execution like `system` and does not reap the child's exit
|
76
|
+
status -- you must call `Process::waitpid` (or equivalent) or the process will
|
77
|
+
become a zombie.
|
78
|
+
|
79
|
+
The `spawn` method is capable of performing a large number of additional
|
80
|
+
operations, from setting up the new process's environment, to changing the
|
81
|
+
child's working directory, to redirecting arbitrary file descriptors.
|
82
|
+
|
83
|
+
See the Ruby 1.9 [`Process::spawn` documentation][ps] for details and the
|
84
|
+
`STATUS` section below for a full account of the various `Process::spawn`
|
85
|
+
features supported by `POSIX::Spawn::spawn`.
|
86
|
+
|
87
|
+
### `system`, `popen4`, and <code>`</code>
|
88
|
+
|
89
|
+
In addition to the `spawn` method, Ruby 1.9 compatible implementations of
|
90
|
+
`Kernel#system` and <code>Kernel#\`</code> are provided in the `POSIX::Spawn`
|
91
|
+
module. The `popen4` method can be used to spawn a process with redirected
|
92
|
+
stdin, stdout, and stderr objects.
|
93
|
+
|
94
|
+
### POSIX::Spawn as a Mixin
|
95
|
+
|
96
|
+
The `POSIX::Spawn` module can also be mixed in to classes and modules to include
|
97
|
+
`spawn` and all utility methods in that namespace:
|
98
|
+
|
99
|
+
require 'posix/spawn'
|
100
|
+
|
101
|
+
class YourGreatClass
|
102
|
+
include POSIX::Spawn
|
103
|
+
|
104
|
+
def speak(message)
|
105
|
+
pid = spawn('echo', message)
|
106
|
+
Process::waitpid(pid)
|
107
|
+
end
|
108
|
+
|
109
|
+
def calculate(expression)
|
110
|
+
pid, in, out, err = popen4('bc')
|
111
|
+
in.write(expression)
|
112
|
+
in.close
|
113
|
+
out.read
|
114
|
+
ensure
|
115
|
+
[in, out, err].each { |io| io.close if !io.closed? }
|
116
|
+
Process::waitpid(pid)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
### POSIX::Spawn::Child
|
121
|
+
|
122
|
+
The `POSIX::Spawn::Child` class includes logic for executing child processes and
|
123
|
+
reading/writing from their standard input, output, and error streams. It's
|
124
|
+
designed to take all input in a single string and provides all output as single
|
125
|
+
strings and is therefore not well-suited to streaming large quantities of data
|
126
|
+
in and out of commands. That said, it has some benefits:
|
127
|
+
|
128
|
+
- **Simple** - requires little code for simple stream input and capture.
|
129
|
+
- **Internally non-blocking** (using `select(2)`) - handles all pipe hang cases
|
130
|
+
due to exceeding `PIPE_BUF` limits on one or more streams.
|
131
|
+
- **Potentially portable** - abstracts lower-level process and stream
|
132
|
+
management APIs so the class can be made to work on platforms like Java and
|
133
|
+
Windows where UNIX process spawning and stream APIs are not supported.
|
134
|
+
|
135
|
+
`POSIX::Spawn::Child` takes the standard `spawn` arguments when instantiated,
|
136
|
+
and runs the process to completion after writing all input and reading all
|
137
|
+
output:
|
138
|
+
|
139
|
+
>> require 'posix/spawn'
|
140
|
+
>> child = POSIX::Spawn::Child.new('git', '--help')
|
141
|
+
|
142
|
+
Retrieve process output written to stdout / stderr, or inspect the process's
|
143
|
+
exit status:
|
144
|
+
|
145
|
+
>> child.out
|
146
|
+
=> "usage: git [--version] [--exec-path[=GIT_EXEC_PATH]]\n ..."
|
147
|
+
>> child.err
|
148
|
+
=> ""
|
149
|
+
>> child.status
|
150
|
+
=> #<Process::Status: pid=80718,exited(0)>
|
151
|
+
|
152
|
+
Use the `:input` option to write data on the new process's stdin immediately
|
153
|
+
after spawning:
|
154
|
+
|
155
|
+
>> child = POSIX::Spawn::Child.new('bc', :input => '40 + 2')
|
156
|
+
>> child.out
|
157
|
+
"42\n"
|
158
|
+
|
159
|
+
Additional options can be used to specify the maximum output size and time of
|
160
|
+
execution before the child process is aborted. See the `POSIX::Spawn::Child`
|
161
|
+
docs for more info.
|
162
|
+
|
163
|
+
## STATUS
|
164
|
+
|
165
|
+
The `POSIX::Spawn::spawn` method is designed to be as compatible with Ruby 1.9's
|
166
|
+
`Process::spawn` as possible. Right now, it is a compatible subset.
|
167
|
+
|
168
|
+
These `Process::spawn` arguments are currently supported to any of
|
169
|
+
`Spawn::spawn`, `Spawn::system`, `Spawn::popen4`, and `Spawn::Child.new`:
|
170
|
+
|
171
|
+
env: hash
|
172
|
+
name => val : set the environment variable
|
173
|
+
name => nil : unset the environment variable
|
174
|
+
command...:
|
175
|
+
commandline : command line string which is passed to a shell
|
176
|
+
cmdname, arg1, ... : command name and one or more arguments (no shell)
|
177
|
+
[cmdname, argv0], arg1, ... : command name, argv[0] and zero or more arguments (no shell)
|
178
|
+
options: hash
|
179
|
+
clearing environment variables:
|
180
|
+
:unsetenv_others => true : clear environment variables except specified by env
|
181
|
+
:unsetenv_others => false : don't clear (default)
|
182
|
+
redirection:
|
183
|
+
key:
|
184
|
+
FD : single file descriptor in child process
|
185
|
+
[FD, FD, ...] : multiple file descriptor in child process
|
186
|
+
value:
|
187
|
+
FD : redirect to the file descriptor in parent process
|
188
|
+
:close : close the file descriptor in child process
|
189
|
+
string : redirect to file with open(string, "r" or "w")
|
190
|
+
[string] : redirect to file with open(string, File::RDONLY)
|
191
|
+
[string, open_mode] : redirect to file with open(string, open_mode, 0644)
|
192
|
+
[string, open_mode, perm] : redirect to file with open(string, open_mode, perm)
|
193
|
+
FD is one of follows
|
194
|
+
:in : the file descriptor 0 which is the standard input
|
195
|
+
:out : the file descriptor 1 which is the standard output
|
196
|
+
:err : the file descriptor 2 which is the standard error
|
197
|
+
integer : the file descriptor of specified the integer
|
198
|
+
io : the file descriptor specified as io.fileno
|
199
|
+
current directory:
|
200
|
+
:chdir => str
|
201
|
+
|
202
|
+
These options are currently NOT supported:
|
203
|
+
|
204
|
+
options: hash
|
205
|
+
process group:
|
206
|
+
:pgroup => true or 0 : make a new process group
|
207
|
+
:pgroup => pgid : join to specified process group
|
208
|
+
:pgroup => nil : don't change the process group (default)
|
209
|
+
resource limit: resourcename is core, cpu, data, etc. See Process.setrlimit.
|
210
|
+
:rlimit_resourcename => limit
|
211
|
+
:rlimit_resourcename => [cur_limit, max_limit]
|
212
|
+
umask:
|
213
|
+
:umask => int
|
214
|
+
redirection:
|
215
|
+
value:
|
216
|
+
[:child, FD] : redirect to the redirected file descriptor
|
217
|
+
file descriptor inheritance: close non-redirected non-standard fds (3, 4, 5, ...) or not
|
218
|
+
:close_others => false : inherit fds (default for system and exec)
|
219
|
+
:close_others => true : don't inherit (default for spawn and IO.popen)
|
220
|
+
|
221
|
+
## ACKNOWLEDGEMENTS
|
222
|
+
|
223
|
+
Copyright (c) by
|
224
|
+
[Ryan Tomayko](http://tomayko.com/about)
|
225
|
+
and
|
226
|
+
[Aman Gupta](https://github.com/tmm1).
|
227
|
+
|
228
|
+
See the `COPYING` file for more information on license and redistribution.
|
data/Rakefile
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
task :default => :test
|
2
|
+
|
3
|
+
# ==========================================================
|
4
|
+
# Packaging
|
5
|
+
# ==========================================================
|
6
|
+
|
7
|
+
GEMSPEC = eval(File.read('posix-spawn.gemspec'))
|
8
|
+
|
9
|
+
require 'rake/gempackagetask'
|
10
|
+
Rake::GemPackageTask.new(GEMSPEC) do |pkg|
|
11
|
+
end
|
12
|
+
|
13
|
+
# ==========================================================
|
14
|
+
# Ruby Extension
|
15
|
+
# ==========================================================
|
16
|
+
|
17
|
+
require 'rake/extensiontask'
|
18
|
+
Rake::ExtensionTask.new('posix_spawn_ext', GEMSPEC) do |ext|
|
19
|
+
ext.ext_dir = 'ext'
|
20
|
+
end
|
21
|
+
task :build => :compile
|
22
|
+
|
23
|
+
# ==========================================================
|
24
|
+
# Testing
|
25
|
+
# ==========================================================
|
26
|
+
|
27
|
+
require 'rake/testtask'
|
28
|
+
Rake::TestTask.new 'test' do |t|
|
29
|
+
t.test_files = FileList['test/test_*.rb']
|
30
|
+
end
|
31
|
+
task :test => :build
|
32
|
+
|
33
|
+
desc 'Run some benchmarks'
|
34
|
+
task :benchmark => :build do
|
35
|
+
ruby '-Ilib', 'bin/posix-spawn-benchmark'
|
36
|
+
end
|
data/TODO
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
[x] license (LGPL)
|
2
|
+
[x] fucking name this thing
|
3
|
+
[x] fastspawn-bm should take iterations and memsize arguments
|
4
|
+
[x] high level Grit::Process like class (string based) (tmm1)
|
5
|
+
[x] add FD => '/path/to/file' (all variations) (rtomayko)
|
6
|
+
[x] raise exception on unhandled options
|
7
|
+
[x] benchmarks in README (tmm1)
|
8
|
+
[x] POSIX::Spawn::spawn usage examples in README
|
9
|
+
[x] POSIX::Spawn#pspawn should be just #spawn
|
10
|
+
[x] :err => :out case -- currently closing out after dup2'ing
|
11
|
+
[x] POSIX::Spawn::Process.new should have same method signature as Process::spawn
|
12
|
+
[x] POSIX::Spawn::Process renamed to POSIX::Spawn::Child
|
13
|
+
[x] Better POSIX::Spawn#spawn comment docs
|
14
|
+
[x] POSIX::Spawn::Child usage examples in README
|
15
|
+
|
16
|
+
|
17
|
+
[ ] popen* interfaces
|
18
|
+
[x] system interface
|
19
|
+
[x] ` interface
|
20
|
+
[ ] jruby Grit::Process stuff
|
21
|
+
[ ] make :vfork an option to Spawn#spawn
|
22
|
+
[ ] check all posix_spawn_* function call return values
|
23
|
+
[ ] POSIX::Spawn as ::Spawn? (maybe, we'll see)
|
@@ -0,0 +1,117 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#/ Usage: posix-spawn-benchmark [-n <count>] [-m <mem-size>]
|
3
|
+
#/ Run posix-spawn (Ruby extension) benchmarks and report to standard output.
|
4
|
+
#/
|
5
|
+
#/ Options:
|
6
|
+
#/ -n, --count=NUM total number of processes to spawn.
|
7
|
+
#/ -m, --mem-size=MB RES size to bloat to before performing benchmarks.
|
8
|
+
#/ -g, --graph benchmark at 10MB itervals up to RES and graph results.
|
9
|
+
#/
|
10
|
+
#/ Benchmarks run with -n 1000 -m 100 by default.
|
11
|
+
require 'optparse'
|
12
|
+
require 'posix-spawn'
|
13
|
+
require 'benchmark'
|
14
|
+
include Benchmark
|
15
|
+
|
16
|
+
allocate = 100 * (1024 ** 2)
|
17
|
+
iterations = 1_000
|
18
|
+
graphmode = false
|
19
|
+
ARGV.options do |o|
|
20
|
+
o.set_summary_indent(' ')
|
21
|
+
o.on("-n", "--count=num") { |val| iterations = val.to_i }
|
22
|
+
o.on("-m", "--mem-size=MB") { |val| allocate = val.to_i * (1024 ** 2) }
|
23
|
+
o.on("-g", "--graph") { graphmode = true }
|
24
|
+
o.on_tail("-h", "--help") { exec "grep ^#/ <'#{__FILE__}' |cut -c4-" }
|
25
|
+
o.parse!
|
26
|
+
end
|
27
|
+
|
28
|
+
if graphmode
|
29
|
+
bloat = []
|
30
|
+
data = {}
|
31
|
+
chunk = allocate / 10
|
32
|
+
max = 0
|
33
|
+
|
34
|
+
10.times do
|
35
|
+
puts "allocating #{chunk / (1024 ** 2)}MB (#{(bloat.size+1) * chunk / (1024 ** 2)}MB total)"
|
36
|
+
bloat << ('x' * chunk)
|
37
|
+
# size = bloat.size / (1024 ** 2)
|
38
|
+
|
39
|
+
%w[ fspawn pspawn ].each do |type|
|
40
|
+
print " - benchmarking #{type}... "
|
41
|
+
time = Benchmark.realtime do
|
42
|
+
iterations.times do
|
43
|
+
pid = POSIX::Spawn.send(type, 'true')
|
44
|
+
Process.wait(pid)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
puts "done (#{time})"
|
48
|
+
|
49
|
+
data[type] ||= []
|
50
|
+
data[type] << time
|
51
|
+
max = time if time > max
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
max = max < 0.5 ? (max * 10).round / 10.0 : max.ceil
|
56
|
+
minmb, maxmb = chunk/(1024**2), allocate/(1024**2)
|
57
|
+
series = %w[ fspawn pspawn ].map{|name| data[name].map{|d| "%.2f" % d }.join(',') }
|
58
|
+
|
59
|
+
chart = {
|
60
|
+
:chs => '900x200',
|
61
|
+
:cht => 'bvg', # grouped vertical bar chart
|
62
|
+
:chtt => "posix-spawn-benchmark --graph --count #{iterations} --mem-size #{maxmb} (#{RUBY_PLATFORM})",
|
63
|
+
|
64
|
+
:chf => 'bg,s,f8f8f8', # background
|
65
|
+
:chbh => 'a,5,25', # 25px between bar groups
|
66
|
+
|
67
|
+
:chd => "t:#{series.join('|')}", # data
|
68
|
+
:chds => "0,#{max}", # scale
|
69
|
+
:chdl => 'fspawn (fork+exec)|pspawn (posix_spawn)', # legend
|
70
|
+
:chco => '1f77b4,ff7f0e', # colors
|
71
|
+
|
72
|
+
:chxt => 'x,y',
|
73
|
+
:chxr => "1,0,#{max},#{max/5}", # y labels up to max time
|
74
|
+
:chxs => '1N** secs', # y labels are +=' secs'
|
75
|
+
:chxl => "0:|#{minmb.step(maxmb, maxmb/10).map{ |mb| "#{mb} MB"}.join('|')}", # x bucket labels
|
76
|
+
}
|
77
|
+
|
78
|
+
url = "https://chart.googleapis.com/chart?"
|
79
|
+
url += chart.map do |key, val|
|
80
|
+
"#{key}=#{val.gsub(' ','%20').gsub('(','%28').gsub(')','%29').gsub('+','%2B')}"
|
81
|
+
end.join('&')
|
82
|
+
url += '#.png'
|
83
|
+
|
84
|
+
puts url
|
85
|
+
|
86
|
+
exit!
|
87
|
+
end
|
88
|
+
|
89
|
+
puts "benchmarking fork/exec vs. posix_spawn over #{iterations} runs" +
|
90
|
+
" at #{allocate / (1024 ** 2)}M res"
|
91
|
+
|
92
|
+
# bloat the process
|
93
|
+
bloat = 'x' * allocate
|
94
|
+
|
95
|
+
# run the benchmarks
|
96
|
+
bm 40 do |x|
|
97
|
+
x.report("fspawn (fork/exec):") do
|
98
|
+
iterations.times do
|
99
|
+
pid = POSIX::Spawn.fspawn('true')
|
100
|
+
Process.wait(pid)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
x.report("pspawn (posix_spawn):") do
|
104
|
+
iterations.times do
|
105
|
+
pid = POSIX::Spawn.pspawn('true')
|
106
|
+
Process.wait(pid)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
if Process.respond_to?(:spawn)
|
110
|
+
x.report("spawn (native):") do
|
111
|
+
iterations.times do
|
112
|
+
pid = Process.spawn('true')
|
113
|
+
Process.wait(pid)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|