Comparison of concurrent implementations of Scala and Golang----Good question

Source: Internet
Author: User
Tags comparison exception handling memory usage thread

comparison of concurrent implementations of Scala and Golang

Concurrency language is the need of large-scale application of architecture, and has its real needs. Before and after learning about Scala and Golang, we deeply appreciate the great differences in style and philosophy between the modern concurrency language and the old Java and C + + languages. This article is mainly for Scala and Golang two of my favorite concurrency language in the concurrency characteristics of different implementations, to do a comparison and elaboration, in order to further deepen understanding.I. The concurrent implementation of Scala and Golang
The Scala language concurrency design uses the Actor model, drawing on Erlang's actor implementation, and after Scala 2.10, Scala uses the Akka Actor model Library.The main features of the Actor model are as follows:
"Everything is a participant", and each actor is independent, the sender and the sent message decoupling, which is the Actor model salient features, the implementation of asynchronous communication; actor is an object that encapsulates state and behavior, communicates with each other through message exchange, and stores messages in the recipient's mailbox; Actor can have a parent-child relationship, the parent actor can supervise the child actor, the only supervisor of the child actor is the parent actor; an actor is a container that contains state, behavior, a mailbox (mailbox to accept messages), sub actors, and a regulatory strategy; The go language can also implement traditional shared memory communication, but go advocates "sharing memory with communication rather than sharing memory". The concurrent communication of Go uses the CSP (communicating sequential Process) model,Its main features are as follows:
Goroutine (the lightweight thread of Go) is a lightweight thread management mechanism for go, which starts a goroutine with "go", allocates an idle thread if the current thread is blocked, creates a new thread if there is no idle thread, and holds the message through the channel. The channel transmits messages between the goroutine, for example, by reading the message in the channel (the popular point is like a "value"), you can understand that a task in a goroutine is complete or not; Go gives the channel a boost, which can be cached.The main differences between Scala and go in the implementation of the concurrent communication model are as follows:
The actor is asynchronous because the sender is decoupled from the implementation of the sent message, whereas the channel is a kind of synchronization, such as the reading and writing of the channel, which depends on the other party to decide whether to block itself; the actor is a container, When using Actorof to create an actor instance, it also means specifying a specific actor instance, that is, specifying which actor is performing the task, the actor must have an identity identity, otherwise how to specify it. The channel is usually anonymous, and after the task is put into the channel you don't have to worry about which channel is performing the task;Two. Example Description
Let's take a look at an example: Summing up the integer values of a sequential sequence (1-10000), observing the efficiency of single-threaded and multithreaded in Scala and go, and comparing the differences between Scala and go concurrency, which is the focus of this article. The specific requirements are as follows:
To accumulate 1-10000 integers, in the concurrency condition, we divide the 1-10000 average into four parts, start four threads for concurrent computation, and then add the results of the four threads to the final cumulative statistic. To see the difference in time more clearly, we add a 3 million empty loop during each calculation of each part:)
three. Scala implementation
The complete sample code for the Scala Akka actor concurrency implementation is listed below:

Akka Concurrent Compute Instance Import akka.actor.Actor import akka.actor.Props import akka.actor.ActorSystem Import Akka.routing.RoundRobinPool//Define a case class sealed trait Sumtrait case Class Result (value:int) extends sumtrait//calculation with a
    ctor class Sumactor extends Actor {val RANGE = 10000 def calculate (Start:int, End:int, flag:string): Int = { var cal = 0 for (i <-(Start-to-end)) {for (J <-1 to 3000000) {} cal + = i} println (
    "Flag:" + flag + ".") Return cal} def receive = {Case Value:int = sender! Result (Calculate ((RANGE/4) * (value-1) + 1, (RANGE/4) * value, value.tostring)) Case _ = = println ("Unknown in SumA ctor ")}}//Print results with actor class Printactor extends actor {def receive = {case (sum:int, starttime:long) = > println ("total:" + sum + "; takes time:" + (System.nanotime ()-startTime)/1000000000.0 + "seconds. ") Case _ = = println (" Unknown in Printactor ... ")}}//main actor, send calculation instructions toSumactor, send print instruction to Printactor class Masteractor extends Actor {var sum = 0 var count = 0 var starttime:long = 0/
  /Declare actor instance, Nrofinstances is the number of routee (Sumactor) in the pool,//here with 4 sumactor to calculate simultaneously, very powerful. Val sumactor = Context.actorof (Props[sumactor]. Withrouter (Roundrobinpool (nrofinstances = 4)), name = "Sumactor") val printactor = Context.actorof (Props[printactor], name = "Printactor") def receive = {case "cal Culate ... "= StartTime = System.nanotime () for (I <-1 to 4) Sumactor! I case Result (value) = sum + + value count + = 1 if (count = = 4) {Printactor!
  (Sum, startTime) context.stop (self)} case _ + println ("Unknown in Masteractor ...")}}}}}}}}}} object sum { def main (args:array[string]): Unit = {var sum = 0 val system = Actorsystem ("Masteractorsystem") Val Mast Eractor = System.actorof (Props[masteractor], name = "Masteractor") Masteractor! "Calculate..."
 Thread.Sleep (System.shutdown ()}}
Here we have designed 3 actor instances, as shown in the following illustration:
Here, we define a total of three actor instances (actor), Masteractor, Sumactor, and Printactor, where the former is the father actor of the latter two, as mentioned in the previous Scala Actor model feature: " The actor can have a parent-child relationship, the parent actor can supervise the child actor, and the only supervisor of the child actor is the parent actor.
Our main program starts the entire calculation process by sending the "Calculate ..." command to Masteractor, and, well, the show is on the way:)
Note the following code:
Val sumactor   = context.actorof (Props[sumactor]
                  . Withrouter (Roundrobinpool (nrofinstances = 4)), name = " Sumactor ")

