+ All Categories
Home > Documents > 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension...

2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension...

Date post: 15-Oct-2020
Category:
Upload: others
View: 14 times
Download: 0 times
Share this document with a friend
54
My first Ruby-Extension written in C Stefan Daschek / @noniq Performance, Performance, Performance!
Transcript
Page 1: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

My first Ruby-Extensionwritten in C

Stefan Daschek / @noniq

Performance, Performance, Performance!

Page 2: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

Why?

Page 3: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

Why?

Page 4: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

Why?

Page 5: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

Why?

Page 6: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

Powered by Ruby!

require "pi_piper" include PiPiper

after pin: $config.gpio_pin, goes: :low do # button pressed, now do all kinds of stuff # (actual code omitted)

sleep 0.5 end

PiPiper.wait

Page 7: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

Under the hood: WiringPi …

Page 8: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

… with PiPiper

Page 9: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

… with PiPiper

🥴

Page 10: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

… with PiPiper

🥴

… also: 🐢 🐢 🐢

Page 11: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

Why is speed relevant?

button pressed

Page 12: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

Why is speed relevant?

button pressed

Page 13: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

Why is speed relevant?

button pressed

Page 14: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

Why is speed relevant?

button pressed

Page 15: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

Why is speed relevant?

button pressed

state unchanged repeatedly

Page 16: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

Let’s make a C extension!Back on track:

Page 17: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

Let’s make a C extension!

Page 18: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

Let’s make a C extension!

Page 19: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

Let’s make a C extension!

Page 20: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

Let’s make a C extension!

Page 21: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

Let’s make a C extension!

Page 22: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

Let’s make a C extension!

Page 23: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

~$ ruby extconf.rb

Page 24: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

~$ ruby extconf.rb

Page 25: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

~$ ruby extconf.rb

#

Page 26: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

#include "ruby.h" #include "extconf.h"

