All Projects → eugeneia → Erlangen

eugeneia / Erlangen

Licence: other
Distributed, asychronous message passing system for Clozure Common Lisp

Projects that are alternatives of or similar to Erlangen

Actor4j Core
Actor4j is an actor-oriented Java framework. Useful for building lightweighted microservices (these are the actors themselves or groups of them). Enhanced performance of message passing.
Stars: ✭ 48 (-15.79%)
Mutual labels:  actors, message-passing
Coerce Rs
Coerce - an asynchronous (async/await) Actor runtime and cluster framework for Rust
Stars: ✭ 231 (+305.26%)
Mutual labels:  actors, distributed
So 5 5
SObjectizer: it's all about in-process message dispatching!
Stars: ✭ 87 (+52.63%)
Mutual labels:  actors, message-passing
Core
CQRS-DDD-Actor framework for Node.js
Stars: ✭ 273 (+378.95%)
Mutual labels:  actors, distributed
Sobjectizer
An implementation of Actor, Publish-Subscribe, and CSP models in one rather small C++ framework. With performance, quality, and stability proved by years in the production.
Stars: ✭ 172 (+201.75%)
Mutual labels:  actors, message-passing
Pyrlang
Erlang node implemented in Python 3.5+ (Asyncio-based)
Stars: ✭ 436 (+664.91%)
Mutual labels:  message-passing, distributed
Actor Zeta
Library that provides an actor style message-passing programming model (in C++).
Stars: ✭ 34 (-40.35%)
Mutual labels:  message-passing
Fgbase
Ready-send coordination layer on top of goroutines.
Stars: ✭ 45 (-21.05%)
Mutual labels:  distributed
Tarant
Reactive, actor based framework that can be used in client and server side.
Stars: ✭ 33 (-42.11%)
Mutual labels:  actors
Pycos
Concurrent, Asynchronous, Distributed, Communicating Tasks with Python
Stars: ✭ 30 (-47.37%)
Mutual labels:  message-passing
Erlamsa
Erlang port of famous radamsa fuzzzer.
Stars: ✭ 56 (-1.75%)
Mutual labels:  distributed
Tns
tns provides distributed solutions for thrift, support service discovery, high availability, load balancing, the gray release, horizontal scaling, and so on.
Stars: ✭ 53 (-7.02%)
Mutual labels:  distributed
Eventd
A simple daemon to track remote or local events and do actions the user wants to
Stars: ✭ 43 (-24.56%)
Mutual labels:  distributed
Crypto Dht
Blockchain over DHT in GO
Stars: ✭ 38 (-33.33%)
Mutual labels:  distributed
Weidentity
基于区块链的符合W3C DID和Verifiable Credential规范的分布式身份解决方案
Stars: ✭ 972 (+1605.26%)
Mutual labels:  distributed
Protoactor Dotnet
Proto Actor - Ultra fast distributed actors for Go, C# and Java/Kotlin
Stars: ✭ 1,070 (+1777.19%)
Mutual labels:  actors
Xxl Job Dotnet
xxl-job is a lightweight distributed task scheduling framework, and this package provide a dotnet executor client for it
Stars: ✭ 31 (-45.61%)
Mutual labels:  distributed
Attaca
Robust, distributed version control for large files.
Stars: ✭ 41 (-28.07%)
Mutual labels:  distributed
Cronner
Cronner 是一个分布式定时任务框架,支持作业依赖、分片、失效转移、集中配置和监控。
Stars: ✭ 51 (-10.53%)
Mutual labels:  distributed
Lizard
💐 Full Amazon Automatic Download
Stars: ✭ 41 (-28.07%)
Mutual labels:  distributed

Erlangen

Distributed, asynchronous message passing system for Clozure Common Lisp.

Warning: Erlangen is immature, experimental software, subject to bugs and changes.

Documentation

Getting Started

First, make sure you have Clozure Common Lisp and Quicklisp installed (if you use a Debian based OS you can install CCL via this package). Next clone Erlangen into quicklisp/local-projects.

You can now start Clozure Common Lisp and load Erlangen:

(ql:quickload :erlangen)
(use-package :erlangen)

Alternatively, you can build Erlangen as a stand-alone executable that boots into a REPL that uses the packages erlangen and erlangen.management:

cd ~/quicklisp/local-projects/erlangen
make bin/erlangen-kernel && bin/erlangen-kernel

