Rustよりも速く進み、Mail.Ru Groupは測定を行いました

そのフレーズで、彼らは私に2015年からのMail.Ru Groupによる記事へのリンクを投げました、 「プログラミング言語を選ぶ方法?」 つまり、Go、Rust、Scala、およびNode.jsのパフォーマンスを比較しました。 GoとRustは1位を争いましたが、Goは勝ちました。



記事の著者としてゴブワが書いたように (スペルは以下に保存されます):

これらのテストは、プログラマーの手に依存する「他のニュアンス」なしで、ベアサーバーの動作を示します。
残念なことに、テストは同等ではなく、記事の客観性と結論に疑問を投げかけたコードは1行だけでした。



この記事には、元の記事からの多くのコピー&ペーストが含まれますが、それらが私を許してくれることを願っています。



テストの本質

テスト中、この設定ではすべての申請者がほぼ同じパフォーマンスで作業することが判明しました。すべてがV8のパフォーマンスに基づいています。 ただし、割り当ての実装は不要ではありませんでした-各言語の開発により、主観的な評価のかなりの部分を構成することが可能になり、最終的な選択に何らかの影響を与える可能性があります。
したがって、2つのシナリオがあります。 1つ目は、ルートURLへの挨拶です。



GET / HTTP/1.1
Host: service.host

HTTP/1.1 200 OK

Hello World!
      
      





— , URL:



GET /greeting/user HTTP/1.1
Host: service.host

HTTP/1.1 200 OK

Hello, user
      
      







Node.js
var cluster = require('cluster');
var numCPUs = require('os').cpus().length;
var http = require("http");
var debug = require("debug")("lite");
var workers = [];
var server;

cluster.on('fork', function(worker) {
    workers.push(worker);

    worker.on('online', function() {
        debug("worker %d is online!", worker.process.pid);
    });

    worker.on('exit', function(code, signal) {
        debug("worker %d died", worker.process.pid);
    });

    worker.on('error', function(err) {
        debug("worker %d error: %s", worker.process.pid, err);
    });

    worker.on('disconnect', function() {
        workers.splice(workers.indexOf(worker), 1);
        debug("worker %d disconnected", worker.process.pid);
    });
});

if (cluster.isMaster) {
    debug("Starting pure node.js cluster");

    ['SIGINT', 'SIGTERM'].forEach(function(signal) {
        process.on(signal, function() {
            debug("master got signal %s", signal);
            process.exit(1);
        });
    });

    for (var i = 0; i < numCPUs; i++) {
        cluster.fork();
    }
} else {
    server = http.createServer();

    server.on('listening', function() {
        debug("Listening %o", server._connectionKey);
    });

    var greetingRe = new RegExp("^\/greeting\/([a-z]+)$", "i");
    server.on('request', function(req, res) {
        var match;

        switch (req.url) {
            case "/": {
                res.statusCode = 200;
                res.statusMessage = 'OK';
                res.write("Hello World!");
                break;
            }

            default: {
                match = greetingRe.exec(req.url);
                res.statusCode = 200;
                res.statusMessage = 'OK';
                res.write("Hello, " + match[1]);    
            }
        }

        res.end();
    });

    server.listen(8080, "127.0.0.1");
}
      
      







Go
package main

import (
    "fmt"
    "net/http"
    "regexp"
)

func main() {
    reg := regexp.MustCompile("^/greeting/([a-z]+)$")
    http.ListenAndServe(":8080", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        switch r.URL.Path {
        case "/":
            fmt.Fprint(w, "Hello World!")
        default:
            fmt.Fprintf(w, "Hello, %s", reg.FindStringSubmatch(r.URL.Path)[1])
        }
    }))
}
      
      







Rust
extern crate hyper;
extern crate regex;

use std::io::Write;
use regex::{Regex, Captures};

use hyper::Server;
use hyper::server::{Request, Response};
use hyper::net::Fresh;
use hyper::uri::RequestUri::{AbsolutePath};

fn handler(req: Request, res: Response<Fresh>) {
    let greeting_re = Regex::new(r"^/greeting/([a-z]+)$").unwrap();

    match req.uri {
        AbsolutePath(ref path) => match (&req.method, &path[..]) {
            (&hyper::Get, "/") => {
                hello(&req, res);
            },
            _ => {
                greet(&req, res, greeting_re.captures(path).unwrap());
            }
        },
        _ => {
            not_found(&req, res);
        }
    };
}

fn hello(_: &Request, res: Response<Fresh>) {
    let mut r = res.start().unwrap();
    r.write_all(b"Hello World!").unwrap();
    r.end().unwrap();
}

fn greet(_: &Request, res: Response<Fresh>, cap: Captures) {
    let mut r = res.start().unwrap();
    r.write_all(format!("Hello, {}", cap.at(1).unwrap()).as_bytes()).unwrap();
    r.end().unwrap();
}

fn not_found(_: &Request, mut res: Response<Fresh>) {
    *res.status_mut() = hyper::NotFound;
    let mut r = res.start().unwrap();
    r.write_all(b"Not Found\n").unwrap();
}

fn main() {
    let _ = Server::http("127.0.0.1:8080").unwrap().handle(handler);
}
      
      







Scala
package lite

import akka.actor.{ActorSystem, Props}
import akka.io.IO
import spray.can.Http
import akka.pattern.ask
import akka.util.Timeout
import scala.concurrent.duration._
import akka.actor.Actor
import spray.routing._
import spray.http._
import MediaTypes._
import org.json4s.JsonAST._

