SlideShare una empresa de Scribd logo
1 de 69
Descargar para leer sin conexión
How to write Ruby
extensions with Crystal
@gaar4ica

Anna Shcherbinina
I like Ruby
5 YO, a lot of stuff
So, I like Ruby
But, sometimes,
I need my code to work
faster
def fibonacci(n)
n <= 1 ? n : fibonacci(n - 1) + fibonacci(n - 2)
end
Case of possible bottle
neck
Ruby has C bindings
C is hard to learn and
understand
Static types Allocating 

memory
And tons of other
complications
for Ruby developer
I’m ruby developer,
I don’t want to write my code in C
I’m ruby developer,
I want to write my code in Ruby.
But to be honest…
Node.js
NET
OWL PDF
XML
Redis
C++
Scala
BASIC
C#
I’m ruby developer,
I want to write my code in Ruby
Also, I like Crystal
Crystal has ruby-like
Spoiler about crystal
Guess the language
# define class Dog
class Dog
def initialize(breed, name)
@breed = breed
@name = name
end
def bark
puts 'Ruff! Ruff!'
end
def display
puts "I am of #{@breed} breed and my name is #{@name}"
end
end

# make an object
d = Dog.new('Labrador', 'Benzy')
Crystal compiles to native
code
Spoiler about crystal
So, I like Crystal
No way
No way
?
C
bindings
C bindings
C layer
Link Crystal classes
and methods to Ruby,
so Ruby knows about
them
Translate 

Ruby-types 

to Crystal-types
So, Crystal lang
WUT?
Compile to efficient 

native code
Statically 

type-checked
Ruby-inspired 

syntax
C bindings
Web Frameworks
• amatista
• amethyst
• kemal
• moonshine
Testing
Search CacheDBDDBD
Third-party 

APIs
Libs
Ready for production or not?
Web apps Microservice
• maths calculation
• small but heavy parts of
projects
What for
Not so like ruby
if @a
# here @a can be nil
end
if @a.is_a?(String)
# here @a is not guaranteed to be a String
end
if @a.responds_to?(:abs)
# here @a is not guaranteed to respond to `abs`
end
The below doesn’t work with instance variables,
class variables or global variables.
Variables
Not so like ruby
To work with these, first assign them to a variable:
if a = @a
# here a can't be nil
end
# Second option: use `Object#try` found in the
standard library
@a.try do |a|
# here a can't be nil
end
if (a = @a).is_a?(String)
# here a is guaranteed to be a String
end
Variables
Not so like ruby
Classes and methods
Not so like ruby
Overloading Return types
Visibility Finalize
Macros
Not so like ruby
class Object
macro def instance_vars_names : Array(String)
{{ @type.instance_vars.map &.name.stringify }}
end
end
class Person
def initialize(@name, @age)
end
end
person = Person.new "John", 30
person.instance_vars_names #=> ["name", "age"]
Threads
Not so like ruby
def generate(chan)
i = 2
loop do
chan.send(i)
i += 1
end
end
def filter(in_chan, out_chan, prime)
loop do
i = in_chan.receive
if i % prime != 0
out_chan.send(i)
end
end
end
ch = Channel(Int32).new
spawn generate(ch)
100.times do
prime = ch.receive
puts prime
ch1 =
Channel(Int32).new
spawn filter(ch, ch1,
prime)
ch = ch1
end
Benchmarking
Time (s)
Go Erlang Crystal Scala Ruby C Crystal Ruby C Crystal Ruby
4.084.082.21
27.92
6.025.08
41.73
6.426.091.971.48
Memory (Mb)
Go Erlang Crystal Scala Ruby C Crystal Ruby C Crystal Ruby
162.40
1.400.40
115.50107.60
32.4030.00
1.505.3017.10
2.00
Threadring Binarytree Fast
Threadring Binarytree Fast
Back to case of
possible bottle neck
def fibonacci(n)
n <= 1 ? n : fibonacci(n - 1) + fibonacci(n - 2)
end
Back to
C
bindings
Less work for C
Passing primitive data structures:
• integer
• string
• boolean
• float / decimal
• etc.
No allocation memory for complex data types
Don’t do a lot of work for casting variables passed as attributes
Ruby C API
Entry point
Then every C extension has to implement a function named
Init_xxxxx (xxxxx being your extension’s name).
It is being executed during the “require” of your extension.
void Init_xxxxx() {
// Code executed during "require"
}
Ruby C API
Types
Ruby C API defines a bunch of handful C constants defining
standard Ruby objects:
C Ruby
Qnil nil
Qtrue TRUE
Qfalse FALSE
rb_cObject Object
rb_mKernel Kernel
rb_cString String
Ruby C API
Declaring modules and classes
// Creating classes
// rb_define_class creates a new class named name
// and inheriting from super.
// The return value is a handle to the new class.
VALUE rb_define_class(const char *name, VALUE super);
// Creating modules
// rb_define_module defines a module whose name
// is the string name.
// The return value is a handle to the module.
VALUE rb_define_module(const char *name);
Ruby C API
Declaring methods
// Creating a method
// rb_define_method defines a new instance method in the class
// or moduleklass.
// The method calls func with argc arguments.
// They differ in how they specify the name - rb_define_method
// uses the constant string name
void rb_define_method(VALUE klass, const char *name, VALUE
(*func)(ANYARGS), int argc);
// rb_define_protected_method and rb_define_private_method are
similar to rb_define_method,
// except that they define protected and private methods,
respectively.
void rb_define_protected_method(VALUE klass, const char *name,
VALUE (*func)(ANYARGS), int argc);
void rb_define_private_method(VALUE klass, const char *name,
VALUE (*func)(ANYARGS), int argc);
Ruby C API
Ruby objects → C types
// Convert Numeric to integer.
long rb_num2int(VALUE obj);
// Convert Numeric to unsigned integer.
unsigned long rb_num2uint(VALUE obj);
// Convert Numeric to double.
double rb_num2dbl(VALUE);
// Convert Ruby string to a String.
VALUE rb_str_to_str(VALUE object);
char* rb_string_value_cstr(volatile VALUE* object_variable);
Ruby C API
C types → Ruby objects
// Convert an integer to Fixnum or Bignum.
INT2NUM( int );
// convert an unsigned integer to Fixnum or Bignum.
UINT2NUM( unsigned int );
// Convert a double to Float.
rb_float_new( double );
// Convert a character string to String.
rb_str_new2( char* );
// Convert a character string to ID (for Ruby function names,
etc.).
rb_intern( char* );
// Convert a character string to a ruby Symbol object.
ID2SYM( rb_intern(char*) );
Ruby C API
Simple example
# my_class.rb
class MyClass
def my_method(param1, param2)
end
end
// my_class_ext.c
static VALUE myclass_mymethod(VALUE rb_self, VALUE rb_param1, VALUE
rb_param2)
{
// Code executed when calling my_method on an object of class MyClass
}
void Init_xxxxx()
{
// Define a new class (inheriting Object) in this module
VALUE myclass = rb_define_class("MyClass", rb_cObject);
// Define a method in this class, taking 2 arguments,
// and using the C method "myclass_method" as its body
rb_define_method(myclass, "my_method", myclass_mymethod, 2);
}
Ruby C API
Simple example
# my_class.rb
class MyClass
def my_method(param1, param2)
end
end
// my_class_ext.c
static VALUE myclass_mymethod(VALUE rb_self, VALUE rb_param1, VALUE
rb_param2)
{
// Code executed when calling my_method on an object of class MyClass
}
void Init_xxxxx()
{
// Define a new class (inheriting Object) in this module
VALUE myclass = rb_define_class("MyClass", rb_cObject);
// Define a method in this class, taking 2 arguments,
// and using the C method "myclass_method" as its body
rb_define_method(myclass, "my_method", myclass_mymethod, 2);
}
Ruby C API
Simple example
# my_class.rb
class MyClass
def my_method(param1, param2)
end
end
// my_class_ext.c
static VALUE myclass_mymethod(VALUE rb_self, VALUE rb_param1, VALUE
rb_param2)
{
// Code executed when calling my_method on an object of class MyClass
}
void Init_xxxxx()
{
// Define a new class (inheriting Object) in this module
VALUE myclass = rb_define_class("MyClass", rb_cObject);
// Define a method in this class, taking 2 arguments,
// and using the C method "myclass_method" as its body
rb_define_method(myclass, "my_method", myclass_mymethod, 2);
}
def fibonacci(n)
n <= 1 ? n : fibonacci(n - 1) + fibonacci(n - 2)
end
Back to case of possible 

