Posts

How Go runtime makes concurrent code simple

5 years ago I showed a way to fight with "all-threads-busy" problem while writing Hopac code . The same problem exists on JVM as well. The root is in the cooperative concurrency model, which is based on thread pools, async/futures/etc. are scheduled to run on them. So, if an async is blocked by an IO call of if it's just executing some computation, the OS thread is not able to do anything else: the scheduler is unable to "pause" such a blocked async and execute anothe one. So, asynchronous code on .NET, JVM, Rust, etc. is inherently unsafe: it's not guaranteed that all existing asyncs progress. The things are different in Go runtime: the scheduler is preemptive, so it can "pause" goroutines at any point: not only at function calls, at loops, but on just any execution point (almost). This makes writing concurrent code dead easy: there's no "async" functions, lambdas or blocks of code, every function or call are the same, being them ex...

Load balancing: Rancher vs Swarm

Image
Rancher has a load balancer built it (HAProxy). Let's compare its performance vs Docker Swarm one. I will use 3 identical nodes: 192GB RAM 28-cores i5 Xeon 1GBit LAN CentOS 7 Docker 1.12 Rancher 1.1.2 I will benchmark against a hello world HTTP server written in Scala with Akka-HTTP and Spray JSON serialization (I don't think it matters though), sources are on GitHub . I will use Apache AB benchmark tool. As a baseline, I exposed the web server port outside the container and run the following command: ab -n 100000 -c 20 -k http://1.1.1.1:29001/person/kot It shows 22400 requests per second. I'm not sure whether it's a great result for Akka-HTTP, considering that some services written in C can handle hundreds of thousands requests per second, but it's not the main topic of this blog post (I ran the test with 100 concurrent connections (-c 100), and it shows ~50k req/sec. I don't know if this number is good enough either :) ) Now I creat...

Running computational intensive code outside of Hopac scheduler

Hopac uses a bounded pool of worker threads, number of which is equal to number of CPU cores (by default). A dangerous thing about this design is that a situation is possible where all the threads are busy doing some CPU intensive work and no other Hopac jobs can proceed. A good solution for this is running such a CPU bound computations on the standard .NET thread pool, freeing Hopac pool for more intelligent work. I found a nice code in one of the older Hopac GitHub discussions which schedules a ordinary function on ThreadPool and represents the result as a Hopac job. Here is a test with explanations:

Upcoming F# struct tuples: are they always faster?

Image
Don Syme  has been working  on struct tuples for F# language. Let's see if they are more performant than "old" (heap allocated) tuples in simple scenario: returning tuple from function. The code is very simple: Decompiled code in Release configuration: Everything we need to change to switch to struct tuples, is adding "struct" keyword in front of constructor and pattern matching: Decompiled code in Release configuration: I don't know about you, but I was surprised with those results. The performance roughly the same. GC is not a bottleneck as no objects were promoted to generation 1. Conclusions: Using struct tuples as a faster or "GC-friendly" alternative to return multiple values from functions does not make sense. Building in release mode erases away heap allocated tuples, but not struct tuples. Building in release mode inlines the "foo" function, which makes the code 10x faster. You can fearlessly allo...

Hash maps: Rust, F#, D, Go, Scala

Image
Let's compare performance of hashmap implementation in Rust, .NET, D (LDC) and Go. Rust: F#: As you can see, Rust is slower at about 17% on insersions and at about 21% on lookups. Update As @GolDDranks suggested on Twitter , since Rust 1.7 it's possible to use custom hashers in HashMap. Let's try it: Yes, it's significantly faster: additions is only 5% slower than .NET implementation, and lookups are 32% *faster*! Great. Update: D added LDC x64 on windows It's very slow at insertions and quite fast on lookups. Update: Go added Update: Scala added Compared to Scala all the other languages looks equally fast :) What's worse, Scala loaded all four CPU cores at almost 100% during the test, while others used roughly single core. My guess is that JVM allocated so many objects (each Int is an object, BTW), that 3/4 of CPU time was spend for garbage collecting. However, I'm a Scala/JVM noob, so I just could write the whole b...