object Boot extends App {
  implicit val system = ActorSystem("on-spray-can")
  val service = system.actorOf(Props[LiteActor], "demo-service")
  implicit val timeout = Timeout(5.seconds)
  IO(Http) ? Http.Bind(service, interface = "localhost", port = 8080)
}

class LiteActor extends Actor with LiteService {
  def actorRefFactory = context
  def receive = runRoute(route)
}

trait LiteService extends HttpService {
  val route =
    path("greeting" / Segment) { user =>
      get {
        respondWithMediaType(`text/html`) {
          complete("Hello, " + user)
        }
      }
    } ~
    path("") {
      get {
        respondWithMediaType(`text/html`) {
          complete("Hello World!")
        }
      }
    }
}
      
      









, .



Don't click
, Node.js Go , Rust . Scala .



regex Rust:



Example: Avoid compiling the same regex in a loop





It is an anti-pattern to compile the same regular expression in a loop since compilation is typically expensive. (It takes anywhere from a few microseconds to a few milliseconds depending on the size of the regex.) Not only is compilation itself expensive, but this also prevents optimizations that reuse allocations internally to the matching engines.



In Rust, it can sometimes be a pain to pass regular expressions around if they're used from inside a helper function. Instead, we recommend using the lazy_static crate to ensure that regular expressions are compiled exactly once.



For example:



#[macro_use] extern crate lazy_static;
extern crate regex;

use regex::Regex;

fn some_helper_function(text: &str) -> bool {
    lazy_static! {
        static ref RE: Regex = Regex::new("...").unwrap();
    }
    RE.is_match(text)
}

fn main() {}
      
      





Specifically, in this example, the regex will be compiled when it is used for the first time. On subsequent uses, it will reuse the previous compilation.


regex Go:



But you should avoid the repeated compilation of a regular expression in a loop for performance reasons.


? … , :

! split , , regexp . wrk split.


.





Rust
extern crate hyper;
extern crate regex;

#[macro_use] extern crate lazy_static;

use std::io::Write;
use regex::{Regex, Captures};

use hyper::Server;
use hyper::server::{Request, Response};
use hyper::net::Fresh;
use hyper::uri::RequestUri::{AbsolutePath};

fn handler(req: Request, res: Response<Fresh>) {
    lazy_static! {
        static ref GREETING_RE: Regex = Regex::new(r"^/greeting/([a-z]+)$").unwrap();
    }

    match req.uri {
        AbsolutePath(ref path) => match (&req.method, &path[..]) {
            (&hyper::Get, "/") => {
                hello(&req, res);
            },
            _ => {
                greet(&req, res, GREETING_RE.captures(path).unwrap());
            }
        },
        _ => {
            not_found(&req, res);
        }
    };
}

fn hello(_: &Request, res: Response<Fresh>) {
    let mut r = res.start().unwrap();
    r.write_all(b"Hello World!").unwrap();
    r.end().unwrap();
}

fn greet(_: &Request, res: Response<Fresh>, cap: Captures) {
    let mut r = res.start().unwrap();
    r.write_all(format!("Hello, {}", cap.at(1).unwrap()).as_bytes()).unwrap();
    r.end().unwrap();
}

fn not_found(_: &Request, mut res: Response<Fresh>) {
    *res.status_mut() = hyper::NotFound;
    let mut r = res.start().unwrap();
    r.write_all(b"Not Found\n").unwrap();
}

fn main() {
    let _ = Server::http("127.0.0.1:3000").unwrap().handle(handler);
}

      
      







, .





, , . , , , , , , 2015.12.17 ( , ).





    • Intel® Core(TM) i7-6820HQ CPU @ 2.70GHz, 4+4
    • CPU Cache L1: 128 KB, L2: 1 MB, L3: 8 MB
    • 8+8 GB 2133MHz DDR3




    • Intel® Core(TM) i3 CPU 560 @ 3.33GHz, 2+2
    • CPU Cache L1: 64 KB, L2: 4 MB
    • 4+4 GB 1333MHz DDR3


  1. go 1.6.2, released 2016/04/20

  2. rust 1.5.0, released 2015/12/10. , Rust.

  3. , Scala Node.js, .





ab



50 000 10 , 256 .





ab -n50000 -c256 -t10 "http://127.0.0.1:3000/





Label Time per request, ms Request, #/sec
Rust 11.729 21825.65
Go 13.992 18296.71


ab -n50000 -c256 -t10 "http://127.0.0.1:3000/greeting/hello"





Label Time per request, ms Request, #/sec
Rust 11.982 21365.36
Go 14.589 17547.04




ab -n50000 -c256 -t10 "http://127.0.0.1:3000/"





Label Time per request, ms Request, #/sec
Rust 8.987 28485.53
Go 9.839 26020.16


ab -n50000 -c256 -t10 "http://127.0.0.1:3000/greeting/hello"





Label Time per request, ms Request, #/sec
Rust 9.148 27984.13
Go 9.689 26420.82


— , — . — - 500rps?! , , !



. , .





ab -n50000 -c256 -t10 "http://127.0.0.1:3000/"





Label Time per request, ms Request, #/sec
Rust 5.601 45708.98
Go 6.770 37815.62


ab -n50000 -c256 -t10 "http://127.0.0.1:3000/greeting/hello"





Label Time per request, ms Request, #/sec
Rust 5.736 44627.28
Go 6.451 39682.85


, Go, ?









, Mail.Ru Group . , 1.5 45 , Go , Mail.Ru Group, , , .



Rust , «The Computer Language Benchmarks Game» Rust vs Go 2015 2017 . .



, , Go, . Rust, , .



, , , .



Let the Holy War begin!



All Articles