bottle neck
And solution
C
A lib declaration groups C functions and types that belong to 

a library.
Crystal extension
It’s easy
lib CrRuby
type VALUE = Void*
$rb_cObject : VALUE
end
Every ruby object is treated as type VALUE in C.
Crystal extension
It’s easy
lib CrRuby
type VALUE = Void*
$rb_cObject : VALUE
end
rb_cObject is a C constant defining standard Ruby Object class.
Crystal extension
It’s easy
lib CrRuby
type VALUE = Void*
$rb_cObject : VALUE
end
lib CrRuby
type VALUE = Void*
$rb_cObject : VALUE
fun rb_num2int(value : VALUE) : Int32
fun rb_int2inum(value : Int32) : VALUE
end
A fun declaration inside a lib binds to a C function.
We bind rb_num2int & rb_int2inum, to use it later.
Crystal extension
It’s easy
lib CrRuby
type VALUE = Void*
type METHOD_FUNC = VALUE, VALUE -> VALUE
$rb_cObject : VALUE
fun rb_num2int(value : VALUE) : Int32
fun rb_int2inum(value : Int32) : VALUE
fun rb_define_class(name: UInt8*, super: VALUE) : VALUE
fun rb_define_method(klass: VALUE, name: UInt8*, func:
METHOD_FUNC, argc: Int32)
end
Crystal extension
It’s easy
We bind rb_define_class & rb_define_method.
def fibonacci_cr(n)
n <= 1 ? n : fibonacci_cr(n - 1) + fibonacci_cr(n - 2)
end
Our new and shiny fibonacci method in Crystal.
def fibonacci(n)
n <= 1 ? n : fibonacci(n - 1) + fibonacci(n - 2)
end
Do you see the difference with implementation in Ruby?
Crystal extension
It’s easy
Crystal extension
It’s easy
Let’s create a wrapper for this method, used for:
• convert inbound Ruby-type parameter to Crystal;
• convert outbound result back to Ruby type.
def fibonacci_cr_wrapper(self : CrRuby::VALUE, value : CrRuby::VALUE)
int_value = CrRuby.rb_num2int(value)
CrRuby.rb_int2inum(fibonacci_cr(int_value))
end
Crystal extension
It’s easy
Now we are done with our functions and C bindings.
lib CrRuby
type VALUE = Void*
type METHOD_FUNC = VALUE, VALUE -> VALUE
$rb_cObject : VALUE
fun rb_num2int(value : VALUE) : Int32
fun rb_int2inum(value : Int32) : VALUE
fun rb_define_class(name: UInt8*, super: VALUE) : VALUE
fun rb_define_method(klass: VALUE, name: UInt8*, func: METHOD_FUNC, argc: Int32)
end
def fibonacci_cr_wrapper(self : CrRuby::VALUE, value : CrRuby::VALUE)
int_value = CrRuby.rb_num2int(value)
CrRuby.rb_int2inum(fibonacci_cr(int_value))
end
def fibonacci_cr(n)
n <= 1 ? n : fibonacci_cr(n - 1) + fibonacci_cr(n - 2)
end
Crystal extension
It’s easy
Now we are done with our functions and C bindings.
lib CrRuby
type VALUE = Void*
type METHOD_FUNC = VALUE, VALUE -> VALUE
$rb_cObject : VALUE
fun rb_num2int(value : VALUE) : Int32
fun rb_int2inum(value : Int32) : VALUE
fun rb_define_class(name: UInt8*, super: VALUE) : VALUE
fun rb_define_method(klass: VALUE, name: UInt8*, func: METHOD_FUNC, argc: Int32)
end
def fibonacci_cr_wrapper(self : CrRuby::VALUE, value : CrRuby::VALUE)
int_value = CrRuby.rb_num2int(value)
CrRuby.rb_int2inum(fibonacci_cr(int_value))
end
def fibonacci_cr(n)
n <= 1 ? n : fibonacci_cr(n - 1) + fibonacci_cr(n - 2)
end
Crystal extension
It’s easy
Now we are done with our functions and C bindings.
lib CrRuby
type VALUE = Void*
type METHOD_FUNC = VALUE, VALUE -> VALUE
$rb_cObject : VALUE
fun rb_num2int(value : VALUE) : Int32
fun rb_int2inum(value : Int32) : VALUE
fun rb_define_class(name: UInt8*, super: VALUE) : VALUE
fun rb_define_method(klass: VALUE, name: UInt8*, func: METHOD_FUNC, argc: Int32)
end
def fibonacci_cr_wrapper(self : CrRuby::VALUE, value : CrRuby::VALUE)
int_value = CrRuby.rb_num2int(value)
CrRuby.rb_int2inum(fibonacci_cr(int_value))
end
def fibonacci_cr(n)
n <= 1 ? n : fibonacci_cr(n - 1) + fibonacci_cr(n - 2)
end
Crystal extension
It’s easy
Now we are done with our functions and C bindings.
lib CrRuby
type VALUE = Void*
type METHOD_FUNC = VALUE, VALUE -> VALUE
$rb_cObject : VALUE
fun rb_num2int(value : VALUE) : Int32
fun rb_int2inum(value : Int32) : VALUE
fun rb_define_class(name: UInt8*, super: VALUE) : VALUE
fun rb_define_method(klass: VALUE, name: UInt8*, func: METHOD_FUNC, argc: Int32)
end
def fibonacci_cr_wrapper(self : CrRuby::VALUE, value : CrRuby::VALUE)
int_value = CrRuby.rb_num2int(value)
CrRuby.rb_int2inum(fibonacci_cr(int_value))
end
def fibonacci_cr(n)
n <= 1 ? n : fibonacci_cr(n - 1) + fibonacci_cr(n - 2)
end
Crystal extension
It’s easy
We bind init function, the first one to be called.
As you remember, a function named Init_xxxxx is being executed
during the “require” of your extension.
fun init = Init_cr_math
end
Crystal extension
It’s easy
Firstly, we start garbage collector.
We need to invoke Crystal's "main" function, the one that initializes all
constants and runs the top-level code.
We pass 0 and null to argc and argv.
fun init = Init_cr_math
GC.init
LibCrystalMain.__crystal_main(0, Pointer(Pointer(UInt8)).null)
end
Crystal extension
It’s easy
We define class CrMath.
fun init = Init_cr_math
GC.init
LibCrystalMain.__crystal_main(0, Pointer(Pointer(UInt8)).null)
cr_math = CrRuby.rb_define_class("CrMath", CrRuby.rb_cObject)
CrRuby.rb_define_method(cr_math, "fibonacci", ->fibonacci_cr_wrapper, 1)
end
Crystal extension
It’s easy
We define class CrMath.
Attach method fibonacci to class.
fun init = Init_cr_math
GC.init
LibCrystalMain.__crystal_main(0, Pointer(Pointer(UInt8)).null)
cr_math = CrRuby.rb_define_class("CrMath", CrRuby.rb_cObject)
CrRuby.rb_define_method(cr_math, "fibonacci", ->fibonacci_cr_wrapper, 1)
end
Crystal extension
Ready to compile
We have Crystal library with C bindings.
lib CrRuby
type VALUE = Void*
type METHOD_FUNC = VALUE, VALUE -> VALUE
$rb_cObject : VALUE
fun rb_num2int(value : VALUE) : Int32
fun rb_int2inum(value : Int32) : VALUE
fun rb_define_class(name: UInt8*, super: VALUE) : VALUE
fun rb_define_method(klass: VALUE, name: UInt8*, func: METHOD_FUNC, argc: Int32)
end
def fibonacci_cr_wrapper(self : CrRuby::VALUE, value : CrRuby::VALUE)
int_value = CrRuby.rb_num2int(value)
CrRuby.rb_int2inum(fibonacci_cr(int_value))
end
def fibonacci_cr(n)
n <= 1 ? n : fibonacci_cr(n - 1) + fibonacci_cr(n - 2)
end
fun init = Init_cr_math
GC.init
LibCrystalMain.__crystal_main(0, Pointer(Pointer(UInt8)).null)
cr_math = CrRuby.rb_define_class("CrMath", CrRuby.rb_cObject)
CrRuby.rb_define_method(cr_math, "fibonacci", ->fibonacci_cr_wrapper, 1)
end
Crystal extension
Ready to compile
We have method fibonacci_cr and wrapper for it.
lib CrRuby
type VALUE = Void*
type METHOD_FUNC = VALUE, VALUE -> VALUE
$rb_cObject : VALUE
fun rb_num2int(value : VALUE) : Int32
fun rb_int2inum(value : Int32) : VALUE
fun rb_define_class(name: UInt8*, super: VALUE) : VALUE
fun rb_define_method(klass: VALUE, name: UInt8*, func: METHOD_FUNC, argc: Int32)
end
def fibonacci_cr_wrapper(self : CrRuby::VALUE, value : CrRuby::VALUE)
int_value = CrRuby.rb_num2int(value)
CrRuby.rb_int2inum(fibonacci_cr(int_value))
end
def fibonacci_cr(n)
n <= 1 ? n : fibonacci_cr(n - 1) + fibonacci_cr(n - 2)
end
fun init = Init_cr_math
GC.init
LibCrystalMain.__crystal_main(0, Pointer(Pointer(UInt8)).null)
cr_math = CrRuby.rb_define_class("CrMath", CrRuby.rb_cObject)
CrRuby.rb_define_method(cr_math, "fibonacci", ->fibonacci_cr_wrapper, 1)
end
Crystal extension
Ready to compile
We have entry point.
lib CrRuby
type VALUE = Void*
type METHOD_FUNC = VALUE, VALUE -> VALUE
$rb_cObject : VALUE
fun rb_num2int(value : VALUE) : Int32
fun rb_int2inum(value : Int32) : VALUE
fun rb_define_class(name: UInt8*, super: VALUE) : VALUE
fun rb_define_method(klass: VALUE, name: UInt8*, func: METHOD_FUNC, argc: Int32)
end
def fibonacci_cr_wrapper(self : CrRuby::VALUE, value : CrRuby::VALUE)
int_value = CrRuby.rb_num2int(value)
CrRuby.rb_int2inum(fibonacci_cr(int_value))
end
def fibonacci_cr(n)
n <= 1 ? n : fibonacci_cr(n - 1) + fibonacci_cr(n - 2)
end
fun init = Init_cr_math
GC.init
LibCrystalMain.__crystal_main(0, Pointer(Pointer(UInt8)).null)
cr_math = CrRuby.rb_define_class("CrMath", CrRuby.rb_cObject)
CrRuby.rb_define_method(cr_math, "fibonacci", ->fibonacci_cr_wrapper, 1)
end
Crystal extension
Ready to compile
Makefile
CRYSTAL = crystal
UNAME = "$(shell uname -ms)"
LIBRARY_PATH = $(shell brew --prefix crystal-lang)/embedded/lib
TARGET = cr_math.bundle
$(TARGET): cr_math.o
$(CC) -bundle -L$(LIBRARY_PATH) -o $@ $^
cr_math.o: cr_math.cr
$(CRYSTAL) build --cross-compile $(UNAME) $<
$ irb
2.1.6 :001 > require './cr_math'
=> true
2.1.6 :002 > CrMath.new.fibonacci(20)
=> 6765
Crystal extension
Using in Ruby
# benchmark.rb
require 'benchmark'
require './cr_math' # We have compiled cr_math.bundle
require './rb_math'
iterations = 10_000
number = 20
Benchmark.bm do |bm|
bm.report("rb") do
iterations.times { RbMath.new.fibonacci(number) }
end
bm.report("cr") do
iterations.times { CrMath.new.fibonacci(number) }
end
end
# rb_math.rb
class RbMath
def fibonacci(n)
return n if n <= 1
fibonacci(n - 1) +
fibonacci(n - 2)
end
end
Crystal extension
Benchmarking
# benchmark.rb
require 'benchmark'
require './cr_math' # We have compiled cr_math.bundle
require './rb_math'
iterations = 10_000
number = 20
Benchmark.bm do |bm|
bm.report("rb") do
iterations.times { RbMath.new.fibonacci(number) }
end
bm.report("cr") do
iterations.times { CrMath.new.fibonacci(number) }
end
end
# rb_math.rb
class RbMath
def fibonacci(n)
return n if n <= 1
fibonacci(n - 1) +
fibonacci(n - 2)
end
end
Crystal extension
Benchmarking
$ ruby benchmark.rb
user system total real
rb 10.270000 0.030000 10.300000 ( 10.314595)
cr 0.460000 0.000000 0.460000 ( 0.464087)
Crystal extension
Benchmarking
Ta-Da!
Thank you
https://twitter.com/gaar4ica
https://github.com/gaar4ica/ruby_ext_in_crystal_math
http://crystal-lang.org/
https://github.com/manastech/crystal
https://github.com/5t111111/ruby_extension_with_crystal
http://blog.jacius.info/ruby-c-extension-cheat-sheet/