The settings here will be online constructor initialize the child actor called "Routee" (Here is Sumactor), the number is 4, that is, we need 4 Sumactor instances to participate in concurrent computing. This step is critical.
Then, in the pattern match that accepts the message, start the calculation actor with the following code:

for (I <-1 to 4) Sumactor! I 

In Sumactor, each compute thread calls the Calculate method, which processes the integer summation of the fragments and returns the fragment accumulation value to the parent actor Masteractor, We have specifically implemented a pattern-matching function in the Masteractor acceptance message through the case class (case Result (value) => , it can be found that pattern matching is very important in the implementation of the Scala concurrency feature and greatly improves developer productivity. Here, we get the fragment accumulated value returned by 4 concurrent processes, and Masteractor calculates the final accumulated value. If all 4 concurrent processes are complete, the Printactor instance is called to print the results and the time spent.
During the whole operation, it is easy to understand the decoupling characteristics between the sender and the sent message, and the sender and receiver are concerned about the tasks they want to handle, such as state and behavior processing, timing and content of sending, time and content of receiving the message, etc. Of course, the actor is indeed a "container", and "perfectly formed": we use classes to encapsulate, which also encapsulates the necessary logical methods.
Scala Akka concurrency implementation, give me the feeling is the design is the key, the function of each actor and the relationship between the expression of clear, the remaining code implementation is very easy, this is the charm of Scala, Akka, in the bottom to help us do a lot of work.
The printactor here does not really make much sense, because it does not implement concurrency. It is implemented primarily to demonstrate message delivery and control between actors.
Take a look at the single-threaded compute run mode:

...
Val RANGE = 10000
var cal = 0
val startTime = System.nanotime () for
 
(i-<-(1-RANGE)) {for
  (J <- 1 to 3000000) {}
    cal + = i
}
 
val endTime = system.nanotime ()
...
The efficiency of concurrent and single-threaded modes in the next piece says, for the time being, press no table.
Four. Go language implementation
The full code for the concurrency function implemented by the go language is still listed first:
Go concurrency Compute Instance Package main import ("FMT" "Runtime" "StrConv" "Time") type Sum []int func (S Su
          m) Calculate (count, start, end int, flag string, ch Chan int) {cal: = 0 for I: = start; I <= end; i++ { for j: = 1; J <= 3000000; J + + {} cal + = i} S[count] = Cal FMT.
     Println ("flag:", "Flag", ".") Ch <-Count} func (S Sum) Letsgo () {//runtime.
 
     NUMCPU () can get CPU cores, my environment is 4 cores, so here is a straightforward set to 4, const NCPU = 4 Const RANGE = 10000 var ch = make (chan int) Runtime. Gomaxprocs (NCPU) for I: = 0; i < ncpu; i++ {Go s.calculate (i, (RANGE/NCPU) *i+1, (RANGE/NCPU) * (i+1), StrConv. Itoa (i+1), ch)} for I: = 0; i < ncpu; i++ {<-ch}} Func main () {var s sum = make ([]int, 4, 4) var sum int = 0 var starttim E = time. Now () S.letsgo () for _, V: = range s {sum + = v} fmt. Println ("Total:", SuM, "; Take time:" Now (). Sub (StartTime)), "seconds.
 ")
}
The implementation of the go language is completely different from the previous Scala implementation style, with the "Go" keyword implementation of the goroutine work mode, combined with channel, to achieve concurrency. Goroutine and channel are two powerful moves in the go language, simple but not simple. Here, our concurrency implementation model is shown in the following figure:
From the above, the concurrent magic of the Go language comes from Goroutine and channel. We have defined a sum type (insert: The type system of the go language is also very special, this is another topic,:)), it has two methods: Letsgo () and Calculate,letsgo () first create a counting channel, The 4 concurrent computations are then initiated. Each compute coprocessor calls Calculate () for a segmented calculation (and passes in the channel), and at the end of the Calculate () method, when the fragment calculation is complete, a count flag is plugged into the channel:
CH <-Count 
There is always a process first run here, then the corresponding count flag of the association is plugged into the channel, before the counting flag in the channel is not read, the other process after processing the business logic of the segmented calculation, the other association of the count flag is not plugged into the channel, The other process can only wait, because the channel has been stuck in a count flag, the flag has not been read out, the program is blocked. Then look at the following code:
For I: = 0; i < ncpu; i++ {
    <-ch
}
Here, from the channel in turn, remove the number of the association in the process of counting signs. Each time a flag is taken, it means that the sign corresponds to the completion of the mission, the next process after judging the channel is empty, it will be the count flag into the channel. So the loop, until the counting sign in the channel is all taken out, all the processes are finished. Also, what happens if you read a channel that has nothing in it and continues to read it. Then, the program will block until something is readable.
For the write, wait, and read of the channel, a simple image is depicted in the following diagram:

