annotate src/capnproto-0.6.0/doc/_posts/2013-12-13-promise-pipelining-capnproto-vs-ice.md @ 81:7029a4916348

Merge build update
author Chris Cannam
date Thu, 31 Oct 2019 13:36:58 +0000
parents 0994c39f1e94
children
rev   line source
cannam@62 1 ---
cannam@62 2 layout: post
cannam@62 3 title: "Promise Pipelining and Dependent Calls: Cap'n Proto vs. Thrift vs. Ice"
cannam@62 4 author: kentonv
cannam@62 5 ---
cannam@62 6
cannam@62 7 _UPDATED: Added Thrift to the comparison._
cannam@62 8
cannam@62 9 So, I totally botched the 0.4 release announcement yesterday. I was excited about promise
cannam@62 10 pipelining, but I wasn't sure how to describe it in headline form. I decided to be a bit
cannam@62 11 silly and call it "time travel", tongue-in-cheek. My hope was that people would then be
cannam@62 12 curious, read the docs, find out that this is actually a really cool feature, and start doing
cannam@62 13 stuff with it.
cannam@62 14
cannam@62 15 Unfortunately, [my post](2013-12-12-capnproto-0.4-time-travel.html) only contained a link to
cannam@62 16 the full explanation and then confusingly followed the "time travel" section with a separate section
cannam@62 17 describing the fact that I had implemented a promise API in C++. Half the readers clicked through
cannam@62 18 to the documentation and understood. The other half thought I was claiming that promises alone
cannam@62 19 constituted "time travel", and thought I was ridiculously over-hyping an already-well-known
cannam@62 20 technique. My HN post was subsequently flagged into oblivion.
cannam@62 21
cannam@62 22 Let me be clear:
cannam@62 23
cannam@62 24 **Promises alone are _not_ what I meant by "time travel"!**
cannam@62 25
cannam@62 26 <img src='{{ site.baseurl }}images/capnp-vs-thrift-vs-ice.png' style='width:350px; height:275px; float: right;'>
cannam@62 27
cannam@62 28 So what did I mean? Perhaps [this benchmark](https://github.com/kentonv/capnp-vs-ice) will
cannam@62 29 make things clearer. Here, I've defined a server that exports a simple four-function calculator
cannam@62 30 interface, with `add()`, `sub()`, `mult()`, and `div()` calls, each taking two integers and\
cannam@62 31 returning a result.
cannam@62 32
cannam@62 33 You are probably already thinking: That's a ridiculously bad way to define an RPC interface!
cannam@62 34 You want to have _one_ method `eval()` that takes an expression tree (or graph, even), otherwise
cannam@62 35 you will have ridiculous latency. But this is exactly the point. **With promise pipelining, simple,
cannam@62 36 composable methods work fine.**
cannam@62 37
cannam@62 38 To prove the point, I've implemented servers in Cap'n Proto, [Apache Thrift](http://thrift.apache.org/),
cannam@62 39 and [ZeroC Ice](http://www.zeroc.com/). I then implemented clients against each one, where the
cannam@62 40 client attempts to evaluate the expression:
cannam@62 41
cannam@62 42 ((5 * 2) + ((7 - 3) * 10)) / (6 - 4)
cannam@62 43
cannam@62 44 All three frameworks support asynchronous calls with a promise/future-like interface, and all of my
cannam@62 45 clients use these interfaces to parallelize calls. However, notice that even with parallelization,
cannam@62 46 it takes four steps to compute the result:
cannam@62 47
cannam@62 48 # Even with parallelization, this takes four steps!
cannam@62 49 ((5 * 2) + ((7 - 3) * 10)) / (6 - 4)
cannam@62 50 (10 + ( 4 * 10)) / 2 # 1
cannam@62 51 (10 + 40) / 2 # 2
cannam@62 52 50 / 2 # 3
cannam@62 53 25 # 4
cannam@62 54
cannam@62 55 As such, the Thrift and Ice clients take four network round trips. Cap'n Proto, however, takes
cannam@62 56 only one.
cannam@62 57
cannam@62 58 Cap'n Proto, you see, sends all six calls from the client to the server at one time. For the
cannam@62 59 latter calls, it simply tells the server to substitute the former calls' results into the new
cannam@62 60 requests, once those dependency calls finish. Typical RPC systems can only send three calls to
cannam@62 61 start, then must wait for some to finish before it can continue with the remaining calls. Over
cannam@62 62 a high-latency connection, this means they take 4x longer than Cap'n Proto to do their work in
cannam@62 63 this test.
cannam@62 64
cannam@62 65 So, does this matter outside of a contrived example case? Yes, it does, because it allows you to
cannam@62 66 write cleaner interfaces with simple, composable methods, rather than monster do-everything-at-once
cannam@62 67 methods. The four-method calculator interface is much simpler than one involving sending an
cannam@62 68 expression graph to the server in one batch. Moreover, pipelining allows you to define
cannam@62 69 object-oriented interfaces where you might otherwise be tempted to settle for singletons. See
cannam@62 70 [my extended argument]({{ site.baseurl }}rpc.html#introduction) (this is what I was trying to get
cannam@62 71 people to click on yesterday :) ).
cannam@62 72
cannam@62 73 Hopefully now it is clearer what I was trying to illustrate with this diagram, and what I meant
cannam@62 74 by "time travel"!
cannam@62 75
cannam@62 76 <img src='{{ site.baseurl }}images/time-travel.png' style='max-width:639px'>