Más contenido relacionado

Destacado

Destacado (17)

Lluvias en el norte 24.03.17
Lluvias en el norte 24.03.17Lluvias en el norte 24.03.17
Lluvias en el norte 24.03.17
 
kintone Café Akita Vol.1 対面開発
kintone Café Akita Vol.1 対面開発kintone Café Akita Vol.1 対面開発
kintone Café Akita Vol.1 対面開発
 
Gender gap in public speaking
Gender gap in public speakingGender gap in public speaking
Gender gap in public speaking
 
パケットが教えてくれた ルートサーバが 13個の理由
パケットが教えてくれた ルートサーバが 13個の理由パケットが教えてくれた ルートサーバが 13個の理由
パケットが教えてくれた ルートサーバが 13個の理由
 
Emergencias oncológicas (Diplomado UniRemington) Parte 4/6
Emergencias oncológicas (Diplomado UniRemington) Parte 4/6Emergencias oncológicas (Diplomado UniRemington) Parte 4/6
Emergencias oncológicas (Diplomado UniRemington) Parte 4/6
 
Quimioterapia paliativa (Diplomado UniRemington) Parte 5c/6
Quimioterapia paliativa (Diplomado UniRemington) Parte 5c/6Quimioterapia paliativa (Diplomado UniRemington) Parte 5c/6
Quimioterapia paliativa (Diplomado UniRemington) Parte 5c/6
 
RUNNING Smalltalk - 実践Smalltalk
RUNNING Smalltalk - 実践SmalltalkRUNNING Smalltalk - 実践Smalltalk
RUNNING Smalltalk - 実践Smalltalk
 
