Last active
November 29, 2015 22:15
-
-
Save paddor/760caa83c6ebd2eb20ab to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/env ruby | |
require 'benchmark/ips' | |
require 'ffi' | |
module CZMQ | |
module FFI | |
module LibC | |
extend ::FFI::Library | |
ffi_lib ::FFI::Platform::LIBC | |
attach_function :free, [ :pointer ], :void, blocking: true | |
end | |
extend ::FFI::Library | |
def self.available? | |
@available | |
end | |
begin | |
lib_name = 'libczmq' | |
lib_paths = ['/usr/local/lib', '/opt/local/lib', '/usr/lib64'] | |
.map { |path| "#{path}/#{lib_name}.#{::FFI::Platform::LIBSUFFIX}" } | |
ffi_lib lib_paths + [lib_name] | |
@available = true | |
rescue LoadError | |
warn "" | |
warn "WARNING: ::CZMQ::FFI is not available without libczmq." | |
warn "" | |
@available = false | |
end | |
if available? | |
opts = { | |
blocking: true # only necessary on MRI to deal with the GIL. | |
} | |
enum :zarmour_mode, [ | |
:mode_base64_std, 0, | |
:mode_base64_url, 1, | |
:mode_base32_std, 2, | |
:mode_base32_hex, 3, | |
:mode_base16, 4, | |
:mode_z85, 5, | |
] | |
attach_function :zarmour_new, [], :pointer, **opts | |
attach_function :zarmour_destroy, [:pointer], :void, **opts | |
attach_function :zarmour_encode, [:pointer, :pointer, :size_t], :pointer, **opts | |
attach_function :zarmour_set_mode, [:pointer, :zarmour_mode], :void, **opts | |
end | |
# zarmour - armoured text encoding and decoding | |
class Zarmour | |
class DestroyedError < RuntimeError; end | |
# Boilerplate for self pointer, initializer, and finalizer | |
class << self | |
alias :__new :new | |
end | |
def initialize ptr, finalize=true | |
@ptr = ptr | |
if @ptr.null? | |
@ptr = nil # Remove null pointers so we don't have to test for them. | |
elsif finalize | |
@finalizer = self.class.create_finalizer_for @ptr | |
ObjectSpace.define_finalizer self, @finalizer | |
end | |
end | |
def self.create_finalizer_for ptr | |
Proc.new do | |
ptr_ptr = ::FFI::MemoryPointer.new :pointer | |
ptr_ptr.write_pointer ptr | |
::CZMQ::FFI.zarmour_destroy ptr_ptr | |
end | |
end | |
def null? | |
!@ptr or @ptr.null? | |
end | |
# Return internal pointer | |
def __ptr | |
raise DestroyedError unless @ptr | |
@ptr | |
end | |
# So external Libraries can just pass the Object to a FFI function which expects a :pointer | |
alias_method :to_ptr, :__ptr | |
# Nullify internal pointer and return pointer pointer | |
def __ptr_give_ref | |
raise DestroyedError unless @ptr | |
ptr_ptr = ::FFI::MemoryPointer.new :pointer | |
ptr_ptr.write_pointer @ptr | |
ObjectSpace.undefine_finalizer self if @finalizer | |
@finalizer = nil | |
@ptr = nil | |
ptr_ptr | |
end | |
# Create a new zarmour. | |
def self.new() | |
ptr = ::CZMQ::FFI.zarmour_new() | |
__new ptr | |
end | |
# Encode a stream of bytes into an armoured string. | |
def encode(data, data_size) | |
raise DestroyedError unless @ptr | |
self_p = @ptr | |
data_size = Integer(data_size) | |
result = ::CZMQ::FFI.zarmour_encode(self_p, data, data_size) | |
result = ::FFI::AutoPointer.new(result, LibC.method(:free)) | |
result | |
end | |
def encode_immediate_free(data, data_size) | |
raise DestroyedError unless @ptr | |
self_p = @ptr | |
data_size = Integer(data_size) | |
cstring = ::CZMQ::FFI.zarmour_encode(self_p, data, data_size) | |
rubystring = cstring.read_string | |
LibC.free cstring | |
rubystring | |
end | |
# Set the mode property. | |
def set_mode(mode) | |
raise DestroyedError unless @ptr | |
self_p = @ptr | |
result = ::CZMQ::FFI.zarmour_set_mode(self_p, mode) | |
result | |
end | |
end | |
end | |
end | |
SHORT_STRING = FFI::MemoryPointer.from_string "foobar baz foo bar b" | |
SHORT_STRING_LEN = SHORT_STRING.size - 1 # without the null byte | |
LONG_STRING = FFI::MemoryPointer.from_string(("foobar foobar foobar" * 100)) | |
LONG_STRING_LEN = LONG_STRING.size - 1 # without the null byte | |
ZARMOUR = CZMQ::FFI::Zarmour.new | |
ZARMOUR.set_mode(:mode_z85) | |
puts "SHORT STRING (#{SHORT_STRING_LEN} bytes)" | |
puts "=" * 78 | |
Benchmark.ips do |x| | |
# Configure the number of seconds used during | |
# the warmup phase (default 2) and calculation phase (default 5) | |
x.config(:time => 5, :warmup => 2) | |
x.report("finalizer free()") do | |
ZARMOUR.encode(SHORT_STRING, SHORT_STRING_LEN) | |
end | |
x.report("immediate free()") do | |
ZARMOUR.encode_immediate_free(SHORT_STRING, SHORT_STRING_LEN) | |
end | |
x.compare! | |
end | |
puts | |
puts "LONG STRING (#{LONG_STRING_LEN} bytes)" | |
puts "=" * 78 | |
Benchmark.ips do |x| | |
x.report("finalizer free()") do | |
ZARMOUR.encode(LONG_STRING, LONG_STRING_LEN) | |
end | |
x.report("immediate free()") do | |
ZARMOUR.encode_immediate_free(LONG_STRING, LONG_STRING_LEN) | |
end | |
x.compare! | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$ ./benchmark_fresh_cstring.rb | |
SHORT STRING (20 bytes) | |
============================================================================== | |
Calculating ------------------------------------- | |
finalizer free() 12.508k i/100ms | |
immediate free() 30.465k i/100ms | |
------------------------------------------------- | |
finalizer free() 173.257k (±48.7%) i/s - 612.892k | |
immediate free() 370.366k (±10.0%) i/s - 1.858M | |
Comparison: | |
immediate free(): 370366.5 i/s | |
finalizer free(): 173256.8 i/s - 2.14x slower | |
LONG STRING (2000 bytes) | |
============================================================================== | |
Calculating ------------------------------------- | |
finalizer free() 4.500k i/100ms | |
immediate free() 5.332k i/100ms | |
------------------------------------------------- | |
finalizer free() 55.104k (±22.5%) i/s - 252.000k | |
immediate free() 62.283k (± 8.6%) i/s - 309.256k | |
Comparison: | |
immediate free(): 62283.3 i/s | |
finalizer free(): 55103.5 i/s - 1.13x slower | |
./benchmark_fresh_cstring.rb 26.23s user 2.25s system 99% cpu 28.584 total |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment