rust reach f us th es
play

Rust: Reach F us th es ! Nich om as Matsakis 1 Q. What is Rust? Q. - PowerPoint PPT Presentation

Rust: Reach F us th es ! Nich om as Matsakis 1 Q. What is Rust? Q. Why should I care? 2 Q. What is Rust? A. High-level code, low-level performance Q. Why should I care? 3 Decisions GC Control, flexibility


  1. Rust: Reach F us th es ! Nich om as Matsakis 1

  2. Q. What is Rust? Q. Why should I care? 2

  3. Q. What is Rust? A. High-level code, low-level performance Q. Why should I care? 3

  4. Decisions… GC 😁 😖 😏 Control, flexibility 😲 😁 😏 Double free? 😲 😁 😏 Dangling pointers? 😲 😁 😏 Buffer overflow? 😲 😲 😏 Data races? 4

  5. Static type system = Eat your spinach! Photo credit: Sanjoy Ghosh 5 https://www.flickr.com/photos/sanjoy/4016632253/

  6. Static type system = Eat your spinach! Photo credit: Salim Virji 6 https://www.flickr.com/photos/salim/8594532469/

  7. The Rust compiler just saved me from a nasty threading bug. I was working on cage (our open source development tool for Docker apps with lots of microservices), and I decided to parallelize the routine that transformed docker-compose.yml files. 7

  8. Performance Ruby: class ::String 964K iter/sec def blank? /\A[[:space:]]*\z/ == self end end 8

  9. static VALUE case 0x2005: Performance rb_str_blank_as(VALUE str) case 0x2006: { case 0x2007: rb_encoding *enc; case 0x2008: char *s, *e; case 0x2009: case 0x200a: enc = STR_ENC_GET(str); case 0x2028: s = RSTRING_PTR(str); case 0x2029: Ruby: if (!s || RSTRING_LEN(str) == 0) return Qtrue; case 0x202f: case 0x205f: 964K iter/sec e = RSTRING_END(str); case 0x3000: while (s < e) { #if ruby_version_before_2_2() int n; case 0x180e: unsigned int cc = rb_enc_codepoint_len(s, e, &n, enc); #endif 10x! /* found */ switch (cc) { break ; case 9: default : case 0xa: return Qfalse; case 0xb: } C: case 0xc: s += n; case 0xd: } 10.5M iter/sec case 0x20: return Qtrue; case 0x85: } case 0xa0: case 0x1680: case 0x2000: case 0x2001: case 0x2002: case 0x2003: https://github.com/SamSaffron/fast_blank case 0x2004:

  10. Performance Ruby: class ::String 964K iter/sec def blank? /\A[[:space:]]*\z/ == self C: end 10.5M iter/sec end extern “C” fn fast_blank(buf: Buf) -> bool { Rust: buf.as_slice().chars().all(|c| c.is_whitespace()) 11M iter/sec } Get Rust Get iterator over Are all characters string slice each character whitespace? 10

  11. High-level, zero-cost abstractions fn is_whitespace(text: &str) -> bool { text.chars() .all(|c| c.is_whitespace()) } fn load_images(paths: &[PathBuf]) -> Vec<Image> { paths.par_iter() .map(|path| Image::load(path)) .collect() } 11

  12. Q. What is Rust? A. High-level code, low-level performance Q. Why should I care? A. You can do more! 12

  13. Experienced C++ hacker? Make and maintain the designs you always wanted — but could not justify. Prefer Ruby? JavaScript? Tune up your application and address hot-spots. Lower memory usage. Add threads without fear. 13

  14. I like Rust because it is boring . — CJ Silverio, npm CTO 14

  15. Design of Rust Rust in Production Rust Community 15

  16. “Must be this tall to write multi-threaded code” David Baron 
 Mozilla Distinguished Engineer 16

  17. Data races Actor-based languages Sharing (e.g., Erlang, WebWorkers) Functional languages Mutation (e.g., Haskell) No ordering Sequential programming Data race 17

  18. Data races Sharing Rust: No sharing and 
 mutation at the same time. Mutation No ordering Data race 18

  19. Ownership and Borrowing Photo Credit: Nathan Kam https://www.youtube.com/watch?v=Tnssn9KcWLg 19

  20. ~ Ownership and borrowing ~ Type Ownership Alias? Mutate? ✓ T Owned

  21. fn main() { fn eat(apple: Apple) { let apple = Apple::new(); … eat(apple); } Give ownership eat(apple); of the apple. ~~~~~~~~~ Take ownership } Error: `apple` has of the apple been moved. Owns Owns 21

  22. fn main() { fn deliver(bag: Vec<Apple>) { let apple = Apple::new(); … let mut bag = Vec::new(); } bag.push(apple); Give ownership. Take ownership bag.push(Apple::new()); deliver(bag); of the vector Give ownership. } Owns Owns 22

  23. “Manual” memory management in Rust: Values owned by creator . ] Feels invisible. Values moved via assignment. When final owner returns, value is freed . 23

  24. ~ Ownership and borrowing ~ Type Ownership Alias? Mutate? ✓ T Owned ✓ &T Shared reference

  25. fn main() { fn weigh(bag: &Vec<Apple>) -> u32 { let apple = Apple::new(); … let mut bag = Vec::new(); } bag.push(apple); Shared reference bag.push(Apple::new()); to the vector let weight = weigh(&bag); … (Return type) } Loan out the bag shared references 25

  26. Sharing “freezes” data (temporarily) let mut bag = Vec::new(); bag.push(…); `bag` mutable here let r = &bag; `bag` borrowed here bag.len(); reading `bag` ok while shared cannot mutate while shared bag.push(…); ~~~~~~~~~~~ cannot mutate through shared ref r.push(…); ~~~~~~~~~ after last use of `r`, 
 bag.push(…); `bag` is mutable again 26

  27. ~ Ownership and borrowing ~ Type Ownership Alias? Mutate? ✓ T Owned ✓ &T Shared reference ✓ &mut T Mutable reference

  28. Mutable references: no other access let mut bag = Vec::new(); bag.push(…); `bag` mutable here let r = & mut bag; `bag` mutably borrowed here bag.len(); cannot access `bag` while borrowed ~~~~~~~~~ r.push(…); but can mutate through `r` bag.push(…); after last use of `r`, `bag` is accessible again 28

  29. Parallelism Photo credit: Dave Gingrich 29 https://www.flickr.com/photos/ndanger/2744507570/

  30. Observation: Building parallel abstractions is easy. Misusing those abstractions is also easy. func foo(…) { m := make(map[string]string) Go Code m[“Hello”] = “World” send data over channel channel <- m m[“Hello”] = “Data Race” but how to stop sender from 
 } using it afterwards? 30

  31. fn foo(…) { impl <T> Channel<T> { let m = HashMap::new(); fn send(& mut self , data: T) { m.insert(“Hello”, “World”); … channel.send(m); } m.insert(“Hello”, “Data Race”); } ~~~~~~~~~~~~~~~~~~~~~~~~~~ } Take ownership of the data Error: use of moved value: `book` 31

  32. ~ Concurrency paradigms ~ Paradigm Ownership? Borrowing? ✓ Message passing Fork join ✓

  33. borrowed from caller fn load_images(paths: &[PathBuf]) -> Vec<Image> { paths.iter() For each path… .map(|path| { Image::load(path) …load an image… }) .collect() …create and return a vector. } paths = [ “a.jpg”, “b.png”, …, “c.jpg” ] 33

  34. extern crate rayon; Third-party library fn load_images(paths: &[PathBuf]) -> Vec<Image> { paths.par_iter() Make it parallel .map(|path| { Image::load(path) }) .collect() } paths = [ “a.jpg”, “b.png”, …, “c.jpg” ] 34

  35. Observation: Building parallel abstractions is easy. Misusing those abstractions is also easy. 35

  36. fn load_images(paths: &[PathBuf]) -> Vec<Image> { let mut jpgs = 0; How many jpgs seen so far? paths.par_iter() If current file name ends in “jpg”… .map(|path| { if path.ends_with(“.jpg”) { jpgs += 1; } Image::load(path) …add 1 to the counter. }) .collect() } 0 0 + 1 + 1 0 1 1 1 36

  37. fn load_images(paths: &[PathBuf]) -> Vec<Image> { let mut jpgs = 0; paths.par_iter() borrows .map( ) borrows mutably .collect() mutably } |path| { |path| { if path.ends_with(“.jpg”) { if path.ends_with(“.jpg”) { ~~~~~~~~~ ❌ ~~~~~~~~~ ❌ jpgs += 1; jpgs += 1; } } Image::load(path) Image::load(path) } } 37

  38. ~ Concurrency paradigms ~ Paradigm Ownership? Borrowing? ✓ Message passing Fork join ✓ ✓ ✓ Locking ✓ ✓ Lock-free Futures ✓ ✓ …

  39. Unsafe 39

  40. Rust: An Extensible Language File R e f e m r e s n c i e l System e l c l o a u r Libraries n a t P Core Language Ownership and Borrowing 40

  41. Safe abstractions fn split_at_mut(…) { unsafe { … } Validates input, etc. } Trust me. Ownership/borrowing/traits give tools to enforce safe abstraction boundaries. 41

  42. Stylo (Parallel CSS Rendering — coming in FF57) Total KLOC Unsafe KLOC Unsafe % Total 146.2 51.7 35% Interesting stuff 71.6 1.4 1.9% FFI Bindings 74.5 50.3 67.4% 42

  43. Integration Photo credit: Don Urban 43 https://www.flickr.com/photos/donpezzano/3044965125/

  44. node.js Ruby neon-bindings/neon usehelix.com CPython PyO3/pyo3 dgrunwald/rust-cpython getsentry/milksnake 44

  45. Worker ArrayBuffer 45

  46. fn callback( mut vm: CallContext) { ArrayBuffer let buffer = vm.arguments(0); let guard: VmGuard = vm.lock(); let data = buffer.borrow_mut(&guard); data .par_iter_mut() buffer .for_each(|i| *i += 1); data } borrows vm guard data 46

  47. ArrayBuffer What could go wrong? What if we invoked JS callback while using `data`? ▶ Can’t: Invoking JS callbacks requires mutable buffer borrow of `vm`! data borrow vm guard data 47

  48. https://medium.com/@wireapp/3ff37fc98c3f 48

  49. ruby! { class Console { def log(string: &str) { println!("LOG: {:?}", string); } } } 49

  50. Rust in Production 50

  51. Will discuss today! Whitepapers available online. rust-lang.org/en-US/whitepapers.html 51

  52. Category: Experienced system devs 52

  53. 53

  54. 54

Download Presentation
Download Policy: The content available on the website is offered to you 'AS IS' for your personal information and use only. It cannot be commercialized, licensed, or distributed on other websites without prior consent from the author. To download a presentation, simply click this link. If you encounter any difficulties during the download process, it's possible that the publisher has removed the file from their server.

Recommend


More recommend