Add fast_blank example

This commit is contained in:
Godfrey Chan 2016-06-24 17:06:31 +08:00
parent b33956b661
commit ce6412819c
13 changed files with 317 additions and 2 deletions

View File

@ -1,8 +1,6 @@
#[macro_use]
extern crate helix;
use helix::{UncheckedValue, ToRust};
declare_types! {
reopen class Array {
def is_superset_of(self, needle: &[usize]) -> bool {
@ -29,6 +27,10 @@ declare_types! {
}
}
// Delete me:
use helix::{UncheckedValue, ToRust};
impl AsRef<[usize]> for Array {
fn as_ref(&self) -> &[usize] {
let checked = self.0.to_checked().unwrap();

3
examples/turbo_blank/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
target
*.bundle
*.gem

View File

@ -0,0 +1,24 @@
[package]
name = "turbo_blank"
version = "0.1.0"
authors = ["Godfrey Chan <godfreykfc@gmail.com>"]
[lib]
crate-type = ["staticlib"]
[dependencies]
libc = "*"
[dependencies.libcruby-sys]
path = "../../crates/libcruby-sys"
[dependencies.helix]
path = "../.."
[profile.release]
lto = true

View File

@ -0,0 +1,5 @@
source 'https://rubygems.org'
gemspec
gem 'helix_runtime', path: '../../ruby'

View File

@ -0,0 +1,29 @@
PATH
remote: ../../ruby
specs:
helix_runtime (0.5.0)
PATH
remote: .
specs:
turbo_blank (0.1.0)
helix_runtime
GEM
remote: https://rubygems.org/
specs:
minitest (5.8.3)
rake (10.5.0)
PLATFORMS
ruby
DEPENDENCIES
bundler (~> 1.11)
helix_runtime!
minitest (~> 5.0)
rake (~> 10.0)
turbo_blank!
BUNDLED WITH
1.11.2

View File

@ -0,0 +1,34 @@
require 'rake/clean'
require 'rake/testtask'
require 'bundler/setup'
require 'bundler/gem_tasks'
directory 'target'
directory 'lib/turbo_blank'
task :cargo_build do
sh "cargo build --release"
end
CLEAN.include('target')
file "lib/turbo_blank/native.bundle" => ['lib/turbo_blank', :cargo_build] do
sh "gcc -Wl,-force_load,target/release/libturbo_blank.a --shared -Wl,-undefined,dynamic_lookup -o lib/turbo_blank/native.bundle"
end
CLOBBER.include('lib/turbo_blank/native.bundle')
task :irb => "lib/turbo_blank/native.bundle" do
exec "irb -Ilib -rturbo_blank"
end
task :benchmark => "lib/turbo_blank/native.bundle" do
exec "ruby -Ilib benchmark.rb"
end
Rake::TestTask.new(:test) do |t|
t.libs << "test"
t.libs << "lib"
t.test_files = FileList['test/**/*_test.rb']
end
task :test => "lib/turbo_blank/native.bundle"
task :default => :test

View File

@ -0,0 +1,110 @@
require 'bundler/setup'
require 'benchmark'
ENV['IMPLEMENTATION'] = 'NONE'
require 'turbo_blank'
WARMUP_ITER = 10_000
BENCHMARK_ITER = 2_000_000
puts "Benchmarking at #{BENCHMARK_ITER} iterations..."
def scenario(title, str, expected)
title = " #{title} ".inspect
program = <<-RUBY_CODEZ
str = #{str.inspect}
puts
puts #{title}.center(80, '-')
puts
# smoke test
unless str.rails_4_2_blank? == #{expected}
raise "Expected " + str.inspect + ".rails_4_2_blank? to be #{expected}"
end
unless str.rails_5_blank? == #{expected}
raise "Expected " + str.inspect + ".rails_5_blank? to be #{expected}"
end
unless str.is_blank == #{expected}
raise "Expected " + str.inspect + ".is_blank to be #{expected}"
end
# warmup
i = 0
while i < #{WARMUP_ITER}
str.rails_4_2_blank?
str.rails_5_blank?
str.is_blank
i += 1
end
# for realz
GC.start
rails_4_2_result = Benchmark.measure {
i = 0
while i < #{BENCHMARK_ITER}
str.rails_4_2_blank?
i += 1
end
}
GC.start
rails_5_result = Benchmark.measure {
i = 0
while i < #{BENCHMARK_ITER}
str.rails_5_blank?
i += 1
end
}
GC.start
rust_result = Benchmark.measure {
i = 0
while i < #{BENCHMARK_ITER}
str.is_blank
i += 1
end
}
# Should I use real time or...?
puts "Rails 4.2 " + rails_4_2_result.real.round(5).to_s.ljust(7,"0") + " sec"
puts "Rails 5 " + rails_5_result.real.round(5).to_s.ljust(7,"0") + " sec (" + (#{BENCHMARK_ITER}/rails_5_result.real).round.to_s.rjust(8) + " ops/sec) - " + (rails_4_2_result.real / rails_5_result.real).round(2).to_s.rjust(5) + "x faster"
puts "Rust " + rust_result.real.round(5).to_s.ljust(7,"0") + " sec (" + (#{BENCHMARK_ITER}/rust_result.real).round.to_s.rjust(8) + " ops/sec) - " + (rails_4_2_result.real / rust_result.real).round(2).to_s.rjust(5) + "x faster"
RUBY_CODEZ
eval program
end
scenario("Empty string", "", true)
scenario("Single space", " ", true)
scenario("10 spaces", " ", true)
scenario("Mixed", "\r\n\r\n ", true)
scenario("Not blank", "this is a test", false)
scenario("Not blank (padded)", " this is a test", false)
scenario("Not blank (long)",
"this is a longer test
this is a longer test
this is a longer test
this is a longer test
this is a longer test",
false)
scenario("Not blank (long, padded)",
" this is a longer test
this is a longer test
this is a longer test
this is a longer test
this is a longer test",
false)

View File

@ -0,0 +1,30 @@
require "helix_runtime"
RubyString = String
require "turbo_blank/native"
class String
BLANK_RE = /\A[[:space:]]*\z/
def rails_4_2_blank?
BLANK_RE === self
end
def rails_5_blank?
empty? || BLANK_RE === self
end
case ENV["IMPLEMENTATION"]
when "RUST"
alias blank? is_blank
when "RAILS_4_2"
alias blank? rails_4_2_blank?
when "RAILS_5"
alias blank? rails_5_blank?
when "NONE"
else
puts "\nPlease specify an IMPLEMENTATION: RUST, RAILS_4_2, RAILS_5 or NONE"
exit!
end
end

View File

@ -0,0 +1,3 @@
module TurboBlank
VERSION = "0.1.0"
end

View File

@ -0,0 +1,22 @@
#[macro_use]
extern crate helix;
declare_types! {
reopen class RubyString {
def is_blank(self) -> bool {
// self.chars().all(|c| c.is_whitespace())
self.to_string().chars().all(|c| c.is_whitespace())
}
}
}
// Delete me:
use helix::{UncheckedValue, ToRust};
impl ToString for RubyString {
fn to_string(&self) -> String {
let checked = self.0.to_checked().unwrap();
checked.to_rust()
}
}

View File

@ -0,0 +1,4 @@
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
require 'turbo_blank'
require 'minitest/autorun'

View File

@ -0,0 +1,15 @@
require 'test_helper'
class TurboBlankTest < Minitest::Test
def test_blank
assert_predicate '', :blank?
assert_predicate ' ', :blank?
assert_predicate " \n\t \r ", :blank?
assert_predicate ' ', :blank?
assert_predicate "\u00a0", :blank?
end
def test_not_blank
refute_predicate 'a', :blank?
end
end

View File

@ -0,0 +1,34 @@
# coding: utf-8
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'turbo_blank/version'
Gem::Specification.new do |spec|
spec.name = "turbo_blank"
spec.version = TurboBlank::VERSION
spec.authors = ["Godfrey Chan"]
spec.email = ["godfreykfc@gmail.com"]
spec.summary = %q{TODO: Write a short summary, because Rubygems requires one.}
spec.description = %q{TODO: Write a longer description or delete this line.}
spec.homepage = "TODO: Put your gem's website or public repo URL here."
spec.license = "MIT"
# Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
# delete this section to allow pushing this gem to any host.
if spec.respond_to?(:metadata)
spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
else
raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
end
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
spec.bindir = "exe"
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
spec.require_paths = ["lib"]
spec.add_runtime_dependency "helix_runtime"
spec.add_development_dependency "bundler", "~> 1.11"
spec.add_development_dependency "rake", "~> 10.0"
spec.add_development_dependency "minitest", "~> 5.0"
end