Open Source Insight: CVE-2017-2636 Vuln of the Week & UK National Cyber Secur...
Open Source Insight: CVE-2017-2636 Vuln of the Week & UK National Cyber Secur...Open Source Insight: CVE-2017-2636 Vuln of the Week & UK National Cyber Secur...
Open Source Insight: CVE-2017-2636 Vuln of the Week & UK National Cyber Secur...
 
Peter Allor - The New Era of Cognitive Security
Peter Allor - The New Era of Cognitive SecurityPeter Allor - The New Era of Cognitive Security
Peter Allor - The New Era of Cognitive Security
 
Spiders, Chatbots, and the Future of Metadata: A look inside the BNC BiblioSh...
Spiders, Chatbots, and the Future of Metadata: A look inside the BNC BiblioSh...Spiders, Chatbots, and the Future of Metadata: A look inside the BNC BiblioSh...
Spiders, Chatbots, and the Future of Metadata: A look inside the BNC BiblioSh...
 
Conference entreprise libérée
Conference entreprise libéréeConference entreprise libérée
Conference entreprise libérée
 
はじめてのgithub
はじめてのgithubはじめてのgithub
はじめてのgithub
 
Ten query tuning techniques every SQL Server programmer should know
Ten query tuning techniques every SQL Server programmer should knowTen query tuning techniques every SQL Server programmer should know
Ten query tuning techniques every SQL Server programmer should know
 
Designing for Diversity in Design Orgs (Presentation)
Designing for Diversity in Design Orgs (Presentation)Designing for Diversity in Design Orgs (Presentation)
Designing for Diversity in Design Orgs (Presentation)
 
iostat await svctm の 見かた、考え方
iostat await svctm の 見かた、考え方iostat await svctm の 見かた、考え方
iostat await svctm の 見かた、考え方
 
JAWSDAYS2017 新訳 とあるアーキテクトのクラウドデザインパターン目録 AMI Maintenance Environment
JAWSDAYS2017 新訳 とあるアーキテクトのクラウドデザインパターン目録 AMI Maintenance EnvironmentJAWSDAYS2017 新訳 とあるアーキテクトのクラウドデザインパターン目録 AMI Maintenance Environment
JAWSDAYS2017 新訳 とあるアーキテクトのクラウドデザインパターン目録 AMI Maintenance Environment
 
spc2010-16_BICMAC TIME 13:16
spc2010-16_BICMAC TIME 13:16spc2010-16_BICMAC TIME 13:16
spc2010-16_BICMAC TIME 13:16
 

Similar a How to write Ruby extensions with Crystal

A tour on ruby and friends
A tour on ruby and friendsA tour on ruby and friends
A tour on ruby and friends
旻琦 潘
 
Lightening Talk: Model Cat Can Haz Scope?
Lightening Talk: Model Cat Can Haz Scope?Lightening Talk: Model Cat Can Haz Scope?
Lightening Talk: Model Cat Can Haz Scope?
evenellie
 
Metaprogramovanie #1
Metaprogramovanie #1Metaprogramovanie #1
Metaprogramovanie #1
Jano Suchal
 
C Sharp: Basic to Intermediate Part 01
C Sharp: Basic to Intermediate Part 01C Sharp: Basic to Intermediate Part 01
C Sharp: Basic to Intermediate Part 01
Zafor Iqbal
 

Similar a How to write Ruby extensions with Crystal (20)

Ruby C extensions at the Ruby drink-up of Sophia, April 2012
Ruby C extensions at the Ruby drink-up of Sophia, April 2012Ruby C extensions at the Ruby drink-up of Sophia, April 2012
Ruby C extensions at the Ruby drink-up of Sophia, April 2012
 
Ruby Internals
Ruby InternalsRuby Internals
Ruby Internals
 
06 ruby variables
06 ruby variables06 ruby variables
06 ruby variables
 
iOS Session-2
iOS Session-2iOS Session-2
iOS Session-2
 
05 ruby classes
05 ruby classes05 ruby classes
05 ruby classes
 
Ruby/Rails
Ruby/RailsRuby/Rails
Ruby/Rails
 
Ruby training day1
Ruby training day1Ruby training day1
Ruby training day1
 
Object Trampoline: Why having not the object you want is what you need.
Object Trampoline: Why having not the object you want is what you need.Object Trampoline: Why having not the object you want is what you need.
Object Trampoline: Why having not the object you want is what you need.
 
Attributes Unwrapped: Lessons under the surface of active record
Attributes Unwrapped: Lessons under the surface of active recordAttributes Unwrapped: Lessons under the surface of active record
Attributes Unwrapped: Lessons under the surface of active record
 
A tour on ruby and friends
A tour on ruby and friendsA tour on ruby and friends
A tour on ruby and friends
 
Ruby: Beyond the Basics
Ruby: Beyond the BasicsRuby: Beyond the Basics
Ruby: Beyond the Basics
 
Introduction to the Ruby Object Model
Introduction to the Ruby Object ModelIntroduction to the Ruby Object Model
Introduction to the Ruby Object Model
 
Oops concepts in php
Oops concepts in phpOops concepts in php
Oops concepts in php
 
Iq rails
Iq railsIq rails
Iq rails
 
Lightening Talk: Model Cat Can Haz Scope?
Lightening Talk: Model Cat Can Haz Scope?Lightening Talk: Model Cat Can Haz Scope?
Lightening Talk: Model Cat Can Haz Scope?
 
Metaprogramovanie #1
Metaprogramovanie #1Metaprogramovanie #1
Metaprogramovanie #1
 
TDC 2012 - Patterns e Anti-Patterns em Ruby
TDC 2012 - Patterns e Anti-Patterns em RubyTDC 2012 - Patterns e Anti-Patterns em Ruby
TDC 2012 - Patterns e Anti-Patterns em Ruby
 
JavaScript(Es5) Interview Questions & Answers
JavaScript(Es5)  Interview Questions & AnswersJavaScript(Es5)  Interview Questions & Answers
JavaScript(Es5) Interview Questions & Answers
 
Cocoa for Web Developers
Cocoa for Web DevelopersCocoa for Web Developers
Cocoa for Web Developers
 
C Sharp: Basic to Intermediate Part 01
C Sharp: Basic to Intermediate Part 01C Sharp: Basic to Intermediate Part 01
C Sharp: Basic to Intermediate Part 01
 

Último

( Pune ) VIP Baner Call Girls 🎗️ 9352988975 Sizzling | Escorts | Girls Are Re...
( Pune ) VIP Baner Call Girls 🎗️ 9352988975 Sizzling | Escorts | Girls Are Re...( Pune ) VIP Baner Call Girls 🎗️ 9352988975 Sizzling | Escorts | Girls Are Re...
( Pune ) VIP Baner Call Girls 🎗️ 9352988975 Sizzling | Escorts | Girls Are Re...
nilamkumrai
 
➥🔝 7737669865 🔝▻ mehsana Call-girls in Women Seeking Men 🔝mehsana🔝 Escorts...
➥🔝 7737669865 🔝▻ mehsana Call-girls in Women Seeking Men  🔝mehsana🔝   Escorts...➥🔝 7737669865 🔝▻ mehsana Call-girls in Women Seeking Men  🔝mehsana🔝   Escorts...
➥🔝 7737669865 🔝▻ mehsana Call-girls in Women Seeking Men 🔝mehsana🔝 Escorts...
nirzagarg
 
pdfcoffee.com_business-ethics-q3m7-pdf-free.pdf
pdfcoffee.com_business-ethics-q3m7-pdf-free.pdfpdfcoffee.com_business-ethics-q3m7-pdf-free.pdf
pdfcoffee.com_business-ethics-q3m7-pdf-free.pdf
JOHNBEBONYAP1
 