VALUE rb_wait_for_state_change(VALUE self, VALUE pin) { // TODO }

void Init_debounced_gpio() { VALUE module = rb_define_module("DebouncedGPIO"); rb_define_singleton_method( module, "wait_for_state_change", rb_wait_for_state_change, 1 ); }

Minimum Viable C Extension

Page 27: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

#include "ruby.h" #include "extconf.h"

VALUE rb_wait_for_state_change(VALUE self, VALUE pin) { // TODO }

void Init_debounced_gpio() { VALUE module = rb_define_module("DebouncedGPIO"); rb_define_singleton_method( module, "wait_for_state_change", rb_wait_for_state_change, 1 ); }

debounced_gpio $

Minimum Viable C Extension

Page 28: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

#include "ruby.h" #include "extconf.h"

VALUE rb_wait_for_state_change(VALUE self, VALUE pin) { // TODO }

void Init_debounced_gpio() { VALUE module = rb_define_module("DebouncedGPIO"); rb_define_singleton_method( module, "wait_for_state_change", rb_wait_for_state_change, 1 ); }

debounced_gpio $ make compiling debounced_gpio.clinking shared-object debounced_gpio.bundle

debounced_gpio $

Minimum Viable C Extension

Page 29: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

#include "ruby.h" #include "extconf.h"

VALUE rb_wait_for_state_change(VALUE self, VALUE pin) { // TODO }

void Init_debounced_gpio() { VALUE module = rb_define_module("DebouncedGPIO"); rb_define_singleton_method( module, "wait_for_state_change", rb_wait_for_state_change, 1 ); }

debounced_gpio $ make compiling debounced_gpio.clinking shared-object debounced_gpio.bundle

debounced_gpio $

Minimum Viable C Extension

require_relative "debounced_gpio/debounced_gpio"

DebouncedGPIO.wait_for_state_change(42)

puts "Success!"

Page 30: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

#include "ruby.h" #include "extconf.h"

VALUE rb_wait_for_state_change(VALUE self, VALUE pin) { // TODO }

void Init_debounced_gpio() { VALUE module = rb_define_module("DebouncedGPIO"); rb_define_singleton_method( module, "wait_for_state_change", rb_wait_for_state_change, 1 ); }

debounced_gpio $ make compiling debounced_gpio.clinking shared-object debounced_gpio.bundle

debounced_gpio $

Minimum Viable C Extension

require_relative "debounced_gpio/debounced_gpio"

DebouncedGPIO.wait_for_state_change(42)

puts "Success!"

Page 31: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

Let’s return something:VALUE rb_wait_for_state_change(VALUE self, VALUE rb_pin) {

}

// TODO

Page 32: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

Let’s return something:VALUE rb_wait_for_state_change(VALUE self, VALUE rb_pin) {

}

// TODOreturn INT2NUM(1337);

Page 33: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

Let’s return something:VALUE rb_wait_for_state_change(VALUE self, VALUE rb_pin) {

}

// TODOreturn INT2NUM(1337);

Page 34: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

VALUE rb_wait_for_state_change(VALUE self, VALUE rb_pin) {

}

Let’s return something:VALUE rb_wait_for_state_change(VALUE self, VALUE rb_pin) {

}

// TODOreturn INT2NUM(1337);

Looking for an argument?

Page 35: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

VALUE rb_wait_for_state_change(VALUE self, VALUE rb_pin) {

}

int pin = NUM2INT(rb_pin);

Let’s return something:VALUE rb_wait_for_state_change(VALUE self, VALUE rb_pin) {

}

// TODOreturn INT2NUM(1337);

Looking for an argument?

Page 36: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

VALUE rb_wait_for_state_change(VALUE self, VALUE rb_pin) {

}

int pin = NUM2INT(rb_pin);if (pin == 42) {

Let’s return something:VALUE rb_wait_for_state_change(VALUE self, VALUE rb_pin) {

}

// TODOreturn INT2NUM(1337);

Looking for an argument?

Page 37: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

VALUE rb_wait_for_state_change(VALUE self, VALUE rb_pin) {

}

int pin = NUM2INT(rb_pin);if (pin == 42) {

return INT2NUM(pin * 2);

Let’s return something:VALUE rb_wait_for_state_change(VALUE self, VALUE rb_pin) {

}

// TODOreturn INT2NUM(1337);

Looking for an argument?

Page 38: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

VALUE rb_wait_for_state_change(VALUE self, VALUE rb_pin) {

}

int pin = NUM2INT(rb_pin);if (pin == 42) {

return INT2NUM(pin * 2);}

Let’s return something:VALUE rb_wait_for_state_change(VALUE self, VALUE rb_pin) {

}

// TODOreturn INT2NUM(1337);

Looking for an argument?

Page 39: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

VALUE rb_wait_for_state_change(VALUE self, VALUE rb_pin) {

}

int pin = NUM2INT(rb_pin);if (pin == 42) {

return INT2NUM(pin * 2);}return Qnil;

Let’s return something:VALUE rb_wait_for_state_change(VALUE self, VALUE rb_pin) {

}

// TODOreturn INT2NUM(1337);

Looking for an argument?

Page 40: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

VALUE rb_wait_for_state_change(VALUE self, VALUE rb_pin) {

}

int pin = NUM2INT(rb_pin);if (pin == 42) {

return INT2NUM(pin * 2);}return Qnil;

Let’s return something:VALUE rb_wait_for_state_change(VALUE self, VALUE rb_pin) {

}

// TODOreturn INT2NUM(1337);

Looking for an argument?

Page 41: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

VALUE rb_wait_for_state_change(VALUE self, VALUE rb_pin) {

}

int pin = NUM2INT(rb_pin); if (pin == 42) { return INT2NUM(pin * 2); } return Qnil;

Let’s return something:VALUE rb_wait_for_state_change(VALUE self, VALUE rb_pin) {

}

// TODOreturn INT2NUM(1337);

Looking for an argument?

Page 42: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

Documentation: github.com/ruby/ruby/blob/master/doc/extension.rdoc

Page 43: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

Documentation: github.com/ruby/ruby/blob/master/doc/extension.rdoc

Page 44: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

Documentation: github.com/ruby/ruby/blob/master/doc/extension.rdoc

Page 45: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

Documentation: github.com/ruby/ruby/blob/master/doc/extension.rdoc

Page 46: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

#include "ruby.h" #include "extconf.h"

VALUE rb_wait_for_state_change(VALUE self, VALUE pin) { while(1); }

void Init_debounced_gpio() {

VALUE module = rb_define_module("DebouncedGPIO"); rb_define_singleton_method(module, "wait_for_state_change", rb_wait_for_state_change, 1); }

Odds and ends: Pardon the interruption!

Page 47: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

#include "ruby.h" #include "extconf.h"

VALUE rb_wait_for_state_change(VALUE self, VALUE pin) { while(1); }

void Init_debounced_gpio() {

VALUE module = rb_define_module("DebouncedGPIO"); rb_define_singleton_method(module, "wait_for_state_change", rb_wait_for_state_change, 1); }

Odds and ends: Pardon the interruption!

signal(SIGINT, handle_signal); signal(SIGTERM, handle_signal);

Page 48: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

#include "ruby.h" #include "extconf.h"

VALUE rb_wait_for_state_change(VALUE self, VALUE pin) { while(1); }

void Init_debounced_gpio() {

VALUE module = rb_define_module("DebouncedGPIO"); rb_define_singleton_method(module, "wait_for_state_change", rb_wait_for_state_change, 1); }

Odds and ends: Pardon the interruption!

signal(SIGINT, handle_signal); signal(SIGTERM, handle_signal);

void handle_signal(int sig) { if (sig == SIGINT) { rb_interrupt(); } else { ruby_default_signal(sig); } }

Page 49: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

#include "ruby.h" #include "extconf.h"

VALUE rb_wait_for_state_change(VALUE self, VALUE pin) { while(1); }

void Init_debounced_gpio() {

VALUE module = rb_define_module("DebouncedGPIO"); rb_define_singleton_method(module, "wait_for_state_change", rb_wait_for_state_change, 1); }

Odds and ends: Pardon the interruption!

signal(SIGINT, handle_signal); signal(SIGTERM, handle_signal);

#include <signal.h>

void handle_signal(int sig) { if (sig == SIGINT) { rb_interrupt(); } else { ruby_default_signal(sig); } }

Page 50: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

Odds and ends: Do not block other threads!struct no_gvl_args { int pin; int newState; };

VALUE rb_wait_for_state_change(VALUE self, VALUE pin) { struct no_gvl_args args; args.pin = NUM2INT(pin); rb_thread_call_without_gvl(waitForStateChange, &args, NULL, NULL); return INT2NUM(args.newState); }

Page 51: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

Odds and ends: Do not block other threads!struct no_gvl_args { int pin; int newState; };

VALUE rb_wait_for_state_change(VALUE self, VALUE pin) { struct no_gvl_args args; args.pin = NUM2INT(pin); rb_thread_call_without_gvl(waitForStateChange, &args, NULL, NULL); return INT2NUM(args.newState); }

ruby/thread.c

Page 52: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

Finally – this is what the Ruby code now looks like:

require_relative "debounced_gpio/debounced_gpio"

loop do value = DebouncedGPIO.wait_for_state_change($config.gpio_pin) if value == 1 # button pressed # ... else # button released # ... end end

Page 53: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

Thanks! Questions?Stefan Daschek / @noniq

Page 54: 2020-01 My first Ruby-Extension written in C My first Ruby-Extension... · My first Ruby-Extension ... # button pressed, now do all kinds of stuff # (actual code omitted) sleep 0.5

Actual debouncing routine (in C)int getDebouncedState(int pin) { int lastValue = digitalRead(pin); int debounceCount = 0; while (1) { int value = digitalRead(pin); if (value == lastValue) { debounceCount++; } else { debounceCount = 0; } if (debounceCount == DEBOUNCE_CYCLES) { return value; } lastValue = value; delay(DELAY); } }


Recommended