November 3, 2010

Language that matters

Preface
The purpose of this article is to help my friends to better understand, why they should spend their time learning Erlang/Scala, and even functional programming, instead of Java.
This's "time-friendly" explanation of the "actor-based" concurrency, it's not something that requires from you to have the iq >= 150.
Author found himself in a very bad shape when he had started getting understanding "actor-based" concurrency from wikipedia or from original article. So, if you're smart enough to understand everything from the original sources and think that this article is a naive piece of shit then getta fuck out from here.


Begin
Last year I was in Chernigov, visited the good friend of mine, and we had a kind of disput around concurrency.
That was a time when I finished learning utillities from java.util.concurrent, researching concurrent collections and the rest concurrent crap, and finally I became amazed of what Java offers! (still 're)
Then my friend said one thing, that backup me in reality, he said that there're tasks which couldn't be implemented concurrently, and you can't do any with this, it's simply impossible, say factorial: f(x) = x * f(x-1).
From the school you know that you should wait until next call will return result, and next call should wait ... etc. Even if you transform f(x) into more "parallel-friendly" form: x*(x-1)*f(x-2), you still wouldn't be able to calculate x*(x-1) in thread1 and f(x-2) in thread2. Seems like obvious thing - but it's undoable! Why? 'cause of Java' architecure doesn't allow this and C/C++ arch. doesn't allow this, and Python, Pascal doesn't allow this and whatever imperative language you take - it will fail with such a primitive task.


Last weekend I spent some time on Erlang presentation. Why Erlang? Because it's buzzword nowadays - "concurrent-oriented functional language", "next-mainstream language", "facebook and twitter" use it, and etc. etc. etc. So, like a regular code monkey which could be easily "picked up" by marketing noice, I decided to look into it.
But, you know, unfortunately I have an average IQ, so, I didn't get from the first try(and from the second, and from the third) several principal topics:
  • why it's functional language?!
  • what "message oriented language" certainly means, in details what's that?!
  • what're lightweight process, and how it's possible that Erlang maintains thousands of them?! (Ok, I know that cpu has cores and num_of_cores = num_of_threads(aka OS threads), I know what's OS processes mean, but WTF "leightweight process" does mean?!)
  • language' creators assert that erlang-program will run x- times faster on x- core machine! WTF?! How?! What they can do with factorial?! Does it matter 10 or 2 cores, for f(8) you still should wait for f(6), for f(6) you should wait for f(4) and etc.
After three hours I found the answer on last question, and then on the rest three. No math equations, no theorems - just logical thinking.


It's simple
Say, we want calculate f(5) concurrenctly, having 2 CPUs(cores), right now don't bother how. See the picture:












































You can see that "lightweight process" is just a function. But how they connect to each other(*), how they return results to each other in a way that makes program run very fast?! In Java, for example, functions "connect" to each other through stack of
execution and in C/C++ too, and in Python, and in Pascal..




Let me guide you through the last question(*). Write down f(5) in this way:


P1#cpu2 = P2#cpu1 * (P3#cpu1 = P4#cpu1 * P5#cpu2)


Pi#CPUj     - i-th leightweight process on CPUj
i           - in [1, many]
j           - num of cores


Ok, but still, how this excels imperative style ?! Why f(5) should run faster if add one more core?!


The answer: Pi executes its work and doesn't wait for completion of Pi+1, each Pi executes independently! It's done through non-blocking IO, non-blocking queues and message passing! Each process have a non-blocking queue from which it reads messages, it's same as: 
class process {
    ConcurrentLinkedQueue queue;
    void receive() {
        for(;;) {
            if (queue.get() != null) {
                // hey! what's up?!
            }
        }
    }
}
OS scheduler allocates thread and cpu-time for each Pi to execute, and it's ok if it can't do anything usefull! For example if P3#cpu1 gets time to execute(read 'being assigned to OS thread') it will find that there's nothing to do 'cause P4#cpu1 and P5#cpu2 didn't return results, so what?! - yield OS thread and let the other processes get a chance to work!

The most attracting part of all this - you get grid/cloud/network computing almost out of the box:
P1#(127.0.0.1) = P2#(243.88.33.11) * (P3#(10.10.0.6) = P4#(77.55.99.33) * P5#(14.133.15.16))


Hope this article will help you in understanding the "state-of-things" when you decide start learning Erlang or Scala and encourage you to get more about the topic. 


Thanks for your time!


References
http://en.wikipedia.org/wiki/Lambda_calculus
http://en.wikipedia.org/wiki/Actor_(computer_science)
http://en.wikipedia.org/wiki/Erlang_(programming_language)