winitzki/chymyst-core
{ "createdAt": "2017-02-05T03:00:50Z", "defaultBranch": "master", "description": "Declarative concurrency in Scala - The implementation of the chemical machine", "fullName": "winitzki/chymyst-core", "homepage": "", "language": "Scala", "name": "chymyst-core", "pushedAt": "2017-02-05T03:02:10Z", "stargazersCount": 2, "topics": [], "updatedAt": "2018-04-25T03:37:19Z", "url": "https://github.com/winitzki/chymyst-core"}Chymyst — declarative concurrency in Scala
Section titled “Chymyst — declarative concurrency in Scala”This repository hosts Chymyst Core — a library that provides a Scala domain-specific language for declarative concurrency.
Chymyst is a framework-in-planning that will build upon Chymyst Core to enable creating concurrent applications declaratively.
Chymyst is based on the chemical machine paradigm, known in the academic world as Join Calculus (JC).
JC has the same expressive power as CSP (Communicating Sequential Processes) and the Actor model, but is easier to use.
(See also Conceptual overview of concurrency.)
The initial code of Chymyst Core was based on previous work by Jiansen He (2011) and Philipp Haller (2008), as well as on Join Calculus prototypes in Objective-C/iOS and Java/Android (2012).
The current implementation is tested under Oracle JDK 8 with Scala 2.11.8 and 2.12.1.
Overview of Chymyst and the chemical machine paradigm
Section titled “Overview of Chymyst and the chemical machine paradigm”Video presentation of early version of Chymyst Core, then called JoinRun
Section titled “Video presentation of early version of Chymyst Core, then called JoinRun”This talk was given at Scalæ by the Bay 2016. See also these talk slides revised for the current syntax.
[Main features of the chemical machine]!(docs/chymyst_features.md)
Section titled “[Main features of the chemical machine]!(docs/chymyst_features.md)”[Comparison of the chemical machine vs. academic Join Calculus]!(docs/chymyst_vs_jc.md#comparison-chemical-machine-vs-academic-join-calculus)
Section titled “[Comparison of the chemical machine vs. academic Join Calculus]!(docs/chymyst_vs_jc.md#comparison-chemical-machine-vs-academic-join-calculus)”[Comparison of the chemical machine vs. the Actor model]!(docs/chymyst_vs_jc.md#comparison-chemical-machine-vs-actor-model)
Section titled “[Comparison of the chemical machine vs. the Actor model]!(docs/chymyst_vs_jc.md#comparison-chemical-machine-vs-actor-model)”[Comparison of the chemical machine vs. the coroutines / channels approach (CSP)]!(docs/chymyst_vs_jc.md#comparison-chemical-machine-vs-csp)
Section titled “[Comparison of the chemical machine vs. the coroutines / channels approach (CSP)]!(docs/chymyst_vs_jc.md#comparison-chemical-machine-vs-csp)”[Technical documentation for Chymyst Core]!(docs/chymyst-core.md).
Section titled “[Technical documentation for Chymyst Core]!(docs/chymyst-core.md).”Example: “dining philosophers”
Section titled “Example: “dining philosophers””This is a complete runnable example. The logic of “dining philosophers” is implemented in a completely declarative and straightforward code.
import io.chymyst.jc._
object Main extends App { /** Print message and wait for a random time interval. */ def wait(message: String): Unit = { println(message) Thread.sleep(scala.util.Random.nextInt(20)) }
val hungry1 = m[Int] val hungry2 = m[Int] val hungry3 = m[Int] val hungry4 = m[Int] val hungry5 = m[Int] val thinking1 = m[Int] val thinking2 = m[Int] val thinking3 = m[Int] val thinking4 = m[Int] val thinking5 = m[Int] val fork12 = m[Unit] val fork23 = m[Unit] val fork34 = m[Unit] val fork45 = m[Unit] val fork51 = m[Unit]
site ( go { case thinking1(_) => wait("Socrates is thinking"); hungry1() }, go { case thinking2(_) => wait("Confucius is thinking"); hungry2() }, go { case thinking3(_) => wait("Plato is thinking"); hungry3() }, go { case thinking4(_) => wait("Descartes is thinking"); hungry4() }, go { case thinking5(_) => wait("Voltaire is thinking"); hungry5() },
go { case hungry1(_) + fork12(_) + fork51(_) => wait("Socrates is eating"); thinking1() + fork12() + fork51() }, go { case hungry2(_) + fork23(_) + fork12(_) => wait("Confucius is eating"); thinking2() + fork23() + fork12() }, go { case hungry3(_) + fork34(_) + fork23(_) => wait("Plato is eating"); thinking3() + fork34() + fork23() }, go { case hungry4(_) + fork45(_) + fork34(_) => wait("Descartes is eating"); thinking4() + fork45() + fork34() }, go { case hungry5(_) + fork51(_) + fork45(_) => wait("Voltaire is eating"); thinking5() + fork51() + fork45() } ) // Emit molecules representing the initial state: thinking1() + thinking2() + thinking3() + thinking4() + thinking5() fork12() + fork23() + fork34() + fork45() + fork51() // Now reactions will start and print messages to the console.}Status
Section titled “Status”The Chymyst Core library is in alpha pre-release, with very few API changes envisioned for the future.
The semantics of the chemical machine (restricted to single-host, multicore computations) is fully implemented and tested on many nontrivial examples.
The library JAR is published to Maven Central.
Extensive tutorial and usage documentation is available.
Unit tests include examples such as concurrent counters, parallel “or”, concurrent merge-sort, and “dining philosophers”. Test coverage is 100% according to codecov.io.
Performance benchmarks indicate that Chymyst Core can schedule about 10,000 reactions per second per CPU core, and the performance bottleneck is in submitting jobs to threads (a distant second bottleneck is pattern-matching in the internals of the library).
Known limitations:
Chymyst Coreis about 2x slower than Jiansen He’sScalaJoinon the blocking molecule benchmark, and about 1.2x slower on some non-blocking molecule benchmarks.Chymyst Corehas no fairness with respect to the choice of molecules: If a reaction could proceed with many alternative sets of input molecules, the input molecules are not chosen at random.Chymyst Corehas no distributed execution (Jiansen He’sDisjoin.scalais not ported toChymyst, and probably will not be). Distributed computation should be implemented in a better way than posting channel names on an HTTP server. (However,Chymyst Corewill use all cores on a single machine.)
Run unit tests
Section titled “Run unit tests”sbt test
The tests will print some error messages and exception stack traces - this is normal, as long as all tests pass.
Some tests are timed and will fail on a slow machine.
Run the benchmark application
Section titled “Run the benchmark application”sbt benchmark/run will run the benchmarks.
To build the benchmark application as a self-contained JAR, run
sbt benchmark/assembly
Then run it as
java -jar benchmark/target/scala-2.11/benchmark-assembly-*.jar
Use Chymyst Core in your programs
Section titled “Use Chymyst Core in your programs”Chymyst Core is published to Maven Central.
To pull the dependency, add this to your build.sbt at the appropriate place:
libraryDependencies += "io.chymyst" %% "core" % "latest.integration"To use the chemical machine DSL, add import io.chymyst.jc._ in your Scala sources.
See the “hello, world” project for a complete minimal example.
Build the library JARs
Section titled “Build the library JARs”To build the library JARs:
sbt core/package core/package-doc
This will prepare JAR assemblies as well as their Scaladoc documentation packages.
The main library is in the core JAR assembly (core/target/scala-2.11/core-*.jar).
User code should depend on that JAR only.
Publish to Sonatype
Section titled “Publish to Sonatype”$ sbt> project core> +publishSigned> sonatypeReleaseTrivia
Section titled “Trivia”![Robert Boyle’s self-flowing flask]!(docs/Boyle_Self-Flowing_Flask.png)
This drawing is by Robert Boyle, who was one of the founders of the science of chemistry.
In 1661 he published a treatise titled “The Sceptical Chymyst”, from which the Chymyst framework borrows its name.