Low Sexy Call Girls In Mohali 9053900678 🥵Have Save And Good Place 🥵
Low Sexy Call Girls In Mohali 9053900678 🥵Have Save And Good Place 🥵Low Sexy Call Girls In Mohali 9053900678 🥵Have Save And Good Place 🥵
Low Sexy Call Girls In Mohali 9053900678 🥵Have Save And Good Place 🥵
Chandigarh Call girls 9053900678 Call girls in Chandigarh
 
valsad Escorts Service ☎️ 6378878445 ( Sakshi Sinha ) High Profile Call Girls...
valsad Escorts Service ☎️ 6378878445 ( Sakshi Sinha ) High Profile Call Girls...valsad Escorts Service ☎️ 6378878445 ( Sakshi Sinha ) High Profile Call Girls...
valsad Escorts Service ☎️ 6378878445 ( Sakshi Sinha ) High Profile Call Girls...
Call Girls In Delhi Whatsup 9873940964 Enjoy Unlimited Pleasure
 
6.High Profile Call Girls In Punjab +919053900678 Punjab Call GirlHigh Profil...
6.High Profile Call Girls In Punjab +919053900678 Punjab Call GirlHigh Profil...6.High Profile Call Girls In Punjab +919053900678 Punjab Call GirlHigh Profil...
6.High Profile Call Girls In Punjab +919053900678 Punjab Call GirlHigh Profil...
@Chandigarh #call #Girls 9053900678 @Call #Girls in @Punjab 9053900678
 
📱Dehradun Call Girls Service 📱☎️ +91'905,3900,678 ☎️📱 Call Girls In Dehradun 📱
📱Dehradun Call Girls Service 📱☎️ +91'905,3900,678 ☎️📱 Call Girls In Dehradun 📱📱Dehradun Call Girls Service 📱☎️ +91'905,3900,678 ☎️📱 Call Girls In Dehradun 📱
📱Dehradun Call Girls Service 📱☎️ +91'905,3900,678 ☎️📱 Call Girls In Dehradun 📱
@Chandigarh #call #Girls 9053900678 @Call #Girls in @Punjab 9053900678
 
VIP Call Girls Pollachi 7001035870 Whatsapp Number, 24/07 Booking
VIP Call Girls Pollachi 7001035870 Whatsapp Number, 24/07 BookingVIP Call Girls Pollachi 7001035870 Whatsapp Number, 24/07 Booking
VIP Call Girls Pollachi 7001035870 Whatsapp Number, 24/07 Booking
dharasingh5698
 

Último (20)

20240509 QFM015 Engineering Leadership Reading List April 2024.pdf
20240509 QFM015 Engineering Leadership Reading List April 2024.pdf20240509 QFM015 Engineering Leadership Reading List April 2024.pdf
20240509 QFM015 Engineering Leadership Reading List April 2024.pdf
 
( Pune ) VIP Baner Call Girls 🎗️ 9352988975 Sizzling | Escorts | Girls Are Re...
( Pune ) VIP Baner Call Girls 🎗️ 9352988975 Sizzling | Escorts | Girls Are Re...( Pune ) VIP Baner Call Girls 🎗️ 9352988975 Sizzling | Escorts | Girls Are Re...
( Pune ) VIP Baner Call Girls 🎗️ 9352988975 Sizzling | Escorts | Girls Are Re...
 
➥🔝 7737669865 🔝▻ mehsana Call-girls in Women Seeking Men 🔝mehsana🔝 Escorts...
➥🔝 7737669865 🔝▻ mehsana Call-girls in Women Seeking Men  🔝mehsana🔝   Escorts...➥🔝 7737669865 🔝▻ mehsana Call-girls in Women Seeking Men  🔝mehsana🔝   Escorts...
➥🔝 7737669865 🔝▻ mehsana Call-girls in Women Seeking Men 🔝mehsana🔝 Escorts...
 
pdfcoffee.com_business-ethics-q3m7-pdf-free.pdf
pdfcoffee.com_business-ethics-q3m7-pdf-free.pdfpdfcoffee.com_business-ethics-q3m7-pdf-free.pdf
pdfcoffee.com_business-ethics-q3m7-pdf-free.pdf
 
Wagholi & High Class Call Girls Pune Neha 8005736733 | 100% Gennuine High Cla...
Wagholi & High Class Call Girls Pune Neha 8005736733 | 100% Gennuine High Cla...Wagholi & High Class Call Girls Pune Neha 8005736733 | 100% Gennuine High Cla...
Wagholi & High Class Call Girls Pune Neha 8005736733 | 100% Gennuine High Cla...
 
Nanded City ( Call Girls ) Pune 6297143586 Hot Model With Sexy Bhabi Ready ...
Nanded City ( Call Girls ) Pune  6297143586  Hot Model With Sexy Bhabi Ready ...Nanded City ( Call Girls ) Pune  6297143586  Hot Model With Sexy Bhabi Ready ...
Nanded City ( Call Girls ) Pune 6297143586 Hot Model With Sexy Bhabi Ready ...
 
Low Sexy Call Girls In Mohali 9053900678 🥵Have Save And Good Place 🥵
Low Sexy Call Girls In Mohali 9053900678 🥵Have Save And Good Place 🥵Low Sexy Call Girls In Mohali 9053900678 🥵Have Save And Good Place 🥵
Low Sexy Call Girls In Mohali 9053900678 🥵Have Save And Good Place 🥵
 
valsad Escorts Service ☎️ 6378878445 ( Sakshi Sinha ) High Profile Call Girls...
valsad Escorts Service ☎️ 6378878445 ( Sakshi Sinha ) High Profile Call Girls...valsad Escorts Service ☎️ 6378878445 ( Sakshi Sinha ) High Profile Call Girls...
valsad Escorts Service ☎️ 6378878445 ( Sakshi Sinha ) High Profile Call Girls...
 
6.High Profile Call Girls In Punjab +919053900678 Punjab Call GirlHigh Profil...
6.High Profile Call Girls In Punjab +919053900678 Punjab Call GirlHigh Profil...6.High Profile Call Girls In Punjab +919053900678 Punjab Call GirlHigh Profil...
6.High Profile Call Girls In Punjab +919053900678 Punjab Call GirlHigh Profil...
 
Microsoft Azure Arc Customer Deck Microsoft
Microsoft Azure Arc Customer Deck MicrosoftMicrosoft Azure Arc Customer Deck Microsoft
Microsoft Azure Arc Customer Deck Microsoft
 
Busty Desi⚡Call Girls in Vasundhara Ghaziabad >༒8448380779 Escort Service
Busty Desi⚡Call Girls in Vasundhara Ghaziabad >༒8448380779 Escort ServiceBusty Desi⚡Call Girls in Vasundhara Ghaziabad >༒8448380779 Escort Service
Busty Desi⚡Call Girls in Vasundhara Ghaziabad >༒8448380779 Escort Service
 
Call Girls Sangvi Call Me 7737669865 Budget Friendly No Advance BookingCall G...
Call Girls Sangvi Call Me 7737669865 Budget Friendly No Advance BookingCall G...Call Girls Sangvi Call Me 7737669865 Budget Friendly No Advance BookingCall G...
Call Girls Sangvi Call Me 7737669865 Budget Friendly No Advance BookingCall G...
 
📱Dehradun Call Girls Service 📱☎️ +91'905,3900,678 ☎️📱 Call Girls In Dehradun 📱
📱Dehradun Call Girls Service 📱☎️ +91'905,3900,678 ☎️📱 Call Girls In Dehradun 📱📱Dehradun Call Girls Service 📱☎️ +91'905,3900,678 ☎️📱 Call Girls In Dehradun 📱
📱Dehradun Call Girls Service 📱☎️ +91'905,3900,678 ☎️📱 Call Girls In Dehradun 📱
 