Akka.NET Streams vs Hopac vs AsyncSeq

Image
Akka.NET Streams is a port of its Scala/Java counterpart and intended to execute complex data processing graphs, optionally in parallel and even distributed. It has quite different semantics compared to Hopac's one and it's wrong to compare them feature-by-feature, but it's still interesting to benchmark them in a scenario which both of them supports well: read lines of a file asynchronously, filter them by a regex in controlled degree of parallelism, then normalize the lines with a simple string manipulation algorithm, also in parallel, then count the number of lines. Firts, Akka.NET: Note that I have to use the empty string as indication that the regular expression does not match. I should use `option ` of course (just like I do in the Hopac snippet below), but Akka.NET Streams is strict about what is allowed to be returned by its combinators like `Map` or `Filter`, in particular, you cannot return `null`, doing so makes Akka.NET unhappy and it will throw exception...

Regular expressions: Rust vs F# vs Scala

Image
Let's implement the following task: read first 10M lines from a text file of the following format: then find all lines containing Microsoft namespace in them, and format the type names the usual way, like "Microsoft.Win32.IAssemblyEnum". First, F#: Now Rust: After several launches the file was cached by the OS and both implementations became non IO-bound. F# one took 29 seconds and 31MB of RAM at peak; Rust - 11 seconds and 18MB. The Rust code is as twice as long as F# one, but it's handling all possible errors explicitly - no surprises at runtime at all. The F# code may throw some exceptions (who knows what kind of them? Nobody). It's possible to wrap all calls to .NET framework with `Choice.attempt (fun _ -> ...)`, then define custom Error types for regex related code, for IO one and a top-level one, and the code'd be even longer then Rust's, hard to read and it would still give no guarantee that we catch all possible exceptions. Up...

Elixir: first look

I don't have a clear impression about Elixir language yet. I don't like it has Ruby like syntax, but do like it has pipe operator and macros. So, Fibonacci: It executes in about 13 seconds which is on pair (even faster for unknown reason) with Erlang, no surprises here. D (GDC) - 0.990 C# - 1.26 D (DMD) - 1.3 C++ - 1.33 F# - 1.38 Nemerle - 1.45 Rust - 1.66 Go - 2.38 Haskell - 2.8 Clojure - 9 Elixir - 13 Erlang - 17 Ruby - 60 Python - 120

SHA1 compile time checked literals: F# vs Nemerle vs D

Image
I've always been interested in metaprogramming. Sooner or later, I'm starting to feel constrained within a language without it. F# is a really nice language, but I'm afraid I'd have got bored with it if it'd not have Type Providers, for example. Why metaprogramming is so important? Because it allows changing a language without cracking the compiler. It allows making things which seemed to be impossible to implement. I'm dealing with cryptography hashes a lot at work, nothing rocket since, just MD5, SHA-1 and so on. And I write tons of tests where such hashes are used in form of string literals, like this: The problem with this code is that the compiler cannot guarantee that the hex string in the last line represents a valid SHA-1. If it does not, the test will fail at runtime for a reason it's not intended to. OK, now we can formulate our task: provide a language construct to enforce a string literal being a valid SHA-1 hexadecimal, at compile t...

Fib: C++, C# and GDC

As a reference implementation, I added C++ one: It's execution time is 1.33 seconds, which surprisingly is not the best result so far. A C# version: Also, I compiled  this D code with GDC compiler and it executed in 990 ms, which is the best result: D (GDC) - 0.990 C# - 1.26 D (DMD) - 1.3 C++ - 1.33 F# - 1.38 Nemerle - 1.45 Rust - 1.66 Go - 2.38 Haskell - 2.8 Clojure - 9 Erlang - 17 Ruby - 60 Python - 120 Unfortunately, I have not managed to compile the D code with LDC compiler, it returns the following error: Building: DFib (Release) Performing main compilation... Current dictionary: d:\git\DFib\DFib D:\ldc2-0.15.2-beta1-win64-msvc\bin\ldc2.exe -O3 -release "main.d"   "-od=obj\Release" "-of=d:\git\DFib\DFib\bin\Release\DFib.exe" LINK : fatal error LNK1181: cannot open input file 'kernel32.lib' Error: C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin\link.exe failed with status: 1181 ...