Programming a Parallelized Map with Asynchronous Agents and Message Passing

Let’s jump straight into a practical example. The Erlangen repository contains the erlangen.example package which implements parallel-map, a parallelized map function. Its like Common Lisp’s map over a single vector, only that it spreads the work across multiple concurrent agents (fancy processes/native threads).

parallel-map returns the results of calling a function on the elements in a vector in a new vector. It uses up to level agents to parallelize the workload. Optionally, a result-type can be specified as the element type of the result vector. Here is roughly how parallel-map works:

  1. it spawns some worker agents and attaches to them in monitor mode, so that it will receive an exit notification for each worker
  2. it sends each worker agent a message with a chunk of work
  3. it waits for and receives the exit notification of each worker, which contains the chunk’s result, and inserts them into the final result vector

The worker agents initially do nothing at all. They each just wait to receive a function to execute, and quit when they are done. Note that we use the Trivia pattern matching library to match received messages.

(defun worker ()
  (ematch (receive)
    ((and (type function) function)
     (funcall function))))

Eventually, each worker will receive a map-thunk closure, which maps the mapping function over a chunk of the vector bounded by start and end.

(defun map-chunk (function vector start end result-type)
  (lambda ()
    (let ((results (make-array (- end start) :element-type result-type)))
      (loop for i from start below end do
           (setf (aref results (- i start))
                 (funcall function (aref vector i))))
      (values start end results))))

Now let’s look at parallel-map. To distribute the work, it computes

  • length—the length of our input vector
  • n-chunks—the number of chunks we will divide the work up into
  • chunk-size—the minimum length (in elements) of a chunk

and spawns a worker agent for each chunk.

(defun parallel-map (function vector &key (level 2) (result-type t))
  (let* ((length (length vector))
         (n-chunks (min level length))
         (chunk-size (floor length n-chunks))
         (workers (loop for i from 1 to n-chunks collect
                       (spawn 'worker :attach :monitor))))

Next it sends each worker a closure for the chunk it should process. It divides the work into n-chunks intervals of at least chunk-size length, that fully cover the vector.

    (loop for worker in workers
          for chunk from 1
          for start from 0 by chunk-size
          for end = (if (< chunk n-chunks)
                        (+ start chunk-size)
                        length)
       do (send (map-chunk function vector start end result-type) worker))

Finally it allocates a vector to store the results in, and waits to receive each chunk result. If any worker exits unexpectedly, parallel-map exits with that workers exit reason. Again, because we attached to the worker agents in monitor mode, all remaining workers will also receive the exit signal and shut down.

    (loop with results = (make-array length :element-type result-type)
          for worker in workers do
         (ematch (receive)
           ((list (type agent) :ok start end chunk-result)
            (replace results chunk-result :start1 start :end1 end))
           ((list (type agent) :exit reason)
            (exit reason)))
       finally (return results))))

Now we can spawn parallel-map agents like this

(spawn '(parallel-map 1+ #(2 4 6 8 10 12 14) :level 3) :attach :monitor)
(receive)
→ (#<AGENT #x302002A191ED> :OK #(3 5 7 9 11 13 15))

Getting Distributed

What fun are agents if they aren’t distributed over a network? Erlangen comes with support for distribution via TCP/IP built in. Each instance of Erlangen can act as a node, and talk to other Erlangen nodes. In order to facilitate port discovery of of remote nodes, there needs to be a port mapper running on the host. To build and run the Erlangen port mapper, execute the commands

make bin/erlangen-port-mapper
bin/erlangen-port-mapper localhost &

in a shell in the root of the Erlangen repository. Now build the Erlangen kernel in the same way to conveniently run additional Erlangen instances, and use it to start a node named map-node.

make bin/erlangen-kernel
bin/erlangen-kernel -n -e '(node :host "localhost" :name "map-node")'

Hint: if you use Emacs, you can start a new Erlangen instance with Slime via C-u M-x slime RET /path/to/erlangen-kernel.

Finally, we can also make our initial Erlangen instance a node, and offload some work to map-node:

(spawn '(node :host "localhost"))
(spawn '(erlangen.examples:parallel-map 1+ #(2 4 6 8 10 12 14))
       :attach :monitor
       :node "map-node")
(receive)
→ ("localhost/map-node/0" :OK #(3 5 7 9 11 13 15))

What happened? We spawned an agent on the remote map-node instance to run parallel-map, and received its exit notification transparently over the network.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].