All Time Service Available Call Girls Mg Road 👌 ⏭️ 6378878445
All Time Service Available Call Girls Mg Road 👌 ⏭️ 6378878445All Time Service Available Call Girls Mg Road 👌 ⏭️ 6378878445
All Time Service Available Call Girls Mg Road 👌 ⏭️ 6378878445
 
Dubai=Desi Dubai Call Girls O525547819 Outdoor Call Girls Dubai
Dubai=Desi Dubai Call Girls O525547819 Outdoor Call Girls DubaiDubai=Desi Dubai Call Girls O525547819 Outdoor Call Girls Dubai
Dubai=Desi Dubai Call Girls O525547819 Outdoor Call Girls Dubai
 
Hire↠Young Call Girls in Tilak nagar (Delhi) ☎️ 9205541914 ☎️ Independent Esc...
Hire↠Young Call Girls in Tilak nagar (Delhi) ☎️ 9205541914 ☎️ Independent Esc...Hire↠Young Call Girls in Tilak nagar (Delhi) ☎️ 9205541914 ☎️ Independent Esc...
Hire↠Young Call Girls in Tilak nagar (Delhi) ☎️ 9205541914 ☎️ Independent Esc...
 
VIP Call Girls Pollachi 7001035870 Whatsapp Number, 24/07 Booking
VIP Call Girls Pollachi 7001035870 Whatsapp Number, 24/07 BookingVIP Call Girls Pollachi 7001035870 Whatsapp Number, 24/07 Booking
VIP Call Girls Pollachi 7001035870 Whatsapp Number, 24/07 Booking
 
"Boost Your Digital Presence: Partner with a Leading SEO Agency"
"Boost Your Digital Presence: Partner with a Leading SEO Agency""Boost Your Digital Presence: Partner with a Leading SEO Agency"
"Boost Your Digital Presence: Partner with a Leading SEO Agency"
 
Call Girls Ludhiana Just Call 98765-12871 Top Class Call Girl Service Available
Call Girls Ludhiana Just Call 98765-12871 Top Class Call Girl Service AvailableCall Girls Ludhiana Just Call 98765-12871 Top Class Call Girl Service Available
Call Girls Ludhiana Just Call 98765-12871 Top Class Call Girl Service Available
 
VIP Model Call Girls Hadapsar ( Pune ) Call ON 9905417584 Starting High Prof...
VIP Model Call Girls Hadapsar ( Pune ) Call ON 9905417584 Starting  High Prof...VIP Model Call Girls Hadapsar ( Pune ) Call ON 9905417584 Starting  High Prof...
VIP Model Call Girls Hadapsar ( Pune ) Call ON 9905417584 Starting High Prof...
 

