Skip to content

Commit

Permalink
Merge pull request #2450 from janbiedermann/windows_performance_compare
Browse files Browse the repository at this point in the history
Windows performance:compare
  • Loading branch information
hmdne committed Oct 27, 2022
2 parents 850079a + 0f480dc commit 8ae6198
Show file tree
Hide file tree
Showing 8 changed files with 128 additions and 48 deletions.
23 changes: 23 additions & 0 deletions HACKING.md
Expand Up @@ -185,6 +185,29 @@ Array#permutation_returns_an_Enumerator_which_works_as_expected_even_when_the_ar
Array#permutation_generates_from_a_defensive_copy,_ignoring_mutations 0.038
```

### AsciiDoctor Benchmark and git branch performance comparison

It is testing the performance for the real life application AsciiDoctor, compiling it, running it and asset size.
It prints a nice summary to compare the current branch with the master branch.
On Windows make sure to have the Ruby DevKit enabled with `ridk enable`.

Run the task on any system with: `bundle exec rake performance:compare`

Example output:
```
=== Summary ===
Summary of performance changes between (previous) master and (current) v1.5.1-48-gc470e969:
Comparison of V8 function optimization status:
Dateien tmp/performance/optstatus_previous und tmp/performance/optstatus_current sind identisch.
Comparison of the Asciidoctor (a real-life Opal application) compile and run:
Compile time: 9.367 (±2.21%) -> 9.579 (±17.53%) (change: +2.26%)
Run time: 2.049 (±9.25%) -> 2.147 (±19.70%) (change: +4.80%)
Bundle size: 4740.27 kB -> 4740.27 kB (change: +0.00%)
Minified bundle size: 995.10 kB -> 995.10 kB (change: +0.00%)
Mangled & minified: 706.32 kB -> 706.32 kB (change: +0.00%)
```

## Parser

Expand Down
4 changes: 3 additions & 1 deletion lib/opal/builder_scheduler.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true

require 'opal/os' unless RUBY_ENGINE == 'opal'

module Opal
class BuilderScheduler
def initialize(builder)
Expand All @@ -13,7 +15,7 @@ def initialize(builder)

if RUBY_ENGINE != 'opal'
# Windows has a faulty `fork`.
if Gem.win_platform? || ENV['OPAL_PREFORK_DISABLE']
if OS.windows? || ENV['OPAL_PREFORK_DISABLE']
require 'opal/builder_scheduler/sequential'
Opal.builder_scheduler = BuilderScheduler::Sequential
else
Expand Down
15 changes: 4 additions & 11 deletions lib/opal/cli_runners/chrome.rb
Expand Up @@ -5,6 +5,7 @@
require 'timeout'
require 'tmpdir'
require 'rbconfig'
require 'opal/os'

module Opal
module CliRunners
Expand Down Expand Up @@ -115,7 +116,7 @@ def run_chrome_server
raise 'Chrome server can be started only on localhost' if chrome_host != DEFAULT_CHROME_HOST

# Disable web security with "--disable-web-security" flag to be able to do XMLHttpRequest (see test_openuri.rb)
chrome_server_cmd = %{#{windows? ? "\"#{chrome_executable.shellescape}\"" : chrome_executable.shellescape} \
chrome_server_cmd = %{#{OS.shellescape(chrome_executable)} \
--headless \
--disable-web-security \
--remote-debugging-port=#{chrome_port} \
Expand Down Expand Up @@ -153,15 +154,15 @@ def chrome_server_running?

def chrome_executable
ENV['GOOGLE_CHROME_BINARY'] ||
if windows?
if OS.windows?
[
'C:/Program Files/Google/Chrome Dev/Application/chrome.exe',
'C:/Program Files/Google/Chrome/Application/chrome.exe'
].each do |path|
next unless File.exist? path
return path
end
elsif mac_os?
elsif OS.macos?
'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'
else
%w[
Expand All @@ -177,14 +178,6 @@ def chrome_executable
end
end

def windows?
/bccwin|cygwin|djgpp|mingw|mswin|wince/.match?(RbConfig::CONFIG['host_os'])
end

def mac_os?
/darwin|mac os/.match?(RbConfig::CONFIG['host_os'])
end

def mktmpdir(&block)
Dir.mktmpdir('chrome-opal-', &block)
end
Expand Down
6 changes: 3 additions & 3 deletions lib/opal/cli_runners/nodejs.rb
Expand Up @@ -3,6 +3,7 @@
require 'shellwords'
require 'opal/paths'
require 'opal/cli_runners/system_runner'
require 'opal/os'

module Opal
module CliRunners
Expand Down Expand Up @@ -39,10 +40,9 @@ def self.call(data)

# Ensure stdlib node_modules is among NODE_PATHs
def self.node_modules
npsep = Gem.win_platform? ? ';' : ':'
ENV['NODE_PATH'].to_s.split(npsep).tap do |paths|
ENV['NODE_PATH'].to_s.split(OS.env_sep).tap do |paths|
paths << NODE_PATH unless paths.include? NODE_PATH
end.join(npsep)
end.join(OS.env_sep)
end

class MissingNodeJS < RunnerError
Expand Down
59 changes: 59 additions & 0 deletions lib/opal/os.rb
@@ -0,0 +1,59 @@
# frozen_string_literal: true

module Opal
module OS
module_function

def windows?
/bccwin|cygwin|djgpp|mingw|mswin|wince/.match?(RbConfig::CONFIG['host_os'])
end

def macos?
/darwin|mac os/.match?(RbConfig::CONFIG['host_os'])
end

def shellescape(str)
if windows?
'"' + str.gsub('"', '""') + '"'
else
str.shellescape
end
end

def env_sep
windows? ? ';' : ':'
end

def path_sep
windows? ? '\\' : '/'
end

def cmd_sep
windows? ? ' & ' : ' ; '
end

def dev_null
windows? ? 'NUL' : '/dev/null'
end

def bash_c(*commands)
cmd = if windows?
[
'bundle',
'exec',
'cmd',
'/c',
]
else
[
'bundle',
'exec',
'bash',
'-c',
]
end

cmd << commands.join(cmd_sep)
end
end
end
2 changes: 1 addition & 1 deletion lib/opal/util.rb
Expand Up @@ -15,7 +15,7 @@ module Util
# @param str [String] string to minify
# @return [String]
def uglify(source, mangle: false)
sh "bin/yarn -s run terser -c #{'-m' if mangle}", data: source
sh "#{'ruby ' if Gem.win_platform?}bin/yarn -s run terser -c #{'-m' if mangle}", data: source
end

# Gzip code to check file size.
Expand Down
57 changes: 30 additions & 27 deletions tasks/performance.rake
@@ -1,6 +1,9 @@
# A set of tasks to track performance regressions by the CI

require 'opal/util'
require_relative "#{__dir__}/../lib/opal/util"
require_relative "#{__dir__}/../lib/opal/os"

OS = Opal::OS

class Timing
def initialize(tries: 31, &block)
Expand Down Expand Up @@ -67,35 +70,34 @@ end
$failures = []

ASCIIDOCTOR_REPO_BASE = ENV['ASCIIDOCTOR_REPO_BASE'] || 'https://github.com/asciidoctor'

ASCIIDOCTOR_COMMIT = '869e8236'
ASCIIDOCTOR_JS_COMMIT = '053fa0d3'
# Selected asciidoctor versions were working on Aug 19 2021, feel free to update.
ASCIIDOCTOR_PREPARE = [
"bundle",
"exec",
"bash",
"-c",
<<~BASH
mkdir -p tmp/performance
pushd tmp/performance
git clone #{ASCIIDOCTOR_REPO_BASE}/asciidoctor >/dev/null 2>&1
pushd asciidoctor; git checkout 869e8236 >/dev/null 2>&1; popd
git clone #{ASCIIDOCTOR_REPO_BASE}/asciidoctor.js >/dev/null 2>&1
pushd asciidoctor.js; git checkout 053fa0d3 >/dev/null 2>&1; popd
erb ../../tasks/performance/asciidoctor_test.rb.erb > asciidoctor_test.rb
popd
BASH
]

ASCIIDOCTOR_BUILD_OPAL = "bin/opal --no-cache -c " \
"-Itmp/performance/asciidoctor/lib " \
"-Itmp/performance/asciidoctor.js/packages/core/lib " \
"-sconcurrent/map -sslim/include " \
"tmp/performance/asciidoctor_test.rb > tmp/performance/asciidoctor_test.js"
S = OS.path_sep
ASCIIDOCTOR_PREPARE = OS.bash_c(
"pushd tmp#{S}performance",
"git clone #{ASCIIDOCTOR_REPO_BASE}/asciidoctor >#{OS.dev_null} 2>&1",
"pushd asciidoctor", "git checkout #{ASCIIDOCTOR_COMMIT} >#{OS.dev_null} 2>&1", "popd",
"git clone #{ASCIIDOCTOR_REPO_BASE}/asciidoctor.js >#{OS.dev_null} 2>&1",
"pushd asciidoctor.js", "git checkout #{ASCIIDOCTOR_JS_COMMIT} >#{OS.dev_null} 2>&1", "popd",
"erb ../../tasks/performance/asciidoctor_test.rb.erb > asciidoctor_test.rb",
"popd"
)

ASCIIDOCTOR_BUILD_OPAL = "#{'ruby ' if Gem.win_platform?}bin/opal --no-cache -c " \
"-Itmp/performance/asciidoctor/lib " \
"-Itmp/performance/asciidoctor.js/packages/core/lib " \
"-sconcurrent/map -sslim/include " \
"tmp/performance/asciidoctor_test.rb > tmp/performance/asciidoctor_test.js"
ASCIIDOCTOR_RUN_RUBY = "bundle exec ruby -Itmp/performance/asciidoctor/lib tmp/performance/asciidoctor_test.rb"
ASCIIDOCTOR_RUN_OPAL = "node tmp/performance/asciidoctor_test.js"

# Generate V8 function optimization status report for corelib methods
NODE_OPTSTATUS = "env NODE_OPTS=--allow-natives-syntax bin/opal tasks/performance/optimization_status.rb"
NODE_OPTSTATUS = if Gem.win_platform?
"set NODE_OPTS=--allow-natives-syntax && ruby bin/opal tasks/performance/optimization_status.rb"
else
"env NODE_OPTS=--allow-natives-syntax bin/opal tasks/performance/optimization_status.rb"
end

performance_stat = ->(name) {
stat = {}
Expand Down Expand Up @@ -128,6 +130,7 @@ namespace :performance do

# Prepare
puts "\n* Preparing asciidoctor..."
FileUtils.mkdir_p("tmp/performance") unless Dir.exist?("tmp/performance")
sh(*ASCIIDOCTOR_PREPARE)

puts "\n* Running AsciiDoctor with CRuby..."
Expand All @@ -136,12 +139,12 @@ namespace :performance do
current = performance_stat.(:current)

# Prepare previous
sh("git checkout --recurse-submodules #{ref} && bundle install >/dev/null 2>&1")
sh("git checkout --recurse-submodules #{ref} && bundle install >#{OS.dev_null} 2>&1")

previous = performance_stat.(:previous)

# Restore current
sh("git checkout --recurse-submodules - && bundle install >/dev/null 2>&1")
sh("git checkout --recurse-submodules - && bundle install >#{OS.dev_null} 2>&1")

# Summary
puts "\n=== Summary ==="
Expand Down
10 changes: 5 additions & 5 deletions tasks/testing.rake
@@ -1,4 +1,8 @@
require_relative "#{__dir__}/../lib/opal/os"
require 'rspec/core/rake_task'

OS = Opal::OS

RSpec::Core::RakeTask.new(:rspec) do |t|
t.pattern = 'spec/lib/**/*_spec.rb'
end
Expand Down Expand Up @@ -249,7 +253,7 @@ module Testing
puts 'Server ready.'
yield self
ensure
if windows?
if OS.windows?
# https://blog.simplificator.com/2016/01/18/how-to-kill-processes-on-windows-using-ruby/
# system("taskkill /f /pid #{pid}")
Process.kill("KILL", server)
Expand All @@ -260,10 +264,6 @@ module Testing
end
end

def windows?
(/cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM) != nil
end

def sinatra_server_running?
puts "Connecting to localhost:4567..."
TCPSocket.new('localhost', '4567').close
Expand Down

0 comments on commit 8ae6198

Please sign in to comment.