forked from graphql-crystal/benchmarks
-
Notifications
You must be signed in to change notification settings - Fork 0
/
run.cr
executable file
·138 lines (116 loc) · 3.97 KB
/
run.cr
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#!/usr/bin/crystal
require "socket"
require "http"
require "json"
require "yaml"
require "ecr"
benchmarks = Array(Benchmark).from_yaml ECR.render("benchmarks.yaml")
shards_mut = Mutex.new
ch = Channel(Nil).new
benchmarks.map do |b|
spawn do
dir = Path[Dir.current, b.id]
if File.exists? dir.join("shard.yml")
shards_mut.synchronize do
wait_p run("shards", ["install", "-q", "--frozen"], dir)
end
end
wait_p run("crystal", ["build", "--release", "-D", "preview_mt", "main.cr"], dir) if File.exists? dir.join("shard.yml")
wait_p run("npm", ["ci", "--silent"], dir) if File.exists? dir.join("package.json")
wait_p run("cargo", ["build", "--release", "--quiet"], dir) if File.exists? dir.join("Cargo.toml")
wait_p run("go", ["build", "-o", "main", "main.go"], dir) if File.exists? dir.join("go.mod")
wait_p run("pipenv", ["install"], dir) if File.exists? dir.join("Pipfile")
wait_p run("sbt", ["--warn", "compile", "assembly"], dir) if File.exists? dir.join("build.sbt")
wait_p run("bundle", ["install", "--quiet"], dir) if File.exists? dir.join("Gemfile")
wait_p run("mix", ["deps.get", "--only", "prod"], dir) if File.exists? dir.join("mix.exs")
wait_p run("mix", ["compile"], dir) if File.exists? dir.join("mix.exs")
wait_p run("nimble", ["--silent", "-y", "install"], dir) if File.exists? dir.join("main.nimble")
wait_p run("nimble", ["--silent", "-y", "build", "-d:release", "-d:chronicles_log_level=WARN"], dir) if File.exists? dir.join("main.nimble")
ch.send(nil)
rescue ex
puts ex.message
exit 1
end
end.each { |_| ch.receive }
benchmarks.each_with_index do |b, i|
if port_bound?
# a TERM signal doesn't always end all processes
# agoo takes many seconds to terminate, and python doesn't terminate since we started using `pipenv run`
# there may be a proper solution for each case, but for now we use fuser as a workaround
system("fuser -k 8000/tcp")
raise "port 8000 already in use" unless wait_unbound 60
puts "killed socket process"
end
puts "running #{b.id}..."
p = run(b.run.cmd, b.run.args, Path[Dir.current, b.id])
while !port_bound?
exit 1 if p.terminated?
sleep 1
end
res = `bombardier -c #{System.cpu_count * 50} -d 5s -m POST -b '{"query":"{ hello }"}' -H "Content-Type: application/json" -o json -p r http://localhost:8000/graphql`
exit 1 unless $?.success?
p.terminate
wait_p p
benchmarks[i].result = JSON.parse(res.split('\n').last)["result"]
end
benchmarks = benchmarks.sort do |a, b|
b.result.not_nil!.["rps"]["mean"].as_f <=> a.result.not_nil!.["rps"]["mean"].as_f
end
readme = ECR.render("README.ecr")
File.write "README.md", readme
puts readme
def run(cmd, args, dir)
env = {"CRYSTAL_WORKERS" => System.cpu_count.to_s, "MIX_ENV" => "prod", "MIX_QUIET" => "1", "PORT" => "8000"}
p = Process.new(cmd, env: env, args: args, input: Process::Redirect::Inherit, output: Process::Redirect::Inherit, error: Process::Redirect::Inherit, chdir: dir.to_s)
at_exit { p.terminate unless p.terminated? }
p
end
def wait_p(p)
r = p.wait
if r.exit_code != 0
puts "command failed with exit code #{r.exit_code}"
exit 1
end
end
def wait_unbound(time : Int32)
if port_bound?
if time > 0
sleep 1
wait_unbound time - 1
else
false
end
else
true
end
end
def port_bound?
s = TCPServer.new("127.0.0.1", 8000)
s.close
false
rescue Socket::BindError
true
end
class Benchmark
include YAML::Serializable
class Script
include YAML::Serializable
property cmd : String
property args : Array(String)?
end
property id : String
property name : String
property url : String
property lang : String
property server : String
property run : Script
@[YAML::Field(ignore: true)]
property result : JSON::Any?
def reqs
"#{@result.not_nil!.["rps"]["mean"].as_f.humanize(2)}ps"
end
def latency
ms = @result.not_nil!.["latency"]["mean"].as_f / 1000
"#{ms.format(decimal_places: 2)}ms"
end
end