Ce diaporama a bien été signalé.
Nous utilisons votre profil LinkedIn et vos données d’activité pour vous proposer des publicités personnalisées et pertinentes. Vous pouvez changer vos préférences de publicités à tout moment.

Rust's Journey to Async/await

76 vues

Publié le

Video and slides synchronized, mp3 and slide download available at URL http://bit.ly/2PeX3wC.

Steve Klabnik gives an overview of Rust’s history, diving into the technical details of how the design has changed, and talks about the difficulties of adding a major new feature to a programming language. Filmed at qconnewyork.com.

Steve Klabnik is on the core team of Rust, leads the documentation team, and is an author of "The Rust Programming Language”. He is a frequent speaker at conferences and is a prolific open source contributor, previously working on projects such as Ruby and Ruby on Rails.

Publié dans : Technologie
  • Soyez le premier à commenter

  • Soyez le premier à aimer ceci

Rust's Journey to Async/await

  1. 1. Rust’s Journey to Async/Await Steve Klabnik
  2. 2. InfoQ.com: News & Community Site • 750,000 unique visitors/month • Published in 4 languages (English, Chinese, Japanese and Brazilian Portuguese) • Post content from our QCon conferences • News 15-20 / week • Articles 3-4 / week • Presentations (videos) 12-15 / week • Interviews 2-3 / week • Books 1 / month Watch the video with slide synchronization on InfoQ.com! https://www.infoq.com/presentations/ rust-2019/
  3. 3. Presented at QCon New York www.qconnewyork.com Purpose of QCon - to empower software development by facilitating the spread of knowledge and innovation Strategy - practitioner-driven conference designed for YOU: influencers of change and innovation in your teams - speakers and topics driving the evolution and innovation - connecting and catalyzing the influencers and innovators Highlights - attended by more than 12,000 delegates since 2007 - held in 9 cities worldwide
  4. 4. Hi, I’m Steve! ● On the Rust team ● Work at Cloudflare ● Doing two workshops!
  5. 5. What is async? Parallel: do multiple things at once Concurrent: do multiple things, not at once Asynchronous: actually unrelated! Sort of...
  6. 6. “Task” A generic term for some computation running in a parallel or concurrent system
  7. 7. Parallel Only possible with multiple cores or CPUs
  8. 8. Concurrent Pretend that you have multiple cores or CPUs
  9. 9. Asynchronous A word we use to describe language features that enable parallelism and/or concurrency
  10. 10. Even more terminology
  11. 11. Cooperative vs Preemptive Multitasking
  12. 12. Cooperative Multitasking Each task decides when to yield to other tasks
  13. 13. Preemptive Multitasking The system decides when to yield to other tasks
  14. 14. Native vs green threads
  15. 15. Native threads Tasks provided by the operating system Sometimes called “1:1 threading”
  16. 16. Green Threads Tasks provided by your programming language Sometimes called “N:M threading”
  17. 17. Native vs Green threads Native thread advantages: ● Part of your system; OS handles scheduling ● Very straightforward, well-understood Native thread disadvantages: ● Defaults can be sort of heavy ● Relatively limited number you can create Green thread advantages: ● Not part of the overall system; runtime handles scheduling ● Lighter weight, can create many, many, many, many green threads Green thread disadvantages: ● Stack growth can cause issues ● Overhead when calling into C
  18. 18. Why do we care?
  19. 19. Apache “Pre-fork” Control Process Child Process
  20. 20. Apache “worker” Control Process Child Process Child Thread Child Thread Child Thread Child Thread Thread pool
  21. 21. Let’s talk about Rust
  22. 22. Rust was built to enhance Firefox, which is an HTTP client, not server
  23. 23. “Synchronous, non-blocking network I/O”
  24. 24. Isn’t this a contradiction in terms?
  25. 25. Synchronous Asynchronous Blocking Old-school implementations Doesn’t make sense Non-blocking Go, Ruby Node.js
  26. 26. Tons of options Synchronous, blocking ● Your code looks like it blocks, and it does block ● Very basic and straightforward Asynchronous, non-blocking ● Your code looks like it doesn’t block, and it doesn’t block ● Harder to write Synchronous, non-blocking ● Your code looks like it blocks, but it doesn’t! ● The secret: the runtime is non-blocking ● Your code still looks straightforward, but you get performance benefits ● A common path for languages built on synchronous, blocking I/O to gain performance while retaining compatibility
  27. 27. Not all was well in Rust-land
  28. 28. A “systems programming language” that doesn’t let you use the system’s threads?
  29. 29. Not all was well in Rust-land
  30. 30. Rust 1.0 was approaching
  31. 31. Ship the minimal thing that we know is good
  32. 32. Rust 1.0 was released! 🎉
  33. 33. … but still, not all was well in Rust-land
  34. 34. People 💖 Rust
  35. 35. People want to build network services in Rust
  36. 36. Rust is supposed to be a high-performance language
  37. 37. Rust’s I/O model feels retro, and not performant
  38. 38. The big problem with native threads for I/O
  39. 39. CPU bound vs I/O bound
  40. 40. CPU Bound The speed of completing a task is based on the CPU crunching some numbers My processor is working hard
  41. 41. I/O Bound The speed of completing a task is based on doing a lot of input and output Doing a lot of networking
  42. 42. When you’re doing a lot of I/O, you’re doing a lot of waiting
  43. 43. When you’re doing a lot of waiting, you’re tying up system resources
  44. 44. Go Asynchronous I/O with green threads (Erlang does this too) Main Process Child Thread Child Thread Child Thread Child Thread Green threads
  45. 45. Native vs Green threads Native thread advantages: ● Part of your system; OS handles scheduling ● Very straightforward, well-understood Native thread disadvantages: ● Defaults can be sort of heavy ● Relatively limited number you can create Green thread advantages: ● Not part of the overall system; runtime handles scheduling ● Lighter weight, can create many, many, many, many green threads Green thread disadvantages: ● Stack growth can cause issues ● Overhead when calling into C PREVIOUSLY
  46. 46. A “systems programming language” that has overhead when calling into C code?
  47. 47. Luckily, there is another way
  48. 48. Nginx Asynchronous I/O Event Loop
  49. 49. Evented I/O requires non-blocking APIs
  50. 50. Blocking vs non-blocking
  51. 51. “Callback hell”
  52. 52. Promises let myFirstPromise = new Promise((resolve, reject) => { setTimeout(function(){ resolve("Success!"); }, 250); }); myFirstPromise.then((successMessage) => { console.log("Yay! " + successMessage); });
  53. 53. Promises let myFirstPromise = new Promise((resolve, reject) => { setTimeout(function(){ resolve("Success!"); }, 250); }); myFirstPromise.then((successMessage) => { console.log("Yay! " + successMessage); }).then((...) => { // }).then((...) => { // });
  54. 54. Futures 0.1 pub trait Future { type Item; type Error; fn poll(&mut self) -> Poll<Self::Item, Self::Error>; } id_rpc(&my_server).and_then(|id| { get_row(id) }).map(|row| { json::encode(row) }).and_then(|encoded| { write_string(my_socket, encoded) })
  55. 55. Promises and Futures are different! ● Promises are built into JavaScript ● The language has a runtime ● This means that Promises start executing upon creation ● This feels simpler, but has some drawbacks, namely, lots of allocations ● Futures are not built into Rust ● The language has no runtime ● This means that you must submit your futures to an executor to start execution ● Futures are inert until their poll method is called by the executor ● This is slightly more complex, but extremely efficient; a single, perfectly sized allocation per task! ● Compiles into the state machine you’d write by hand with evented I/O
  56. 56. Futures 0.1: Executors use tokio; fn main() { let addr = "127.0.0.1:6142".parse().unwrap(); let listener = TcpListener::bind(&addr).unwrap(); let server = listener.incoming().for_each(|socket| { Ok(()) }) .map_err(|err| { println!("accept error = {:?}", err); }); println!("server running on localhost:6142"); tokio::run(server); }
  57. 57. We used Futures 0.1 to build stuff!
  58. 58. The design had some problems
  59. 59. Futures 0.2 trait Future { type Item; type Error; fn poll(&mut self, cx: task::Context) -> Poll<Self::Item, Self::Error>; } No implicit context, no more need for thread local storage.
  60. 60. // with callback request('https://google.com/', (response) => { // handle response }) // with promise request('https://google.com/').then((response) => { // handle response }); // with async/await async function handler() { let response = await request('https://google.com/') // handle response } Async/await
  61. 61. Async/await lets you write code that feels synchronous, but is actually asynchronous
  62. 62. Async/await is more important in Rust than in other languages because Rust has no garbage collector
  63. 63. Rust example: synchronous fn read(&mut self, buf: &mut [u8]) -> Result<usize, io::Error> let mut buf = [0; 1024]; let mut cursor = 0; while cursor < 1024 { cursor += socket.read(&mut buf[cursor..])?; }
  64. 64. Rust example: async with Futures fn read<T: AsMut<[u8]>>(self, buf: T) -> impl Future<Item = (Self, T, usize), Error = (Self, T, io::Error)> … the code is too big to fit on the slide The main problem: the borrow checker doesn’t understand asynchronous code. The constraints on the code when it’s created and when it executes are different.
  65. 65. Rust example: async with async/await async { let mut buf = [0; 1024]; let mut cursor = 0; while cursor < 1024 { cursor += socket.read(&mut buf[cursor..]).await?; }; buf } async/await can teach the borrow checker about these constraints.
  66. 66. Not all futures can error trait Future { type Item; type Error; fn poll(&mut self, cx: task::Context) -> Poll<Self::Item, Self::Error>; }
  67. 67. std::future pub trait Future { type Output; fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output>; } Pin is how async/await teaches the borrow checker. If you need a future that errors, set Output to a Result<T, E>.
  68. 68. … but one more thing...
  69. 69. What syntax for async/await? async is not an issue JavaScript and C# do: await value; But what about ? for error handling? await value?; await (value?); (await value)?;
  70. 70. What syntax for async/await? What about chains of await? (await (await value)?);
  71. 71. We argued and argued and argued and argued and argued and ar...
  72. 72. What syntax for async/await? async { let mut buf = [0; 1024]; let mut cursor = 0; while cursor < 1024 { cursor += socket.read(&mut buf[cursor..]).await?; }; buf } // no errors future.await // with errors future.await?
  73. 73. … there’s actually even one last issue that’s popped up
  74. 74. … this talk is already long enough
  75. 75. Additional Ergonomic improvements use runtime::net::UdpSocket; #[runtime::main] async fn main() -> std::io::Result<()> { let mut socket = UdpSocket::bind("127.0.0.1:8080")?; let mut buf = vec![0u8; 1024]; println!("Listening on {}", socket.local_addr()?); loop { let (recv, peer) = socket.recv_from(&mut buf).await?; let sent = socket.send_to(&buf[..recv], &peer).await?; println!("Sent {} out of {} bytes to {}", sent, recv, peer); } }
  76. 76. WebAssembly?
  77. 77. WebAssembly? Promise Future Promise
  78. 78. Finally landing in Rust 1.37 Or maybe 1.38
  79. 79. Finally landing in Rust 1.37 Or maybe 1.38
  80. 80. Finally landing in Rust 1.38!!!!1
  81. 81. Lesson: a world-class I/O system implementation takes years
  82. 82. Lesson: different languages have different constraints
  83. 83. Thank you! @steveklabnik
  84. 84. Watch the video with slide synchronization on InfoQ.com! https://www.infoq.com/presentations/ rust-2019/

×