|
36 | 36 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
37 | 37 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
38 | 38 | # SOFTWARE. |
| 39 | +import os |
| 40 | +import subprocess |
| 41 | +import sys |
| 42 | +import tempfile |
| 43 | +from pathlib import Path |
39 | 44 |
|
| 45 | +# Use a C runner to spawn the subprocesses to avoid counting subprocess module overhead into the benchmark |
| 46 | +RUNNER_CODE = ''' |
| 47 | +#include <stdio.h> |
| 48 | +#include <stdlib.h> |
| 49 | +#include <unistd.h> |
| 50 | +#include <sys/wait.h> |
40 | 51 |
|
41 | | -import subprocess, sys |
| 52 | +int main(int argc, char *argv[]) { |
| 53 | + if (argc < 3) { |
| 54 | + return 1; |
| 55 | + } |
| 56 | + int n = atoi(argv[1]); |
| 57 | + if (n <= 0) { |
| 58 | + return 1; |
| 59 | + } |
| 60 | + char **cmd_argv = &argv[2]; |
| 61 | + for (int i = 0; i < n; ++i) { |
| 62 | + pid_t pid = fork(); |
| 63 | + if (pid < 0) { |
| 64 | + perror("fork"); |
| 65 | + return 1; |
| 66 | + } else if (pid == 0) { |
| 67 | + execvp(cmd_argv[0], cmd_argv); |
| 68 | + perror("execvp"); |
| 69 | + exit(127); // If exec fails |
| 70 | + } else { |
| 71 | + int status; |
| 72 | + if (waitpid(pid, &status, 0) < 0) { |
| 73 | + perror("waitpid"); |
| 74 | + return 1; |
| 75 | + } |
| 76 | + } |
| 77 | + } |
| 78 | + return 0; |
| 79 | +} |
| 80 | +''' |
| 81 | + |
| 82 | +TMPDIR = tempfile.TemporaryDirectory() |
| 83 | +RUNNER_EXE = None |
| 84 | +ORIG_ARGV = None |
| 85 | + |
| 86 | + |
| 87 | +def __setup__(*args): |
| 88 | + global RUNNER_EXE |
| 89 | + tmpdir = Path(TMPDIR.name) |
| 90 | + runner_c = tmpdir / 'runner.c' |
| 91 | + runner_c.write_text(RUNNER_CODE) |
| 92 | + RUNNER_EXE = tmpdir / 'runner' |
| 93 | + subprocess.check_call([os.environ.get('CC', 'gcc'), runner_c, '-O2', '-o', RUNNER_EXE]) |
| 94 | + |
| 95 | + global ORIG_ARGV |
| 96 | + ORIG_ARGV = sys.orig_argv |
| 97 | + for i, arg in enumerate(ORIG_ARGV): |
| 98 | + if arg.endswith('.py'): |
| 99 | + ORIG_ARGV = ORIG_ARGV[:i] |
| 100 | + break |
| 101 | + |
| 102 | + |
| 103 | +def __teardown__(): |
| 104 | + TMPDIR.cleanup() |
42 | 105 |
|
43 | 106 |
|
44 | 107 | def __benchmark__(num=1000000): |
45 | | - while num > 0: |
46 | | - num -= 1 |
47 | | - orig_vm_argv = sys.orig_argv |
48 | | - for i, arg in enumerate(orig_vm_argv): |
49 | | - if arg.endswith('.py'): |
50 | | - orig_vm_argv = orig_vm_argv[:i] |
51 | | - break |
52 | | - subprocess.check_call([ |
53 | | - *orig_vm_argv, |
54 | | - "-I", # isolate from environment |
55 | | - "-S", # do not import site |
56 | | - "-Wignore", # ignore all warnings |
57 | | - "-B", # do not attempt to write pyc files |
58 | | - "-u", # do not add buffering wrappers around output streams |
59 | | - "-c", |
60 | | - "1" |
61 | | - ]) |
| 108 | + subprocess.check_call([ |
| 109 | + str(RUNNER_EXE), |
| 110 | + str(num), |
| 111 | + *ORIG_ARGV, |
| 112 | + "-I", # isolate from environment |
| 113 | + "-S", # do not import site |
| 114 | + "-Wignore", # ignore all warnings |
| 115 | + "-B", # do not attempt to write pyc files |
| 116 | + "-u", # do not add buffering wrappers around output streams |
| 117 | + "-c", |
| 118 | + "1" |
| 119 | + ]) |
0 commit comments