|
| 1 | +# Actor model |
| 2 | + |
| 3 | +- Light-weighted. |
| 4 | +- Inspired by Akka and Erlang. |
| 5 | +- Modular. |
| 6 | + |
| 7 | +Actors are sharing a thread-pool by default which makes them very cheap to create and discard. |
| 8 | +Thousands of actors can be created, allowing you to break the program into small maintainable pieces, |
| 9 | +without violating the single responsibility principle. |
| 10 | + |
| 11 | +## What is an actor model? |
| 12 | + |
| 13 | +[Wiki](http://en.wikipedia.org/wiki/Actor_model) says: |
| 14 | +The actor model in computer science is a mathematical model of concurrent computation |
| 15 | +that treats _actors_ as the universal primitives of concurrent digital computation: |
| 16 | +in response to a message that it receives, an actor can make local decisions, |
| 17 | +create more actors, send more messages, and determine how to respond to the next |
| 18 | +message received. |
| 19 | + |
| 20 | +## Why? |
| 21 | + |
| 22 | +Concurrency is hard this is one of many ways how to simplify the problem. |
| 23 | +It is simpler to reason about actors than about locks (and all their possible states). |
| 24 | + |
| 25 | +## How to use it |
| 26 | + |
| 27 | +{include:file:doc/actor/quick.out.rb} |
| 28 | + |
| 29 | +## Messaging |
| 30 | + |
| 31 | +Messages are processed in same order as they are sent by a sender. It may interleaved with |
| 32 | +messages form other senders though. There is also a contract in actor model that |
| 33 | +messages sent between actors should be immutable. Gems like |
| 34 | + |
| 35 | +- [Algebrick](https://github.com/pitr-ch/algebrick) - Typed struct on steroids based on |
| 36 | + algebraic types and pattern matching |
| 37 | +- [Hamster](https://github.com/hamstergem/hamster) - Efficient, Immutable, Thread-Safe |
| 38 | + Collection classes for Ruby |
| 39 | + |
| 40 | +are very useful. |
| 41 | + |
| 42 | +### Dead letter routing |
| 43 | + |
| 44 | +see {AbstractContext#dead_letter_routing} description: |
| 45 | + |
| 46 | +> {include:Actor::AbstractContext#dead_letter_routing} |
| 47 | +
|
| 48 | +## Architecture |
| 49 | + |
| 50 | +Actors are running on shared thread poll which allows user to create many actors cheaply. |
| 51 | +Downside is that these actors cannot be directly used to do IO or other blocking operations. |
| 52 | +Blocking operations could starve the `default_task_pool`. However there are two options: |
| 53 | + |
| 54 | +- Create an regular actor which will schedule blocking operations in `global_operation_pool` |
| 55 | + (which is intended for blocking operations) sending results back to self in messages. |
| 56 | +- Create an actor using `global_operation_pool` instead of `global_task_pool`, e.g. |
| 57 | + `AnIOActor.spawn name: :blocking, executor: Concurrent.configuration.global_operation_pool`. |
| 58 | + |
| 59 | +Each actor is composed from 4 parts: |
| 60 | + |
| 61 | +### {Reference} |
| 62 | +{include:Actor::Reference} |
| 63 | + |
| 64 | +### {Core} |
| 65 | +{include:Actor::Core} |
| 66 | + |
| 67 | +### {AbstractContext} |
| 68 | +{include:Actor::AbstractContext} |
| 69 | + |
| 70 | +### {Behaviour} |
| 71 | +{include:Actor::Behaviour} |
| 72 | + |
| 73 | +## Speed |
| 74 | + |
| 75 | +Simple benchmark Actor vs Celluloid, the numbers are looking good |
| 76 | +but you know how it is with benchmarks. Source code is in |
| 77 | +`examples/actor/celluloid_benchmark.rb`. It sends numbers between x actors |
| 78 | +and adding 1 until certain limit is reached. |
| 79 | + |
| 80 | +Benchmark legend: |
| 81 | + |
| 82 | +- mes. - number of messages send between the actors |
| 83 | +- act. - number of actors exchanging the messages |
| 84 | +- impl. - which gem is used |
| 85 | + |
| 86 | +### JRUBY |
| 87 | + |
| 88 | + Rehearsal -------------------------------------------------------- |
| 89 | + 50000 2 concurrent 24.110000 0.800000 24.910000 ( 7.728000) |
| 90 | + 50000 2 celluloid 28.510000 4.780000 33.290000 ( 14.782000) |
| 91 | + 50000 500 concurrent 13.700000 0.280000 13.980000 ( 4.307000) |
| 92 | + 50000 500 celluloid 14.520000 11.740000 26.260000 ( 12.258000) |
| 93 | + 50000 1000 concurrent 10.890000 0.220000 11.110000 ( 3.760000) |
| 94 | + 50000 1000 celluloid 15.600000 21.690000 37.290000 ( 18.512000) |
| 95 | + 50000 1500 concurrent 10.580000 0.270000 10.850000 ( 3.646000) |
| 96 | + 50000 1500 celluloid 14.490000 29.790000 44.280000 ( 26.043000) |
| 97 | + --------------------------------------------- total: 201.970000sec |
| 98 | + |
| 99 | + mes. act. impl. user system total real |
| 100 | + 50000 2 concurrent 9.820000 0.510000 10.330000 ( 5.735000) |
| 101 | + 50000 2 celluloid 10.390000 4.030000 14.420000 ( 7.494000) |
| 102 | + 50000 500 concurrent 9.880000 0.200000 10.080000 ( 3.310000) |
| 103 | + 50000 500 celluloid 12.430000 11.310000 23.740000 ( 11.727000) |
| 104 | + 50000 1000 concurrent 10.590000 0.190000 10.780000 ( 4.029000) |
| 105 | + 50000 1000 celluloid 14.950000 23.260000 38.210000 ( 20.841000) |
| 106 | + 50000 1500 concurrent 10.710000 0.250000 10.960000 ( 3.892000) |
| 107 | + 50000 1500 celluloid 13.280000 30.030000 43.310000 ( 24.620000) (1) |
| 108 | + |
| 109 | +### MRI 2.1.0 |
| 110 | + |
| 111 | + Rehearsal -------------------------------------------------------- |
| 112 | + 50000 2 concurrent 4.640000 0.080000 4.720000 ( 4.852390) |
| 113 | + 50000 2 celluloid 6.110000 2.300000 8.410000 ( 7.898069) |
| 114 | + 50000 500 concurrent 6.260000 2.210000 8.470000 ( 7.400573) |
| 115 | + 50000 500 celluloid 10.250000 4.930000 15.180000 ( 14.174329) |
| 116 | + 50000 1000 concurrent 6.300000 1.860000 8.160000 ( 7.303162) |
| 117 | + 50000 1000 celluloid 12.300000 7.090000 19.390000 ( 17.962621) |
| 118 | + 50000 1500 concurrent 7.410000 2.610000 10.020000 ( 8.887396) |
| 119 | + 50000 1500 celluloid 14.850000 10.690000 25.540000 ( 24.489796) |
| 120 | + ---------------------------------------------- total: 99.890000sec |
| 121 | + |
| 122 | + mes. act. impl. user system total real |
| 123 | + 50000 2 concurrent 4.190000 0.070000 4.260000 ( 4.306386) |
| 124 | + 50000 2 celluloid 6.490000 2.210000 8.700000 ( 8.280051) |
| 125 | + 50000 500 concurrent 7.060000 2.520000 9.580000 ( 8.518707) |
| 126 | + 50000 500 celluloid 10.550000 4.980000 15.530000 ( 14.699962) |
| 127 | + 50000 1000 concurrent 6.440000 1.870000 8.310000 ( 7.571059) |
| 128 | + 50000 1000 celluloid 12.340000 7.510000 19.850000 ( 18.793591) |
| 129 | + 50000 1500 concurrent 6.720000 2.160000 8.880000 ( 7.929630) |
| 130 | + 50000 1500 celluloid 14.140000 10.130000 24.270000 ( 22.775288) (1) |
| 131 | + |
| 132 | +*Note (1):* Celluloid is using thread per actor so this bench is creating about 1500 |
| 133 | +native threads. Actor is using constant number of threads. |
0 commit comments