Composing custom error types in F#

I strongly believe that we should keep code as referential transparent as possible. Unfortunately, F# language does not encourage programmers to use Either monad to deal with errors. The common practice in the community is using common in the rest .NET (imperative) world exception based approach. From my experienced, almost all bugs found in production are caused by unhandled exceptions.  The problem In our project we've used the Either monad for error handling with great success for about two years. ExtCore  is a great library making dealing with Either, Reader, State and other monads and their combinations really easy. Consider a typical error handling code, which make use Choice computation expression from ExtCore: The code is a bit hairy because of explicit error mapping. We could introduce an operator as a synonym for Choice.mapError, like <!>, after which the code could become a bit cleaner: (actually it's the approach we use at in our tea...

Go: fib

Go code is relatively low-level since it does not have "foreach over range" syntax construct: Results are not as impressive for a systems language: 2.38 seconds. And it lays below Rust but under Haskell: C# - 1.26 D (DMD) - 1.3 F# - 1.38 Nemerle - 1.45 Rust - 1.66 Go - 2.38 Haskell - 2.8 Clojure - 9 Erlang - 17 Ruby - 60 Python - 120

Computing cryptography hashes: Rust, F#, D and Scala

Image
Let's compare how fast Rust, D and F# (.NET actually) at computing cryptography hashes, namely MD5, SHA1, SHA256 and SHA512. We're going to use rust-crypto cargo : Results: MD5 - 3.39s  SHA1 - 2.89s  SHA256 - 6.97s SHA512 - 4.47s Now the F# code: Results (.NET 4.5, VS 2013, F# 3.1): MD5CryptoServiceProvider - 2.32s (32% faster) SHA1CryptoServiceProvider - 2.92s (1% slower) SHA256Managed - 16.50s (236% slower) SHA256CryptoServiceProvider - 11.50s (164% slower) SHA256Cng - 11.71s (168% slower) SHA512Managed - 61.04s (1365% slower) SHA512CryptoServiceProvider - 21.88s (489% slower) SHA512Cng - 22.19s (496% slower) (.NET 4.6, VS 2015, F# 4.0): MD5CryptoServiceProvider elapled 2.55 SHA1CryptoServiceProvider elapled 2.89 SHA256Managed elapled 17.01 SHA256CryptoServiceProvider elapled 8.74 SHA256Cng elapled 8.75 SHA512Managed elapled 23.42 SHA512CryptoServiceProvider 5.81 SHA512Cng elapled 5.79 D: DMD MD5 - 16.05s (470% ...

Rust: fib

Rust is an interesting language. It is not a primitive one, like Go where we don't have ADTs, pattern matching and generics (but we do have Nils). And it's advertising as a safe and performant system language. Today is the very first day I'm looking at it. Let's "smoke" test it with Fibonacci :) Debug: 3.44 seconds, release: 1.66 seconds. This is not very impressive, but pretty fast indeed. C# - 1.26 D (DMD) - 1.3 F# - 1.38 Nemerle - 1.45 Rust - 1.66 Haskell - 2.8 Clojure - 9 Erlang - 17 Ruby - 60 Python - 120 It's very interesting how it'll behave in concurrent Fibonacci test. The compiler is quite slow: it takes 2-3 seconds to build this tiny program.

Parallel reduce: Hopac, Asyncs, Tasks and Scala's Futures