This is convenient for demonstration purposes, and the covariance and business logic in this example do not cause the association to be dead or locked, so it does not take into account the processing of the persistent wait for the process, if you want to handle timeouts, you can consider this:

For {
    Select {case
    <-ch:
    ... Case <-time. After (3 * time. Second): ...
    }
}

The select mechanism is also a powerful weapon in the go language concurrency processing because it is not related to this topic. But it can be seen that the go language has a deep imprint of Unix and C, the Select, channel concept is a good example.

After all the segmentation calculations are complete, the total accumulated value can be calculated:

For _, V: = range S {
    sum + = v
}

This code obtains the fragment accumulation value from the sum type instance, and finally calculates the total cumulative statistic value.
The channel in Go is cacheable and can be written before the cache is filled. In this case, the channel with the cache is not used, although it is theoretically possible to save the wait time when writing to the channel, but it can be ignored here, and should be treated with caution in large applications.
Take a look at the single-threaded compute run mode:

...
Cal: = 0 for
 
i: = Start, I <= end, i++ {for
    J: = 1; J <= 3000000; j + + {
    }
    cal + = i
}
...
Five. The perception of Scala and go
Operational efficiency

Let's take a look at the operational efficiency. My operating system is Windows 8.1 64-bit, with the following commands to compile and run the Scala and Go program concurrent programs:
SCALAC-CP lib\akka-actor_2.11-2.3.4.jar sum.scala
Scala sum
 
go build sum.go
sum
The specific run times are listed below:
scala:7.189461763 seconds (Single-threaded mode), 3.895642655 seconds (concurrency mode)
Golang 12.987232 seconds (single-threaded mode), 7.1636263 seconds (concurrency mode)
From the above, the concurrent implementation of Scala and go language is about 45% faster than single-threaded implementations, and this data is still quite impressive. And Golang concurrency is a lot slower than Scala, and it's true. I ran on another older 32-bit OS machine, and Scala took nearly 300 seconds of concurrency, while the go language concurrency was almost 20 seconds. Therefore, it should be meaningless to compare the concurrency efficiency of Scala with Go, which is influenced by complex factors such as internal implementation, type system, memory usage mechanism, concurrency mode, concurrency scale, hardware support, and so on. If you must compare the two, it will certainly lead to a war of words.
differences in the pattern
If the previous "Scala and Golang Concurrency implementation ideas", the understanding is also more abstract, but after the above example shows and comparisons, I believe that the perception will be more specific:
Akka's actor is decoupled, relatively independent, define how each actor communicates, the rest of the things in spite of handing them out, they will do their own work according to the established way, and each actor "perfectly formed", which is the inevitable basis for its decoupling. Go language is unique, through the "Go" magic and UNIX-style channel, a more lightweight way to handle concurrency, although lighter, but you still have to pay attention to the status of the channel, do not accidentally blocked, especially the channel more, complex, And it contains the data needed to process the business, not just the counting sign;
The actor implementation of Akka is the form of a library, which can also be applied to the Java language. The parallel implementation of the process is embedded in the go language;
Akka is based on the JVM, the implementation pattern is object-oriented, natural emphasis on abstraction and encapsulation, although can be interspersed with mixed application function style. And the go language clearly embodies the style of imperative language, when it is necessary to consider the encapsulation, the developers need to be more than ink.

Whether it's Scala or go.
It is said that the lightweight process in the go language can be easily launched for tens of millions, which is certainly stressful for Scala. However, the popularity and application of the go language is far less than that of the Java ecosystem, and I hope more applications can practice the go language. In addition, the go language should be more concise from the point of view of the code.
After you understand the Akka, and then look back at Java and its concurrent, there will be a weak explosion of the feeling, is still blocking, synchronization. Therefore, if it is a choice on the Java platform, don't say that Akka is a very important consideration of the indicator.
One thing to mention is that different models have their business and environment in place, so choosing Scala or the Go language is a must, depending on the needs of the real business and the environment-Scala or go. This is always a problem.
Six. Concluding remarks
concurrent implementations and scenarios are complex, such as remote invocation, exception handling, and selection of appropriate concurrency patterns. It is necessary to learn and practice in depth to be able to use concurrent skills. It is hoped that through the elaboration of this article, you will be able to understand some of the concurrent implementation ideas of Scala and Golang.

Scalago language concurrent Sharing report 24

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.