How to write Ruby extensions with Crystal

  • 1. How to write Ruby extensions with Crystal @gaar4ica
 Anna Shcherbinina
  • 3. 5 YO, a lot of stuff
  • 4. So, I like Ruby
  • 5. But, sometimes, I need my code to work faster
  • 6. def fibonacci(n) n <= 1 ? n : fibonacci(n - 1) + fibonacci(n - 2) end Case of possible bottle neck
  • 7. Ruby has C bindings
  • 8. C is hard to learn and understand Static types Allocating 
 memory And tons of other complications for Ruby developer
  • 9. I’m ruby developer, I don’t want to write my code in C
  • 10. I’m ruby developer, I want to write my code in Ruby. But to be honest… Node.js NET OWL PDF XML Redis C++ Scala BASIC C#
  • 11. I’m ruby developer, I want to write my code in Ruby
  • 12. Also, I like Crystal
  • 14. Guess the language # define class Dog class Dog def initialize(breed, name) @breed = breed @name = name end def bark puts 'Ruff! Ruff!' end def display puts "I am of #{@breed} breed and my name is #{@name}" end end
 # make an object d = Dog.new('Labrador', 'Benzy')
  • 15. Crystal compiles to native code Spoiler about crystal
  • 16. So, I like Crystal
  • 17.
  • 21. C layer Link Crystal classes and methods to Ruby, so Ruby knows about them Translate 
 Ruby-types 
 to Crystal-types
  • 23. WUT? Compile to efficient 
 native code Statically 
 type-checked Ruby-inspired 
 syntax C bindings
  • 24. Web Frameworks • amatista • amethyst • kemal • moonshine Testing Search CacheDBDDBD Third-party 
 APIs Libs
  • 25. Ready for production or not? Web apps Microservice • maths calculation • small but heavy parts of projects What for
  • 26. Not so like ruby
  • 27. if @a # here @a can be nil end if @a.is_a?(String) # here @a is not guaranteed to be a String end if @a.responds_to?(:abs) # here @a is not guaranteed to respond to `abs` end The below doesn’t work with instance variables, class variables or global variables. Variables Not so like ruby
  • 28. To work with these, first assign them to a variable: if a = @a # here a can't be nil end # Second option: use `Object#try` found in the standard library @a.try do |a| # here a can't be nil end if (a = @a).is_a?(String) # here a is guaranteed to be a String end Variables Not so like ruby
  • 29. Classes and methods Not so like ruby Overloading Return types Visibility Finalize
  • 30. Macros Not so like ruby class Object macro def instance_vars_names : Array(String) {{ @type.instance_vars.map &.name.stringify }} end end class Person def initialize(@name, @age) end end person = Person.new "John", 30 person.instance_vars_names #=> ["name", "age"]
  • 31. Threads Not so like ruby def generate(chan) i = 2 loop do chan.send(i) i += 1 end end def filter(in_chan, out_chan, prime) loop do i = in_chan.receive if i % prime != 0 out_chan.send(i) end end end ch = Channel(Int32).new spawn generate(ch) 100.times do prime = ch.receive puts prime ch1 = Channel(Int32).new spawn filter(ch, ch1, prime) ch = ch1 end
  • 32. Benchmarking Time (s) Go Erlang Crystal Scala Ruby C Crystal Ruby C Crystal Ruby 4.084.082.21 27.92 6.025.08 41.73 6.426.091.971.48 Memory (Mb) Go Erlang Crystal Scala Ruby C Crystal Ruby C Crystal Ruby 162.40 1.400.40 115.50107.60 32.4030.00 1.505.3017.10 2.00 Threadring Binarytree Fast Threadring Binarytree Fast
  • 33. Back to case of possible bottle neck def fibonacci(n) n <= 1 ? n : fibonacci(n - 1) + fibonacci(n - 2) end
  • 35. Less work for C Passing primitive data structures: • integer • string • boolean • float / decimal • etc. No allocation memory for complex data types Don’t do a lot of work for casting variables passed as attributes
  • 36. Ruby C API Entry point Then every C extension has to implement a function named Init_xxxxx (xxxxx being your extension’s name). It is being executed during the “require” of your extension. void Init_xxxxx() { // Code executed during "require" }
  • 37. Ruby C API Types Ruby C API defines a bunch of handful C constants defining standard Ruby objects: C Ruby Qnil nil Qtrue TRUE Qfalse FALSE rb_cObject Object rb_mKernel Kernel rb_cString String
  • 38. Ruby C API Declaring modules and classes // Creating classes // rb_define_class creates a new class named name // and inheriting from super. // The return value is a handle to the new class. VALUE rb_define_class(const char *name, VALUE super); // Creating modules // rb_define_module defines a module whose name // is the string name. // The return value is a handle to the module. VALUE rb_define_module(const char *name);
  • 39. Ruby C API Declaring methods // Creating a method // rb_define_method defines a new instance method in the class // or moduleklass. // The method calls func with argc arguments. // They differ in how they specify the name - rb_define_method // uses the constant string name void rb_define_method(VALUE klass, const char *name, VALUE (*func)(ANYARGS), int argc); // rb_define_protected_method and rb_define_private_method are similar to rb_define_method, // except that they define protected and private methods, respectively. void rb_define_protected_method(VALUE klass, const char *name, VALUE (*func)(ANYARGS), int argc); void rb_define_private_method(VALUE klass, const char *name, VALUE (*func)(ANYARGS), int argc);
  • 40. Ruby C API Ruby objects → C types // Convert Numeric to integer. long rb_num2int(VALUE obj); // Convert Numeric to unsigned integer. unsigned long rb_num2uint(VALUE obj); // Convert Numeric to double. double rb_num2dbl(VALUE); // Convert Ruby string to a String. VALUE rb_str_to_str(VALUE object); char* rb_string_value_cstr(volatile VALUE* object_variable);
  • 41. Ruby C API C types → Ruby objects // Convert an integer to Fixnum or Bignum. INT2NUM( int ); // convert an unsigned integer to Fixnum or Bignum. UINT2NUM( unsigned int ); // Convert a double to Float. rb_float_new( double ); // Convert a character string to String. rb_str_new2( char* ); // Convert a character string to ID (for Ruby function names, etc.). rb_intern( char* ); // Convert a character string to a ruby Symbol object. ID2SYM( rb_intern(char*) );
  • 42. Ruby C API Simple example # my_class.rb class MyClass def my_method(param1, param2) end end // my_class_ext.c static VALUE myclass_mymethod(VALUE rb_self, VALUE rb_param1, VALUE rb_param2) { // Code executed when calling my_method on an object of class MyClass } void Init_xxxxx() { // Define a new class (inheriting Object) in this module VALUE myclass = rb_define_class("MyClass", rb_cObject); // Define a method in this class, taking 2 arguments, // and using the C method "myclass_method" as its body rb_define_method(myclass, "my_method", myclass_mymethod, 2); }
  • 43. Ruby C API Simple example # my_class.rb class MyClass def my_method(param1, param2) end end // my_class_ext.c static VALUE myclass_mymethod(VALUE rb_self, VALUE rb_param1, VALUE rb_param2) { // Code executed when calling my_method on an object of class MyClass } void Init_xxxxx() { // Define a new class (inheriting Object) in this module VALUE myclass = rb_define_class("MyClass", rb_cObject); // Define a method in this class, taking 2 arguments, // and using the C method "myclass_method" as its body rb_define_method(myclass, "my_method", myclass_mymethod, 2); }
  • 44. Ruby C API Simple example # my_class.rb class MyClass def my_method(param1, param2) end end // my_class_ext.c static VALUE myclass_mymethod(VALUE rb_self, VALUE rb_param1, VALUE rb_param2) { // Code executed when calling my_method on an object of class MyClass } void Init_xxxxx() { // Define a new class (inheriting Object) in this module VALUE myclass = rb_define_class("MyClass", rb_cObject); // Define a method in this class, taking 2 arguments, // and using the C method "myclass_method" as its body rb_define_method(myclass, "my_method", myclass_mymethod, 2); }
  • 45. def fibonacci(n) n <= 1 ? n : fibonacci(n - 1) + fibonacci(n - 2) end Back to case of possible 
 bottle neck And solution C
  • 46. A lib declaration groups C functions and types that belong to 
 a library. Crystal extension It’s easy lib CrRuby type VALUE = Void* $rb_cObject : VALUE end
  • 47. Every ruby object is treated as type VALUE in C. Crystal extension It’s easy lib CrRuby type VALUE = Void* $rb_cObject : VALUE end
  • 48. rb_cObject is a C constant defining standard Ruby Object class. Crystal extension It’s easy lib CrRuby type VALUE = Void* $rb_cObject : VALUE end
  • 49. lib CrRuby type VALUE = Void* $rb_cObject : VALUE fun rb_num2int(value : VALUE) : Int32 fun rb_int2inum(value : Int32) : VALUE end A fun declaration inside a lib binds to a C function. We bind rb_num2int & rb_int2inum, to use it later. Crystal extension It’s easy
  • 50. lib CrRuby type VALUE = Void* type METHOD_FUNC = VALUE, VALUE -> VALUE $rb_cObject : VALUE fun rb_num2int(value : VALUE) : Int32 fun rb_int2inum(value : Int32) : VALUE fun rb_define_class(name: UInt8*, super: VALUE) : VALUE fun rb_define_method(klass: VALUE, name: UInt8*, func: METHOD_FUNC, argc: Int32) end Crystal extension It’s easy We bind rb_define_class & rb_define_method.
  • 51. def fibonacci_cr(n) n <= 1 ? n : fibonacci_cr(n - 1) + fibonacci_cr(n - 2) end Our new and shiny fibonacci method in Crystal. def fibonacci(n) n <= 1 ? n : fibonacci(n - 1) + fibonacci(n - 2) end Do you see the difference with implementation in Ruby? Crystal extension It’s easy
  • 52. Crystal extension It’s easy Let’s create a wrapper for this method, used for: • convert inbound Ruby-type parameter to Crystal; • convert outbound result back to Ruby type. def fibonacci_cr_wrapper(self : CrRuby::VALUE, value : CrRuby::VALUE) int_value = CrRuby.rb_num2int(value) CrRuby.rb_int2inum(fibonacci_cr(int_value)) end
  • 53. Crystal extension It’s easy Now we are done with our functions and C bindings. lib CrRuby type VALUE = Void* type METHOD_FUNC = VALUE, VALUE -> VALUE $rb_cObject : VALUE fun rb_num2int(value : VALUE) : Int32 fun rb_int2inum(value : Int32) : VALUE fun rb_define_class(name: UInt8*, super: VALUE) : VALUE fun rb_define_method(klass: VALUE, name: UInt8*, func: METHOD_FUNC, argc: Int32) end def fibonacci_cr_wrapper(self : CrRuby::VALUE, value : CrRuby::VALUE) int_value = CrRuby.rb_num2int(value) CrRuby.rb_int2inum(fibonacci_cr(int_value)) end def fibonacci_cr(n) n <= 1 ? n : fibonacci_cr(n - 1) + fibonacci_cr(n - 2) end
  • 54. Crystal extension It’s easy Now we are done with our functions and C bindings. lib CrRuby type VALUE = Void* type METHOD_FUNC = VALUE, VALUE -> VALUE $rb_cObject : VALUE fun rb_num2int(value : VALUE) : Int32 fun rb_int2inum(value : Int32) : VALUE fun rb_define_class(name: UInt8*, super: VALUE) : VALUE fun rb_define_method(klass: VALUE, name: UInt8*, func: METHOD_FUNC, argc: Int32) end def fibonacci_cr_wrapper(self : CrRuby::VALUE, value : CrRuby::VALUE) int_value = CrRuby.rb_num2int(value) CrRuby.rb_int2inum(fibonacci_cr(int_value)) end def fibonacci_cr(n) n <= 1 ? n : fibonacci_cr(n - 1) + fibonacci_cr(n - 2) end
  • 55. Crystal extension It’s easy Now we are done with our functions and C bindings. lib CrRuby type VALUE = Void* type METHOD_FUNC = VALUE, VALUE -> VALUE $rb_cObject : VALUE fun rb_num2int(value : VALUE) : Int32 fun rb_int2inum(value : Int32) : VALUE fun rb_define_class(name: UInt8*, super: VALUE) : VALUE fun rb_define_method(klass: VALUE, name: UInt8*, func: METHOD_FUNC, argc: Int32) end def fibonacci_cr_wrapper(self : CrRuby::VALUE, value : CrRuby::VALUE) int_value = CrRuby.rb_num2int(value) CrRuby.rb_int2inum(fibonacci_cr(int_value)) end def fibonacci_cr(n) n <= 1 ? n : fibonacci_cr(n - 1) + fibonacci_cr(n - 2) end
  • 56. Crystal extension It’s easy Now we are done with our functions and C bindings. lib CrRuby type VALUE = Void* type METHOD_FUNC = VALUE, VALUE -> VALUE $rb_cObject : VALUE fun rb_num2int(value : VALUE) : Int32 fun rb_int2inum(value : Int32) : VALUE fun rb_define_class(name: UInt8*, super: VALUE) : VALUE fun rb_define_method(klass: VALUE, name: UInt8*, func: METHOD_FUNC, argc: Int32) end def fibonacci_cr_wrapper(self : CrRuby::VALUE, value : CrRuby::VALUE) int_value = CrRuby.rb_num2int(value) CrRuby.rb_int2inum(fibonacci_cr(int_value)) end def fibonacci_cr(n) n <= 1 ? n : fibonacci_cr(n - 1) + fibonacci_cr(n - 2) end
  • 57. Crystal extension It’s easy We bind init function, the first one to be called. As you remember, a function named Init_xxxxx is being executed during the “require” of your extension. fun init = Init_cr_math end
  • 58. Crystal extension It’s easy Firstly, we start garbage collector. We need to invoke Crystal's "main" function, the one that initializes all constants and runs the top-level code. We pass 0 and null to argc and argv. fun init = Init_cr_math GC.init LibCrystalMain.__crystal_main(0, Pointer(Pointer(UInt8)).null) end
  • 59. Crystal extension It’s easy We define class CrMath. fun init = Init_cr_math GC.init LibCrystalMain.__crystal_main(0, Pointer(Pointer(UInt8)).null) cr_math = CrRuby.rb_define_class("CrMath", CrRuby.rb_cObject) CrRuby.rb_define_method(cr_math, "fibonacci", ->fibonacci_cr_wrapper, 1) end
  • 60. Crystal extension It’s easy We define class CrMath. Attach method fibonacci to class. fun init = Init_cr_math GC.init LibCrystalMain.__crystal_main(0, Pointer(Pointer(UInt8)).null) cr_math = CrRuby.rb_define_class("CrMath", CrRuby.rb_cObject) CrRuby.rb_define_method(cr_math, "fibonacci", ->fibonacci_cr_wrapper, 1) end
  • 61. Crystal extension Ready to compile We have Crystal library with C bindings. lib CrRuby type VALUE = Void* type METHOD_FUNC = VALUE, VALUE -> VALUE $rb_cObject : VALUE fun rb_num2int(value : VALUE) : Int32 fun rb_int2inum(value : Int32) : VALUE fun rb_define_class(name: UInt8*, super: VALUE) : VALUE fun rb_define_method(klass: VALUE, name: UInt8*, func: METHOD_FUNC, argc: Int32) end def fibonacci_cr_wrapper(self : CrRuby::VALUE, value : CrRuby::VALUE) int_value = CrRuby.rb_num2int(value) CrRuby.rb_int2inum(fibonacci_cr(int_value)) end def fibonacci_cr(n) n <= 1 ? n : fibonacci_cr(n - 1) + fibonacci_cr(n - 2) end fun init = Init_cr_math GC.init LibCrystalMain.__crystal_main(0, Pointer(Pointer(UInt8)).null) cr_math = CrRuby.rb_define_class("CrMath", CrRuby.rb_cObject) CrRuby.rb_define_method(cr_math, "fibonacci", ->fibonacci_cr_wrapper, 1) end
  • 62. Crystal extension Ready to compile We have method fibonacci_cr and wrapper for it. lib CrRuby type VALUE = Void* type METHOD_FUNC = VALUE, VALUE -> VALUE $rb_cObject : VALUE fun rb_num2int(value : VALUE) : Int32 fun rb_int2inum(value : Int32) : VALUE fun rb_define_class(name: UInt8*, super: VALUE) : VALUE fun rb_define_method(klass: VALUE, name: UInt8*, func: METHOD_FUNC, argc: Int32) end def fibonacci_cr_wrapper(self : CrRuby::VALUE, value : CrRuby::VALUE) int_value = CrRuby.rb_num2int(value) CrRuby.rb_int2inum(fibonacci_cr(int_value)) end def fibonacci_cr(n) n <= 1 ? n : fibonacci_cr(n - 1) + fibonacci_cr(n - 2) end fun init = Init_cr_math GC.init LibCrystalMain.__crystal_main(0, Pointer(Pointer(UInt8)).null) cr_math = CrRuby.rb_define_class("CrMath", CrRuby.rb_cObject) CrRuby.rb_define_method(cr_math, "fibonacci", ->fibonacci_cr_wrapper, 1) end
  • 63. Crystal extension Ready to compile We have entry point. lib CrRuby type VALUE = Void* type METHOD_FUNC = VALUE, VALUE -> VALUE $rb_cObject : VALUE fun rb_num2int(value : VALUE) : Int32 fun rb_int2inum(value : Int32) : VALUE fun rb_define_class(name: UInt8*, super: VALUE) : VALUE fun rb_define_method(klass: VALUE, name: UInt8*, func: METHOD_FUNC, argc: Int32) end def fibonacci_cr_wrapper(self : CrRuby::VALUE, value : CrRuby::VALUE) int_value = CrRuby.rb_num2int(value) CrRuby.rb_int2inum(fibonacci_cr(int_value)) end def fibonacci_cr(n) n <= 1 ? n : fibonacci_cr(n - 1) + fibonacci_cr(n - 2) end fun init = Init_cr_math GC.init LibCrystalMain.__crystal_main(0, Pointer(Pointer(UInt8)).null) cr_math = CrRuby.rb_define_class("CrMath", CrRuby.rb_cObject) CrRuby.rb_define_method(cr_math, "fibonacci", ->fibonacci_cr_wrapper, 1) end
  • 64. Crystal extension Ready to compile Makefile CRYSTAL = crystal UNAME = "$(shell uname -ms)" LIBRARY_PATH = $(shell brew --prefix crystal-lang)/embedded/lib TARGET = cr_math.bundle $(TARGET): cr_math.o $(CC) -bundle -L$(LIBRARY_PATH) -o $@ $^ cr_math.o: cr_math.cr $(CRYSTAL) build --cross-compile $(UNAME) $<
  • 65. $ irb 2.1.6 :001 > require './cr_math' => true 2.1.6 :002 > CrMath.new.fibonacci(20) => 6765 Crystal extension Using in Ruby
  • 66. # benchmark.rb require 'benchmark' require './cr_math' # We have compiled cr_math.bundle require './rb_math' iterations = 10_000 number = 20 Benchmark.bm do |bm| bm.report("rb") do iterations.times { RbMath.new.fibonacci(number) } end bm.report("cr") do iterations.times { CrMath.new.fibonacci(number) } end end # rb_math.rb class RbMath def fibonacci(n) return n if n <= 1 fibonacci(n - 1) + fibonacci(n - 2) end end Crystal extension Benchmarking
  • 67. # benchmark.rb require 'benchmark' require './cr_math' # We have compiled cr_math.bundle require './rb_math' iterations = 10_000 number = 20 Benchmark.bm do |bm| bm.report("rb") do iterations.times { RbMath.new.fibonacci(number) } end bm.report("cr") do iterations.times { CrMath.new.fibonacci(number) } end end # rb_math.rb class RbMath def fibonacci(n) return n if n <= 1 fibonacci(n - 1) + fibonacci(n - 2) end end Crystal extension Benchmarking
  • 68. $ ruby benchmark.rb user system total real rb 10.270000 0.030000 10.300000 ( 10.314595) cr 0.460000 0.000000 0.460000 ( 0.464087) Crystal extension Benchmarking Ta-Da!