posix-spawn 0.3.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.
- 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
|
+

|
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
|
+

|
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
|