Writing Perl extensions in Rust

Vickenty Fesunov

Booking.com

http://github.com/vickenty/perl-xs

We're hiring

http://workingatbooking.com/

Perl extensions

Rust

http://rust-lang.org/

  • prevents segfaults
  • compatible with c
  • can do unsafe code if needed
  • safe abstractions

Rust XS

sum array

 1: xs! {
 2:     package Array::Util;
 3: 
 4:     sub sum_array(ctx, array_ref: SV) {
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11:     }
12: }

sum array

 1: xs! {
 2:     package Array::Util;
 3: 
 4:     sub sum_array(ctx, array_ref: SV) {
 5:         let mut sum = 0.0;
 6: 
 7: 
 8: 
 9: 
10:         xs_return!(ctx, sum);
11:     }
12: }

sum array

 1: xs! {
 2:     package Array::Util;
 3: 
 4:     sub sum_array(ctx, array_ref: SV) {
 5:         let mut sum = 0.0;
 6:         let array: AV = array_ref.deref_av();
 7: 
 8: 
 9: 
10:         xs_return!(ctx, sum);
11:     }
12: }

sum array

 1: xs! {
 2:     package Array::Util;
 3: 
 4:     sub sum_array(ctx, array_ref: SV) {
 5:         let mut sum = 0.0;
 6:         let array: AV = array_ref.deref_av();
 7:         // ERROR: AV expected, but have Option<AV>
 8: 
 9: 
10:         xs_return!(ctx, sum);
11:     }
12: }

sum array

 1: xs! {
 2:     package Array::Util;
 3: 
 4:     sub sum_array(ctx, array_ref: SV) {
 5:         let mut sum = 0.0;
 6:         let array_opt: Option<AV> = array_ref.deref_av();
 7: 
 8: 
 9: 
10:         xs_return!(ctx, sum);
11:     }
12: }

sum array

 1: xs! {
 2:     package Array::Util;
 3: 
 4:     sub sum_array(ctx, array_ref: SV) {
 5:         let mut sum = 0.0;
 6:         let array_opt: Option<AV> = array_ref.deref_av();
 7:         let array: AV = array_opt
 8: 
 9: 
10:         xs_return!(ctx, sum);
11:     }
12: }

sum array

 1: xs! {
 2:     package Array::Util;
 3: 
 4:     sub sum_array(ctx, array_ref: SV) {
 5:         let mut sum = 0.0;
 6:         let array_opt: Option<AV> = array_ref.deref_av();
 7:         let array: AV = array_opt.expect("an array reference");
 8: 
 9: 
10:         xs_return!(ctx, sum);
11:     }
12: }

sum array

 1: xs! {
 2:     package Array::Util;
 3: 
 4:     sub sum_array(ctx, array_ref: SV) {
 5:         let mut sum = 0.0;
 6:         let array = array_ref.deref_av().expect("an array reference");
 7: 
 8: 
 9: 
10:         xs_return!(ctx, sum);
11:     }
12: }

sum array

 1: xs! {
 2:     package Array::Util;
 3: 
 4:     sub sum_array(ctx, array_ref: SV) {
 5:         let mut sum = 0.0;
 6:         let array = array_ref.deref_av().expect("an array reference");
 7:         for index in 0...array.top_index() {
 8: 
 9:         }
10:         xs_return!(ctx, sum);
11:     }
12: }

sum array

 1: xs! {
 2:     package Array::Util;
 3: 
 4:     sub sum_array(ctx, array_ref: SV) {
 5:         let mut sum = 0.0;
 6:         let array = array_ref.deref_av().expect("an array reference");
 7:         for index in 0...array.top_index() {
 8:             sum += array.fetch(index);
 9:         }
10:         xs_return!(ctx, sum);
11:     }
12: }

sum array

 1: xs! {
 2:     package Array::Util;
 3: 
 4:     sub sum_array(ctx, array_ref: SV) {
 5:         let mut sum = 0.0;
 6:         let array = array_ref.deref_av().expect("an array reference");
 7:         for index in 0...array.top_index() {
 8:             sum += array.fetch(index); // ERROR: '+= Option' is not
 9:         }                              //        implemented for float
10:         xs_return!(ctx, sum);
11:     }
12: }

sum array

 1: xs! {
 2:     package Array::Util;
 3: 
 4:     sub sum_array(ctx, array_ref: SV) {
 5:         let mut sum = 0.0;
 6:         let array = array_ref.deref_av().expect("an array reference");
 7:         for index in 0...array.top_index() {
 8:             sum += array.fetch(index).unwrap_or(0.0);
 9:         }
10:         xs_return!(ctx, sum);
11:     }
12: }

sum array

 1: xs! {
 2:     package Array::Util;
 3: 
 4:     sub sum_array(ctx, array: AV) {
 5:         let mut sum = 0.0;
 6: 
 7:         for index in 0...array.top_index() {
 8:             sum += array.fetch(index).unwrap_or(0.0);
 9:         }
10:         xs_return!(ctx, sum);
11:     }
12: }

sum array

 1: xs! {
 2:     package Array::Util;
 3: 
 4:     sub sum_array(ctx, array: AV) {
 5:         let sum: NV = array.iter()
 6:             .map(|value| value.unwrap_or(0.0))
 7:             .sum();
 8: 
 9: 
10:         xs_return!(ctx, sum);
11:     }
12: }

Benchmarks

Perl 1.003s 100%
Rust 0.645s 64%
XS 0.164s 16%

Why so slow?

  • reference counting
  • no hot path inlining
  • exception handling

exceptions

Sorry, your browser does not support SVG.

exceptions

Sorry, your browser does not support SVG.

exceptions

Sorry, your browser does not support SVG.

Conclusion

Thanks

  • Booking.com
  • p5pclub
  • #rust and the Rust community

FIN

 1: xs! {
 2:     package Array::Util;
 3: 
 4:     sub sum_array(ctx, array: AV) {
 5:         let sum: NV = array.iter()
 6:             .map(|value| value.unwrap_or(0.0))
 7:             .sum();
 8: 
 9:         xs_return!(ctx, sum);
10:     }
11: }

http://github.com/vickenty/perl-xs