Image
Tuomas Hietanen posted a parallel reduce function that uses TPL Tasks. I found it interesting to compare performance of this function with analogues implemented using F# Asyncs, Hopac Jobs and Scala Futures. The author uses noop long-running reduce function to show that it's really run in parallel. In this blog post we are benchmarking another aspect of the implementations: how much extra cost is introduced by a particular parallezation mechanism (library) itself. We translate the original code almost as is to Tasks and Hopac: And Scala's Futures: The results (Core i5, 4 cores): Sequential List.reduce: Real: 00:00:00.014, CPU: 00:00:00.015, GC gen0: 0, gen1: 0, gen2: 0  Tasks: Real: 00:00:01.790, CPU: 00:00:05.678, GC gen0: 36, gen1: 10, gen2: 1  Hopac: Real: 00:00:00.514, CPU: 00:00:01.482, GC gen0: 27, gen1: 2, gen2: 1  Asyncs: Real: 00:00:37.872, CPU: 00:01:48.405, GC gen0: 90, gen1: 29, gen2: 4 Scala Futures: 4.8 seconds (Hopac - 3....

Fibonacci: Hopac vs Async vs TPL Tasks on .NET and Mono

Image
Hopac claims that its Jobs are much more lightweight that F# Asyncs. There are many benchmarks on Hopac github repository, but I wanted to make a simple and straightforward benchmark and what could be simpler that parallel Fibonacci algorithm? :) (actually there's a more comprehensive  benchmark in the Hopac repository itself, see Fibonacci.fs ) Sequential Fibonacci function is usually defined as So write a parallel version in Hopac where each step is performed in a Job and all these Jobs are (potentially) run in Parallel by Hopac's scheduler An equivalent parallel algorithm written using F# Asyncs ...and using TPL Tasks All three functions create *a lot* of parallel jobs/asyncs/tasks. For example, for calculating fib (34) they create ~14 million of jobs (this is why Fibonacci was chose for this test). To make them work efficiently we will use the sequential version of fib for small N, then switch to parallel version Now we can run both of the function with d...

D

I've to say, D is very interesting language with several unique features. What about performance? How it compares to VM-based languages? It took 3.6 seconds in debug mode and 1.3 seconds in release mode, which is on pair with F#. C# - 1.26 D (DMD) - 1.3 F# - 1.38 Nemerle - 1.45 Haskell - 2.8 Clojure - 9 Erlang - 17 Ruby - 60 Python - 120

Scala pros and cons from F# dev view

Recently I started to learn Scala (for about 2 weeks now). Here is prons and cons so far (note: I've not written any serious code yet, just have read "Scala for the impatient" and now reading "Programming in Scala" by Odersky): Prons Passing not evaluated block as argument ("by-name arguments". Allows to develop better DSLs) Macros! There are few libraries written with macros, impossible to implement in a language like c# or f# (MacWire etc.) Cons No not-nullable types (this is a huge one) Not whitespace sensitive (curly braces everywhere)  No type inference for arguments and, sometimes, for result type (signatures involving generic types may be really hairy)  No compiler warning on implicitly discarded expression value (possibly wrong code like arr.map(x => x * 2); arr.map(x => x * 3). The result of the first map is discarded silently. In contrast, F# forces us to write arr |> Array.map (fun x -> x * 2) |> ignore; arr...

STM revisited: add Scala

Some time ago I compared F# and Haskell STM implementation performance. Today I'm adding Scala into it: So, it's more than two times slower than Haskell, but more than 3 times faster than F#. Interesting.

Duck typing in Scala

Scala keeps surprising me. It has much good stuff that F# has. For example, I believed that statically resolved type parameters are unique to F#. Turns out this is not the case. Scala has equivalent feature called "structural types". It has nice syntax compared to F#'s statically resolved type parameters. However, it uses reflection under the hood while F# uses inlining to instantiate functions in-place at compile time. This approach should definitely have awful performance characteristics. Surprisingly, it's not the case: Static call took about 0.5 seconds, "duck typing" call took about 4 seconds. It only 8-10 times slower than static call. Frankly, I expected something like 100x - 1000x degradation. So, it's not as fast as the equivalent feature in F#, but it certainly practically useful in many circumstances.