Go быстрее Rust, Mail.Ru Group сделала замеры

    С такой фразой мне кинули ссылку на статью компании Mail.Ru Group от 2015 «Как выбрать язык программирования?». Если кратко, они сравнили производительность Go, Rust, Scala и Node.js. За первое место боролись Go и Rust, но Go победил.

    Как написал автор статьи gobwas (здесь и далее орфография сохранена):
    Эти тесты показывают, как ведут себя голые серверы, без «прочих нюансов» которые зависят от рук программистов.
    К моему большому сожалению, тесты не были эквивалентными, ошибка всего лишь в 1 строчке кода поставила под сомнение объективность и вывод статьи.

    В статье будет много копипасты из исходной статьи, но я надеюсь, что мне это простят.

    Суть тестов

    При тестировании выяснилось, что все претенденты работают примерно с одинаковой производительностью в такой постановке — все упиралось в производительность V8. Однако реализация задания не была лишней — разработка на каждом из языков позволила составить значительную часть субъективных оценок, которые так или иначе могли бы повлиять на окончательный выбор.
    Итак, мы имеем два сценария. Первый — это просто приветствие по корневому 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 (я надеюсь, что все).

    1. Ноут
      • 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

    2. Десктоп
      • Intel® Core(TM) i3 CPU 560 @ 3.33GHz, 2+2
      • CPU Cache L1: 64 KB, L2: 4 MB
      • 4+4 GB 1333MHz DDR3

    3. go 1.6.2, released 2016/04/20
    4. rust 1.5.0, released 2015/12/10. Да, я специально взял старую версию Rust.
    5. Простите, любители 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!
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама
    Комментарии 282
    • +25
      Простите, любители Scala и Node.js, этот холивар не про вас.

      «За ноду и двор стреляю в упор.»
      шютка
      (не смешная)
      • 0

        Ноду через модуль cluster балансировать не оптимально, для этого есть специально написанные инструменты nginx или haProxy. Поэтому по ноде тесты сомнительные.


        Попробуйте так, ставим ноду 8.5
        создаем index.js


        var http = require("http");
        var server;
        
        server = http.createServer();
        
        server.on('listening', function() {
            console.log(`Listening ${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");

        Убираем балансировку и получаем тот же результат

      • +2
        Сколько ещё будут сравнивать Go и Rust, очевидно, же, что Rust будет быстрее, но в тоже время разница в читаемости кода тоже существенна и тут Go выигрывает на мой взгляд.
        • +18

          Мне очевидно, вам очевидно, а в бенчмарках Mail.Ru Group почему-то победил Go. Смотрите на таблицу в оригинальной статье под заголовком "Обобщение".

          • +6
            Читаемость у Go, конечно лучше. Как минимум из-за более простого и чистого синтаксиса. Но количество кода на Go, в большинстве случаев будет гораздо больше. В некоторых случая разница в количестве кода будет в разы. А как мы все знаем, чем меньше кода — тем меньше шанс допустить ошибку.

            P.S. Сам использую Go, где нужно быстро получить результат. А где важна надёжность использую Rust.
            • +7
              Знаете, если бы читаемость была бы основным критерием, то мы сейчас писали бы на Паскале, Аде или ещё каком-нибудь потомке Алгола.

              Одако нет же: весь спор идёт о том выбрали ли JavaScript, Go, Rust или PHP! А ведь все эти языки в смысле синтаксиса — С-подобные, что, как бы, явно тяжелее читается. Без подготовки, во всяком случае.
              • 0

                Назвать синтаксис Rust'а си-подобным можно лишь с очень большой натяжкой.

                • +3
                  Си-подобный синтаксис — это «магические» символы вместо ключевых слов. «{ }» вместо «begin end», «%» вместо «mod» и т.д. и т.п.

                  Главным тут считаются пресловутые фигурные скобки: все Algol-подобные языки их избегали, так как не на всех национальных клавиатурах они присутствовали, в крайнем случае на них вешалось что-то опциональное (вроде комментариев в Pascal — притом, что есть и другой вид, не требующий фигурных скобок).

                  В rust всё это присутствует в полной мере.
              • +3

                Что? по мне так Go самый нечитаемый и клавиатурно набивательно ориентированный язык.

                • +1
                  Ну для меня Go более читабельный, чем Rust. Но это всё весьма субъективно и я думаю ни когда не будет согласия между всеми в этом вопросе)
                • 0
                  несколько спорно.
                  со структурами нужно додумывать чтотам куда и как скопировалось при наследовании и все такое.
                • +2

                  А не смущает, что hyper взяли старой версии, еще не асинхронный?

                  • +19

                    Я взял hyper не старой версии, а ту, что была доступна автору оригинальной статьи в декабре 2015 года. Более того, я сидел на crates.io пару часов и фиксил ручками Cargo.lock, чтобы принудительно дауншифтнуть версии библиотек до того года. Иначе это была бы некорректная статья с заголовком: "Давайте сравним go 2016 года и новые модные библиотеки Rust".

                • +3
                  За эти годы Раст сменил уже десяток версий.

                  Сравнивать еще было бы неплохо память/кол-во запросов. Потому что конкуренция тут будет серьезней
                  • +10
                    За эти годы Раст сменил уже десяток версий.

                    Правильно, но я специально сделал путешествие в прошлое, чтобы Go и Rust были в равных условиях.

                    • +2
                      А зачем? Ну, доказали вы, что старая статья — врет. Кому это интересно? Как дела обстоят сегодня?
                    • 0
                      память
                      У Go есть рантайм и сборка мусора. Думаю, что тут отрыв будет ещё сильнее.
                    • +34

                      Да почти во всех бенчмарках Го против других языков примеры на этих других языках писал школьник без элементарных знаний. Вот вам пример из такого бенчмарка:


                      Java: (выгружаем всю таблицу в память из sql а потом находим нужную строку)


                      List<User> allUsers = userRepository.findAll();
                      return allUsers.get(random.nextInt(allUsers.size()));

                      Go: (находим строку по sql запросу)


                      rows, err := db.Queryx("select * from users order by random() limit 1")

                      Нет, это не шутка — это публиковалось тут на хабре и много людей так же просмотрело и возможно сделали по этому бреду вывод о "тормозной джаве".

                      • 0
                        Нет, это не шутка — это публиковалось тут на хабре

                        а ссылку привести можно? Без нее выглядит как необоснованный наброс.

                        • +1
                          sohabr.net/habr/post/317006/?version=207685

                          Было несколько статей с подобным (или этим же) кодом
                          • –1

                            так это же не Habrahabr.

                            • +5
                              Это было на хабре, просто автора совершенно справедливо вынудили спрятать такую чушь в черновики.
                              • –2

                                Никто ничего не прятал :-) https://habrahabr.ru/post/316944/ 11.1к просмотров.
                                UPD: а нет, прошу прощения, это другая (ссылалась на ту)

                                • 0

                                  Это совсем другой пост. Что вы нашли между ними общего?

                              • 0

                                Это действительно не хабр, но автор собирался опубликовать эту статью на хабре. И опубликовал её. Но скрыл, возможно из-за негативной реакции сообщества.

                            • 0
                              В черновиках:
                              7 декабря 2016 в 4:49 | сохранено в 14:29
                              Скорость работы Vapor по сравнению с другими веб-фреймворками
                              sohabr.net/habr/post/317006
                              • 0
                                Речь о коде для теста производительности, результаты которого используются в этой статье, на которую в свою очередь ссылаются в статье о Vapor.
                                Интересно, какой должен быть уровень паранойи, чтобы обходить и перепроверять все высказывания в статье, на которую ссылаешься мельком?
                                • –1

                                  Ну ок, показали автору на ошибку, он удалил статью, инцидент исчерпан.


                                  Зачем пинать лежачего и вспоминать об этом здесь?

                                  • 0

                                    Никто статью не удалял, вот она, висит https://habrahabr.ru/post/316944/ 11.1к просмотров.
                                    UPD: а нет, прошу прощения, это другая (ссылалась на ту)

                              • +3
                                школьник без элементарных знаний

                                логично, это же ЦА go :trollface:
                              • +2
                                Хз, в highloadcup только два раста в топ-50 (причем лучший — на 31 месте), при этом 10 решений на Го (лучший «чистый» Го на 15-ом).
                                • +19
                                  Это говорит скорее о том, что в Rust мало людей
                                  Так-то я и Perl вывел в top 50 ;)
                                  • +1
                                    В перле крутой http-сервер кстати (из коробки), одно из откровений чемпионата
                                    • –1
                                      Это говорит скорее о том, что в Rust мало людей

                                      Почему же? Действительно, количество людей имеющих возможность вывести некий язык «в топ» зависит от общество кол-ва людей в этом языке. Но.

                                      У людей в расте есть дополнительная мотивация участия, меньшая конкуренция, ну и язык декларируется как лоулевел и альтернатива С/С++ — т.е. он про системщину. Декларация подобного требует от комьюнити опыта в данной сфере, а иначе как можно агитировать?

                                      И вопрос остаётся открытым — где все эти люди? Остаётся только верить в то, что все они были заняты в это время.
                                      • +2

                                        По отзывам довольно много приходит питонистов и рубистов, которым надо оптимизировать какой-то нагруженный кусок, а писать на С/С++ боязно. Плюс — хайлоад кап был чисто русский, так что могло быть просто мало народу в русскоязычном комьюнити. Я например узнал о соревновании сильно постфактум.

                                        • 0
                                          Плюс — хайлоад кап был чисто русский, так что могло быть просто мало народу в русскоязычном комьюнити.

                                          Очень много. По крайней мере агитаторов.

                                          Я например узнал о соревновании сильно постфактум.

                                          Аналогично. Странно, что организаторы для такой холиварной темы не оставили рабочей свою систему проверки решений и рейтинг(вне зачёта) активным на месяц. Многие(в частности, тут мелька ссылка на редит, где раст-участник жаловался на недостаток времени) просто не успели.

                                      • +1
                                        К сожалению мало людей знает про заслуги Perl на highloadcup, потому что ваша статья опубликована только на гитхабе, хотя статья хорошая и читается на одном дыхании и стоило бы поделиться ею с остальными пользователями хабра. Но выбор ваш. Я хотел написать обзорную статью по всем языкам программирования в highloadcup, но к сожалению некоторые участники указывают ненастоящие данные и боюсь ценность выводов на основе заведомо ложной информации будет стремиться к нулю. Вначале я недооценивал перл и даже думал, что кто-то в шутку указал этот стек, пока не прочитал вашу статью. Может кто-то до сих пор так считает.
                                    • +3
                                      mkpankov на реддите написал ретроспективный анализ своей реализации, где разобрал причины.

                                      • +3
                                        Долго думал, что такое bicycle-building, пока не перевел на русский. Пример того, как не нужно буквально переводить идиомы.
                                    • +2

                                      Прочитайте обсуждение на реддите и две статьи здесь, на Хабре. Вы сможете увидеть, что рамки тестового задания были очень узкими — и основную роль играла оптимизация алгоритмов под конкретный случай, а не язык.

                                      • 0
                                        Что мешало всем остальным языкам «оптимизировать» «свои» «алгоритмы»? Я уж не буду говорить о том, что именно язык прямо и косвенно влияет на возможность оптимизаций. Косвенно через компетенцию автора в данном контексте, а прямо через возможности языка.

                                        Приведу простые примеры. Откуда у человека, который писал на готовом веб-стеке на том же пхп/ноде есть необходимые навыки сетевого программирования? Как вы будите оптимизировать аллокации на языке с ГЦ? А с неуправляемым ГЦ?

                                        И таких примеров массы.

                                        • +2
                                          Как вы будите оптимизировать аллокации на языке с ГЦ?

                                          Точно также, как буду оптимизировать аллокации на языке без ГЦ. Например на C++ стандартная практика использования пулов объектов/памяти.

                                          • +1
                                            Например на C++ стандартная практика использования пулов объектов/памяти.

                                            Это уже не оптимизация, а подступы через известное место. Дело в том, что в языках с ГЦ нету памяти. Как там обстоят дела с placement new и иже с ним. Такими темпами мы потонем в тоннах бойлерплейта. Одно дело иметь уже заточенные средства, а другое дело иметь язык с абсолютно обратной логикой.

                                            Просто пример. В крестах мы взяли 100n памяти и проинициализировали её внутри логики. Память мы взяли за ноль. Как это сделать в языке с ГЦ? А никак. Мы либо будем долбить пуши в массив, либо инициализировать его два раза.

                                            При этом не всё так просто и те же стоки в пулы не засунешь. Как мы будем реализовать строки? Через массивы? Как мы будем их инициализировать? Где мы возьмём мемкопи? Где мы возьмём конкурентоспособную реализацию сишным строкам? Реализуем? Сомневаюсь.

                                            Это я не говорю о том, что пролетаем со всем тем рантаймом, что предоставляет нам язык. Он весь повязан на гц. И опять же — в данном случае мы опять упираемся во вторую причину.
                                            • –1
                                              Дело в том, что в языках с ГЦ нету памяти. Как там обстоят дела с placement new и иже с ним.

                                              Пул объектов?


                                              сишным строкам?

                                              Сишниые строки в самом C/C++ редко использую, т.к. strlen пробегает по всей строке и это медленно.


                                              Мы либо будем долбить пуши в массив, либо инициализировать его два раза.

                                              А в C++ не так? В критичных местах в C++ да же предварительно не очищается память, просто помечаем как "свободную".

                                              • +1
                                                Пул объектов?

                                                Дело в том, что память и объекты — это разные вещи. Объекты могут и обычно несут в себе какую-то логику инициализации. Память же — это просто память.

                                                Сишниые строки в самом C/C++ редко использую, т.к. strlen пробегает по всей строке и это медленно.

                                                Сишные строки как раз-таки и используются. Длинны в С++-строке это просто некая оптимизация получения длинные и не более того.

                                                Есть всякие поиски подстрок, поиски символов и прочее. А медленно — это только в кейсе получения длинны. Остальные операции со строками требует прохода по ней и проверка символа на ноль — операция с нулей(в большинстве случаев) стоимостью. Какая разница что делать — сравнивать два указателя, либо сравнивать текущий символ с нулём?

                                                А в C++ не так?

                                                Да, в С++ это не так. Можно выделить кусок памяти не создавая объектов, а потом создать их в уже выделенной памяти во время её обхода.

                                                В критичных местах в C++ да же предварительно не очищается память, просто помечаем как «свободную».

                                                Что понимается под «очищением памяти»? Возврат страниц в систему? Подобное поведение свойственно любым менеджерам памяти. В реализации почти всех языков берут какой-нибудь jmalloc/tcmalloc/gnumallo, либо свой велосипед. Поэтому менеджер памяти в других языках не обладает какими-то резко другими свойствами.
                                                • 0
                                                  Объекты могут и обычно несут в себе какую-то логику инициализации.

                                                  Дело было в placement new, который в C++ вызывает конструирование объекта. А конструирование объекта, это инициализация.


                                                  Длинны в С++-строке это просто некая оптимизация получения длинные и не более того.

                                                  Только в C++ строки это контейнер, у которого есть lenght и capacity. И если хочу "очистить строку", то сбрасываю длину. И алгоритмы оперирующие на длину, а не на нуль символ, как работали так и работают. Поэтому да же в программах на Си, можно увидеть свою реализацию строк.


                                                  Можно выделить кусок памяти не создавая объектов, а потом создать их в уже выделенной памяти во время её обхода.

                                                  И кроме как embedded это особо никому не нужно, т.к. можно исользовать пул объектов.


                                                  Что понимается под «очищением памяти»?
                                                  Какой-нибудь memset с 0.
                                                  • +2
                                                    Дело было в placement new, который в C++ вызывает конструирование объекта. А конструирование объекта, это инициализация.

                                                    Вот именно! В С++ есть возможность взять область памяти из пула и инициализировать ее как объект путем вызова конструктора через placement new.


                                                    В C# или Java (пример языков с GC) такой возможности нет — а значит, нужно выносить логику инициализации из конструктора в метод инициализации. При этом теряются многие языковые фишки — к примеру, поля только для чтения (readonly/final) — а ведь на их неизменяемости могут быть завязаны оптимизации на уровне JIT. Получается, оптимизация аллокаций может не ускорить программу, а замедлить.


                                                    Наконец, выделение объектов в пул — это еще и необходимость строго следить за ссылками на них, это нечто противоположное всему прошлому опыту программирования на языке со сборщиком мусора.

                                                    • –1

                                                      Лично в Go таких проблем нет, потому что в нем есть Go sync.Pool , который вовсю используется в fasthttp. Который показал неплохой результат в прошедшем highloadcup.

                                                      • +1
                                                        В приведенном вами посте рассматривается пул простых объектов — массивов байт. Буферы в виде массивов байт неплохо размещаются в пуле в любом языке программирования, не только в Go.

                                                        Но изначально-то шла речь о пулах произвольных объектов.
                                                        • –1
                                                          sync.Pool универсальный способ. Ему все равно, что хранить.
                                                          • +1
                                                            Да, но разработчику не все равно какие объекты помещать в пул.
                                                    • 0

                                                      А зачем занулять память(кроме конечно всяких сверхсекретных данных типа ключа шифрования)?

                                                      • 0
                                                        Чтобы данные были преинициализированы. Те же логические переменные. Ссылки/указатели тоже.
                                                        • +1

                                                          Что бы легче найти косяки с работой памяти. Если у тебя в памяти мусор, то программа на C++ еще может делать вид, что работает. А если попробует записать что-то в nullptr, то это сразу будет видно.

                                                          • –1
                                                            Что бы легче найти косяки с работой памяти.

                                                            Это ложная уверенность, а любая ложная уверенность способствует большему кол-во ошибок, нежели что-то иное.

                                                            А если попробует записать что-то в nullptr, то это сразу будет видно.

                                                            В этом вся суть — мы взяли какой-то кейс и выдали его за все косяки, либо за какую-то весомую их часть. Это не так.

                                                            Разименование адреса «на прямую» достаточно редкий кейс, особенно в современном С++. new через new — уже не модно — это не жава.

                                                            Нулевой this вызывать функции позволяет, любые поля, кроме нулевого — будут не nullptr(скорее всего оно попадёт в нулевую страницу, но это просто особенность модели памяти и вас тут просто повезло, ведь вы говорили о nullptr). Все филды после 4к — уже мимо. Массивы — мимо.

                                                            Плюс, есть много кейсов, где это только навредит. Получили в оффсет ноль — не получили сегфолт, а получили мусор — получили сегфолт.
                                                            struct{arr[10];}.arr[offset] = 123;


                                                            На самом деле в любой программе используется малая часть адресспейса. Сколько там там памяти на топовой ноде? Одна миллиардная доля? Даже если это мы заммапим сотни петабайт — это копейки. Это даже не один процент.

                                                            Исходя из всего этого шанс разименовывая мусор попасть не на сегфолт достаточно туманны.

                                                        • 0
                                                          Дело было в placement new, который в C++ вызывает конструирование объекта. А конструирование объекта, это инициализация.

                                                          Дело было не в этом. Зачем обманывать?

                                                          Был определён кейс «выделить память и инициализировать», допустим — инициализировать очередь для обработки. На самом деле даже это неважно — просто инициализировать память.

                                                          В языка с ГЦ память будет проинициализирована ДВА раза. В С++ один раз. Плюс, ещё остаётся открытый вопрос о том, можно ли вообще аллоцировать массив за одну аллокацию.

                                                          Только в C++ строки это контейнер

                                                          Строк в С++ нет, как и в си. Есть std::string, но строки на нём не заканчиваются. Даже если поверить в то, что строки есть, так же в С++ есть сроки из си. С какой стороны не взгляни — утверждение неверное.
                                                          у которого есть lenght и capacity. И если хочу «очистить строку», то сбрасываю длину.

                                                          И что же из этого следует? Какое отношение это имеет к моей цитате? Это её опровергает, либо что?

                                                          *str = 0; Имеет ту же семантику. Дальше что? std::string, кстати, делает то же самое — сишное *str = 0, только к этому изменяет ещё size.

                                                          И алгоритмы оперирующие на длину, а не на нуль символ, как работали так и работают.

                                                          Начнём с того, что ничего не запрещает std::string изменять «нуль-символ». Т.к. в крестах нет реаллока, а у c_str() сложность константная, то срока обязана иметь капасити +1 для нуля. Уже давно даже data() нуль-терминированная.

                                                          Таким образом через что не возьми данные из std::string — сишные функции работать будут. Тут вы соврали.

                                                          Если вы хотели привести контр-пример, то надо было приводить нуль-символы в строке. Но опять же есть memchr(), memcpy().

                                                          Поэтому да же в программах на Си, можно увидеть свою реализацию строк.

                                                          Поэтому — это почему? Из-за lenght у std::string в С++? Сомневаюсь. Поэтому явно от С++ не зависит.

                                                          А оптимизацию для lenght можно прикрутить к чему угодно. И из этого мало что следует.

                                                          Велосипеды-обёртки поверх сишных строк строками не являются.

                                                          И кроме как embedded это особо никому не нужно, т.к. можно исользовать пул объектов.

                                                          Не можно. Это раз, а два — к чему ваши оценки? Вы говорили о чём? О том, что можно делать то же самое, а теперь вы пытаетесь избавиться от неудобных примеров оптимизаций.

                                                          Ну и голословные утверждения про «не нужно» вы так же чем-то сможете подтвердить?

                                                          Какой-нибудь memset с 0.

                                                          Это не очищение памяти. И с по каким таким причинам она вдруг должна «очищаться»?

                                                          • +1
                                                            Таким образом через что не возьми данные из std::string — сишные функции работать будут. Тут вы соврали.

                                                            В std::string вполне может быть нулевой символ до символа на позиции size, std::string продолжит работать, а сишные функции — нет.

                                                            • –1
                                                              Если вы хотели привести контр-пример, то надо было приводить нуль-символы в строке. Но опять же есть memchr(), memcpy().

                                                              Это что такое?
                                                              • 0
                                                                Я, неверное, непонятно и глупо ответил. Чтобы подобно не повторялось — дополню.

                                                                Дело в том, что отвечающий в ответе на мой комментарий описал ту ситуацию, которая уже была описана мною в том комментарии, на который он, собственно, и ответил.
                                                                • 0

                                                                  А memcat какой-нибудь тоже есть, или memlen?


                                                                  Если вы хотите нулевые символы в строке, то вам неизбежно придётся таскать рядом размер этой строки.

                                                                  • 0
                                                                    А memcat какой-нибудь тоже есть, или memlen?


                                                                    Изначально было про то, что сишные функции для работы со строками в рамках std::string не применимы по причине отсутствия нуля после ресайза. Я доказал, что это не так.

                                                                    Далее я помог автору комментария и привёл реальный пример( валидный для std::string), но невалидный для сишных строк. Вы зачем-то пришли и повторили мне то, что я итак сказал до вас. Зачем?

                                                                    А теперь играете в игру «игнорирую всё» — зачем?

                                                                    Если вы хотите нулевые символы в строке, то вам неизбежно придётся таскать рядом размер этой строки.

                                                                    Зачем мне нулевые символы? Ну хорошо — придётся таскать, и?

                                                                    Это отменяет что-то ранее сказанное мною, либо противоречит чему-то? Это ответ на моё утверждение намного выше?

                                                                    Длинны в С++-строке это просто некая оптимизация получения длинные и не более того.

                                                                    Это? Ну дак надо отвечать на тот комментарий, а не на другой.

                                                                    Да и как я уже сказал — нулевые символы это больше надуманный кейс, чем реальный. От того я его и не рассматриваю. Если хотите — пусть будет length ещё и для хранения нулевых символов. Сломается ::data(), ::c_str(), но что поделать.
                                                                    • 0

                                                                      Прошу прощенья, если не уловил контекст целиком.


                                                                      Что до надуманности нулевых символов в строке — мне с достаточной степенью регулярности такое встречается.


                                                                      data/c_str не сломается, кстати. std::string other { str.data(), str.size() } вполне корректно отработает, просто надо быть осторожным.

                                                                      • 0
                                                                        data/c_str не сломается

                                                                        Сломается — это будет совсем не строка, что ожидается.
                                                                        • 0

                                                                          Кем ожидается? Разве в стандарте написано, что там не будет нулей?

                                                                          • +1
                                                                            От ::data()/::c_str() ожидается нуль-терминированная строка аналогичная строке(std::string, у которой методы вызваны), а не её подстрока.

                                                                            • +2

                                                                              Так она вся и возвращается. А что там нули посередине — ну извините.

                                                                              • 0

                                                                                Таки возвращается не строка, а указатель на её первый символ. А дальше может быть что угодно.

                                                  • +1
                                                    Что мешало всем остальным языкам «оптимизировать» «свои» «алгоритмы»? Я уж не буду говорить о том, что именно язык прямо и косвенно влияет на возможность оптимизаций. Косвенно через компетенцию автора в данном контексте, а прямо через возможности языка.
                                                    Приведу простые примеры. Откуда у человека, который писал на готовом веб-стеке на том же пхп/ноде есть необходимые навыки сетевого программирования?

                                                    Здесь не совсем понял, кого вы защищаете, а кому пеняете.


                                                    • Си — КМК всё ясно, это язык, тождественный языку интерфейса системных библиотек. К тому же, до недавнего времени ему по факту не было альтернативы для таких задач.
                                                    • Го — следуя вашей логике, откуда у человека, который писал на готовом net/http, необходимые навыки сетевого программирования?
                                                    • Rust — упомянутые вами средства оптимизации там есть, но их надо применить; было ли это сделано в топовом решении — я не знаю.
                                                    • А вообще в топ-50 попало решение на Perl — что тоже кое о чём говорит.

                                                    Или уточните, о чём речь.


                                                    Как вы будите оптимизировать аллокации на языке с ГЦ?

                                                    Эээ… пулинг и буферы, как и без ГЦ? Если конечно стек на этом языке такое позволяет.


                                                    А с неуправляемым ГЦ?

                                                    Это как? ГЦ который сам в истерике выделяет и удаляет память? :)

                                                    • 0
                                                      Здесь не совсем понял, кого вы защищаете, а кому пеняете.

                                                      Я никого конкретного не защищаю. Защищаю одно — объективность.

                                                      Си — КМК всё ясно, это язык, тождественный языку интерфейса системных библиотек. К тому же, до недавнего времени ему по факту не было альтернативы для таких задач.

                                                      Почему же? Какой-нибудь с89 не далеко ушел от паскаля, да и сотни других языков из той же весовой категории. Правда, они ничего весомого предложить не могли, но что поделать. Это свойство «эпохи».

                                                      В те времена у си даже основной киллерфичи не было — сильного компилятора. И выиграть его было проще. Т.е. ситуация была аналогична той, что есть сейчас. Только сейчас ллвм на халяву даёт такой компилятор кому угодно. Сколько языков повылазило( в частности раст).

                                                      Го — следуя вашей логике, откуда у человека, который писал на готовом net/http, необходимые навыки сетевого программирования?

                                                      Да, с го в данной ситуации история та же, что и с пхп/нодой. Я просто привёл более понятные примеры.

                                                      Rust — упомянутые вами средства оптимизации там есть, но их надо применить; было ли это сделано в топовом решении — я не знаю.

                                                      В этом и есть суть. Во многих языках возможности оптимизации аналогичные с89 и даже выше, но они не выстрелили, либо умерли. И этих возможностей мало — нужно её уметь ими пользоваться.

                                                      Поэтому это и даёт возможность людям умеющим оптимизировать код — делать это на любом языке, но — есть нюансы. И я их так же определил. Что бы вы не делали, но если язык не позволяет опуститься на тот уровень, дать ту свободу, что нужна для оптимизации — вы это не реализуете.

                                                      А вообще в топ-50 попало решение на Perl — что тоже кое о чём говорит.


                                                      github.com/Mons/hlcup/blob/master/libs/Local-HTTPServer/picohttpparser.c — это что? А сколько сишного рантайма в перле?

                                                      Понимаете в чём штука, когда мы делаем подобным бенчмарки — их смысл в чём? Смысл в том, чтобы выявить инструменты и людей, которые способны реализовывать кастомные решения.

                                                      Можно взять готовый сишный код парсера хттп, прикрутить его к питону. А если завтра нам нужно будет не хттп? Мы так же будем искать сишное решение? А если его нет? Будем писать на си?

                                                      То же самое и со всем остальным. Надо отличать дефолтные кейсы от возможностей языка в общем. Ведь когда эти дефолтные кейсы кончаются — начинается ахтунг.

                                                      Эээ… пулинг и буферы, как и без ГЦ? Если конечно стек на этом языке такое позволяет.

                                                      Я выше отвечал по этому поводу.

                                                      Это как? ГЦ который сам в истерике выделяет и удаляет память? :)

                                                      Я не эксперт в ГЦ, но я видел некие хинты для гц в языках. Возможно где-то пошли ещё дальше.
                                                      • 0

                                                        Так, я уже перестал понимать, спорим ли мы с вами или говорим об одной вещи разными словами.


                                                        Вы начали с того, что топовое решение на Rust попало только на 31-е место, а вот на Go — аж на 15-е (потом поправили что на 11-е).


                                                        Я возразил, что на таком уровне вопрос уже не в языке, а в конкретных оптимизациях.


                                                        И вы начали аргументировать, что чем толще рантайм и слой абстракций в самом языке, тем больше проблем такие оптимизации делать. С чем я согласен.


                                                        Просто уточню, что решение на Rust (раз уж за него зацепились) отставало от 1-го места по очкам менее чем вдвое. И именно в этом контексте я и написал, что в таких условиях решает не язык, а конкретные оптимизации.

                                                        • 0
                                                          Вы начали с того, что топовое решение на Rust попало только на 31-е место, а вот на Go — аж на 15-е (потом поправили что на 11-е).

                                                          Это был не я.

                                                          Просто уточню, что решение на Rust (раз уж за него зацепились) отставало от 1-го места по очкам менее чем вдвое. И именно в этом контексте я и написал, что в таких условиях решает не язык, а конкретные оптимизации.

                                                          Именно про это я и говорю, но. Конкретные оптимизации зависят от языка и я, так же, сказал почему.

                                                          Я спорю именно с тем, что оптимизации — это какая-то вещь в себе. Нет. Для оптимизаций нужны возможности, в том числе, со стороны языка.

                                                          Точно так же, как выбор языка не делается просто так. Он обусловлен той областью, в которой работает человек. Т.е. язык косвенно влияет и на опыт человека и на его компетенцию в данной теме.

                                                          • 0
                                                            Это был не я.

                                                            Да, точно. Извиняюсь.


                                                            Именно про это я и говорю, но. Конкретные оптимизации зависят от языка и я, так же, сказал почему.

                                                            Я спорю именно с тем, что оптимизации — это какая-то вещь в себе. Нет. Для оптимизаций нужны возможности, в том числе, со стороны языка.

                                                            Точно так же, как выбор языка не делается просто так. Он обусловлен той областью, в которой работает человек. Т.е. язык косвенно влияет и на опыт человека и на его компетенцию в данной теме.

                                                            Эээ… ну да, я согласен. Засим, думаю, ветку можно прикрывать.

                                                  • +2
                                                    Алгоритмы вообще не решали.
                                                    Все упиралось в сеть и хаки с epoll. Я в финальном был в конце (43-м), после пилюли с epoll(0) — сейчас в песочнице на 25 (учтите, что почти все ее приняли после слива в чате в последний день).
                                                    Если бы я знал о пилюле epoll(0) в день финала, мое решение было бы на 7-ом месте. Но так нельзя говорить, т.к. еще 25 человек из топ-50 могут сказать то же самое.
                                                    • 0

                                                      Мне понравилась идея, предложенная на следующий хайлоад. Постепенно поднимать RPS. Выиграет тот, кто дольше всех проживёт, не выходя за SLA.

                                                      • –2
                                                        Все упиралось в сеть и хаки с epoll.

                                                        Почему же хаки? Знание и использование особенностей работы.
                                                        • +2
                                                          Потому что в реальных задачах epoll(0) — это не оптимизация, а напрасное пережигание электричества.
                                                          • –1
                                                            А jvm — это не напрасное прожигание электричества? А пхп — это не напрасное прожигание электричества? А жсон, хттп и прочее — это что? Это такое же напрасное прожигание электричества.

                                                            Ведь вы же измеряли цпу-время разных решений, да и в реальном мире производительность любых решений покупается в основном за счёт электричества и железа.

                                                            А подобные кастыльные парсеры жсона так же используются в реальных задачах? А подобные роутеры так же используются в реальных задачах? Что же вы так избирательны.

                                                            Я не знаю причины по которым вы говорите то, что говорите. Но я предполагаю то, что причина тут явно не в реальных задачах, не в электричестве, а в неком недовольстве/обиде на тех, кто этот «хак» знал.

                                                            • 0
                                                              Ведь вы же измеряли цпу-время разных решений

                                                              Ведь вы же НЕ измеряли цпу-время разных решений. Естественно.
                                                              • –2
                                                                Кто смелый — кто сможет аргументировать за минусы? А то получается странно. Минусы есть, а ответов нет. В такой ситуации ведь не скажешь, что «я просто несогласен, а ответ уже итак дан другим участником и я с ним согласен». Почему вы поступаете так несправедливо?
                                                                • +4
                                                                  С точки зрения сожжения CPU — безусловно и epoll(0) и пхп с jvm жгут его с КПД разной степени паршивости. Но из прикладных соображений — я представляю решение на пхп в проде, но с epoll(0) уже нет, даже банально по ssh туда влезть не удастся. Я не минусил (и не умею ;)
                                                                  • –1
                                                                    С точки зрения сожжения CPU — безусловно и epoll(0) и пхп с jvm жгут его с КПД разной степени паршивости.

                                                                    Это не ответ. Было определено два тезиса: «Потому что в реальных», «а напрасное пережигание электричества».

                                                                    Что из этого следует? А то, что всё что не из множества «реальных задач» и всё, что «напрасно пережигает электричество» неприменимо и является хаком.

                                                                    Мною были выдвинуты контрпримеры — на что последовало молчания. Если не можете в аргументацию своих тезисов, то зачем это вообще начинать?

                                                                    я представляю решение на пхп в проде

                                                                    Опять же. Вы нарушаете условие. Применимы-ли в проде парсинг жсона и хттп так, как это сделано в некоторых(почти всех) решениях? Нет. Ну дак зачем вы суёте сюда то, чем решения заведомо не соответствует.

                                                                    Так же, не важно то, что вы представляете, а что нет. Пхп просто так жрёт электричество? Жрёт. Под критерий определённый автором первоначального утверждения подходит? Подходит. Всё остальное — отношения к делу не имеет.

                                                                    Вы можете начать рассуждать о том, что рабсила дороже электричества, как какие-то иные критерии дороже, но. За это решение покупается производительность. Точно так же, как в пхп покупается что-то другое.

                                                                    Никто не определил, что производительность — это «неважный» критерий, а что-то иное важный.

                                                                    но с epoll(0) уже нет, даже банально по ssh туда влезть не удастся.

                                                                    Не верно. Епул долбит не во всех потоках одновременно, а даже если во всех — есть планировщик. Никакой ссш у вас не сломается.

                                                                    Ну и вообще, кто вас сказал о том, что epoll(0) заканчивается на захардкоренном нуле? Ничего вам не мешает считать время проведённое в обработчике и на основе данной статистике рулить этим аргументом.

                                                                    Я не минусил (и не умею ;)

                                                                    Я говорю не про вас, а про тех, кто минусует без аргументации.
                                                                    • +4
                                                                      >Это не ответ. Было определено два тезиса: «Потому что в реальных», «а напрасное пережигание электричества».
                                                                      Это не я отвечал если что ).
                                                                      Мой тезис был такой — epoll(0) это хак. И конечно это хак! Смотрите — одно и то же решение: 43 место без epoll(0), 7 с epoll(0), изменена одна строка в коде! Это по вашему филигранная работа и особенности? Мдмашка в чистом виде.
                                                                      >Опять же. Вы нарушаете условие. Применимы-ли в проде парсинг жсона и хттп так, как это сделано в некоторых(почти всех) решениях? Нет. Ну дак зачем вы суёте сюда то, чем решения заведомо не соответствует.
                                                                      Тут и комментировать нечего. Конечно применимы, в реальных хайлод все это сплошь и рядом.
                                                                      >Не верно. Епул долбит не во всех потоках одновременно, а даже если во всех — есть планировщик. Никакой ссш у вас не сломается.
                                                                      Во всех где вызываешь epoll(0) (а это все ядра, иначе зачем он вам вообще нужен в любом виде в реальном проде?). Из личного опыта знаю, что когда все ядра заняты на 100% (а именно это происходит) доступ по ssh становится невозможным.

                                                                      И вообще вы какой-то нервенный… Судя по нику — из-за расклада с PHP в highloadcup-е? Ну так там и нода утерлась, не переживайте вы так.
                                                                      • –1
                                                                        Это не я отвечал если что ).

                                                                        Это неважно. Вы отвечаете в том контексте, которые определили до(после вас) вас. В нём же отвечал и я. Менять условия нельзя.

                                                                        Мой тезис был такой — epoll(0) это хак. И конечно это хак! Смотрите — одно и то же решение: 43 место без epoll(0), 7 с epoll(0), изменена одна строка в коде!

                                                                        Ну дак перечитайте то — на что я отвечал и кому. Зачем менять условия?

                                                                        Вам не нравится одна строчка? Ну что поделать.
                                                                        Это по вашему филигранная работа и особенности? Мдмашка в чистом виде.

                                                                        Это не работа — это знание особенной того окружения, в котором работаете.

                                                                        Тут и комментировать нечего. Конечно применимы, в реальных хайлод все это сплошь и рядом.

                                                                        Тогда это решение точно так же применимо в хайлоаде. Профит даёт.

                                                                        Ну и не понятно так же. Всё это соответствует критериям, которые определили выше. И определил их не я. Вы точно так же оспорили это: habrahabr.ru/post/338268/#comment_10428330

                                                                        В чём причина ваших ко мне претензий?

                                                                        Во всех где вызываешь epoll(0) (а это все ядра, иначе зачем он вам вообще нужен в любом виде в реальном проде?).

                                                                        Епул не обязательно долбить из всех потоков.

                                                                        Из личного опыта знаю, что когда все ядра заняты на 100% (а именно это происходит) доступ по ssh становится невозможным.

                                                                        Не знаю что там у вас за опыт, но такого не может быть по определению. Да и мой опыт говорит прямо об обратном.

                                                                        Даже на десктопе я могу пустить этот вайлтру в скольки угодно тредах и это мне не помешает не то что по ssh зайти, а смотреть ютуб и писать на хабре. При этом это полностью стоковое ядро.

                                                                        И вообще вы какой-то нервенный… Судя по нику — из-за расклада с PHP в highloadcup-е? Ну так там и нода утерлась, не переживайте вы так.

                                                                        Не из-за этого. Я уже описал причины.
                                                                        • +1

                                                                          К слову, epoll(0), или BUSY_WAIT приводит к тому, что еполл сразу возвращается, независимо от кол-ва сработавших событий. Это приводит к тому, что текущий поток крутится в чём-то наподобие спинлока вместо того, чтобы сразу отдать остатки кванта. И да — это даёт прирост — но только в очень конкретных сценариях. И таки да, в нормальном продакшене так не делают, т.к. вместо прироста будет просадка.

                                                                          • –1
                                                                            К слову, epoll(0), или BUSY_WAIT приводит к тому, что еполл сразу возвращается, независимо от кол-ва сработавших событий.

                                                                            Я знаю о том, что произойдёт при epoll(0). Хотя формулировка не верна — кол-во событий на ожидание не влияет — любой событие будет епул.

                                                                            Это приводит к тому, что текущий поток крутится в чём-то наподобие спинлока вместо того, чтобы сразу отдать остатки кванта.

                                                                            Кому? Дяди Васи? Что конкурирует с епуллом по ЦПУ? Неужели обработчик евентов в том же треде(как это делается во всех реализация, что я видел)?

                                                                            И таки да, в нормальном продакшене так не делают, т.к. вместо прироста будет просадка.

                                                                            Какие ваши доказательства?
                                                                            • 0
                                                                              Кому? Дяди Васи? Что конкурирует с епуллом по ЦПУ? Неужели обработчик евентов в том же треде(как это делается во всех реализация, что я видел)?

                                                                              Прежде всего — блокирующие операции, которые вынуждены выполняться в фоновых потоках. Скажем, у gethostbyname нет асинхронного варианта. Надо либо использовать сторонние библиотеки — либо выносить вызовы в отдельные потоки.


                                                                              Далее — любые отложенные задачи. Которые могли бы нормально выполниться в периоды простоя (например, в 3-4 ночи по времени основной аудитории).


                                                                              Служебные программы. Например, мониторинг. Или уже упомянутый тут ssh.


                                                                              А еще процессор может уменьшить свое энергопотребление если никто не крутит его в вечном цикле.

                                                                              • –2
                                                                                Прежде всего — блокирующие операции, которые вынуждены выполняться в фоновых потоках.

                                                                                Какие такие операции и в каких таких потоках? Поподробнее об этом.

                                                                                Далее — любые отложенные задачи. Которые могли бы нормально выполниться в периоды простоя (например, в 3-4 ночи по времени основной аудитории).

                                                                                Им ничего не мешает выполнятся — планировщик устроен так, что он даёт равное время на исполнения всем процессам/потокам. Вам об этом сообщили, но вы продолжаете всё игнорировать и гнёте свою линию.

                                                                                При этом — захардкоренный ноль — это лишь дыра в реализации участников, которые не смогли/не захотели это реализовать иначе.

                                                                                Банальное ev = epoll_wait(..., !ev * timeout); решает почти указанные проблемы, а если пойти чуть дальше — это решает все проблемы.

                                                                                Хак — это как максимум реализация участников, а не сам ноль в таймауте.

                                                                                Служебные программы. Например, мониторинг. Или уже упомянутый тут ssh.

                                                                                Ваши отсылки к нерабочему ssh — не соответствуют действительности. Повторю уже в какой раз. Не знаю откуда вы это взяли, но это неправда.

                                                                                А еще процессор может уменьшить свое энергопотребление если никто не крутит его в вечном цикле.

                                                                                А ещё процессор может уменьшить своё энергопотребление, если не использовать пхп, либо жаву. Дальше что? И опять же — вы продолжаете все мои доводы игнорировать. Зачем?

                                                                                Если есть какие-то критерии по энергопотребленю — с них и надо начинать. А с них никто не начинал и о них никто не говорил. Вы их откуда-то достали и сделали каким-то определяющими.

                                                                                Да и как минимум, перед тем как заявлять — надо посчитать сколько потребляет ЭЭ те же решения на пхп на той же нагрузке, а потом сравнить — стоит ли это меньше, чем пару часов epoll(0). Хотя опять же, данная ситуация — ваши выдумки. Это свойство не epell(0), а отдельных реализация. И я уже говорил почему.

                                                                                • –1
                                                                                  Хак — это как максимум реализация участников, а не сам ноль в таймауте.

                                                                                  Бинго!

                                                                                  • –4
                                                                                    Есть нюансы.

                                                                                    Потому что в реальных задачах epoll(0)

                                                                                    Это мой промах, конечно. Забыл вас спросить о том, что такое epoll(0) для того, чтобы защитить себя от подобного.

                                                                                    Дело в том, что из epoll(0) следует именно epoll(0). Вы говорили именно об epoll(0), а не захардкоривании там нуля. И это очень просто доказывается, в частности, этим:

                                                                                    Если бы я знал о пилюле epoll(0) в день финала, мое решение было бы на 7-ом месте.

                                                                                    Т.е. если бы epoll(0) был бы реализован не путём захардкоренного нуля, а иначе( допустим, как показал выше я) — вы бы получили тот же результат. Но — без всех тех свойств, которые вы приписывали epoll(0). Это мат.

                                                                                    Именно результат вы определили за хак, именно то, что позволяет его добиться — вы определили за хак. Позволяет не ноль, не реализация, а именно epoll(0). Реализация — это нюансы.

                                                                                    И теперь, когда я на это вышел — вы переобулись, но это неправильно и некрасиво.

                                                                                    • 0

                                                                                      А теперь прочитайте еще раз, кто именно писал разные комментарии. Спойлер: у вас был не один оппонент.

                                                                                      • 0
                                                                                        А теперь прочитайте еще раз, кто именно писал разные комментарии. Спойлер: у вас был не один оппонент.

                                                                                        Не верно. Если вы не определяете контекст — он экспортируется из ветки выше. Такие правила. Если я что-то написал в каком-то контексте — нельзя от него(вам) откреститься и сказать «я это не писал». Так это не работает. Я отвечал в рамках него. Вы отвечали точно так же в рамках него и никак иначе.

                                                                                        Если проще. Вы попытались наделить мою цитату своими смыслами, которых в ней не было. В частности:

                                                                                        Потому что в реальных задачах epoll(0) — это не оптимизация, а напрасное пережигание электричества.

                                                                                        Вы взяли определение epoll(0) у автора ветки — иного быть не может. Своего вы не дали.

                                                                                        Вот я дал своё определение — разграничил epoll(0) и паттерн его использования. Поэтому теперь я могу использовать и то и то отдельно.

                                                                                        Вы же этого не сделали — со всеми вытекающими. И теперь это выглядит обычной манипулцией.

                                                                                        • 0
                                                                                          Из контекста лично мне очевидно, что речи про алгоритм выбора тайм-аута для epoll не шло. Просто в какой-то момент часть участников услышала про нулевой тайм-аут, заменила у себя тайм-аут нулевым — и за счет этого резко поднялась в рейтинге.

                                                                                          Это и есть хак.
                                                                                          • –1
                                                                                            Из контекста лично мне очевидно, что речи про алгоритм выбора тайм-аута для epoll не шло.

                                                                                            Про алгоритм выбора таймаута речи и не идёт.

                                                                                            Просто в какой-то момент часть участников услышала про нулевой тайм-аут, заменила у себя тайм-аут нулевым — и за счет этого резко поднялась в рейтинге.

                                                                                            Точно так же они могли узнать про что угодно.

                                                                                            Ваши обвинения были в чём? В неприменимости нулевого таймаута — это не так. Нулевой таймаут не является хаком.

                                                                                            И результат не является хаком. Я так же об этом писал с ссылками на инициатора ветки.

                                                                                            Хаком назывались именно ПОЛУЧЕННЫЕ РЕЗУЛЬТАТЫ, а не метод их получения. И вы сами же этому вторили своими заявлениями про неработоспособность нулевого таймаута.

                                                                                            Это и есть хак.

                                                                                            Это подмена понятий.

                                                                                            Нулевой таймаут — не хак. Нулевой таймаут — оптимизация. Нулевой таймаут не ограничивается захардкоренным нулём. Изначально определялось критерием хака РЕЗУЛЬТАТ. Вы выше это повторили.

                                                                                            А я говорил о том, что как максимум хаком можно назвать захардкоренный ноль. Но это ничего не меняет и не захардкоренный показал бы то же самое и точно так же вывел бы в топ.

                                                                                            Просто задача ограничить цпу-время не стояло — этим никто и не заморачивался. Я не верю в то, что участники из топ10 не осилили бы реализовать это иначе.

                                                                                            Поэтому такая реализация — это лишь свойство задачи. Вернее задача не требовала по иному её решать. Вы же взяли совершенно левые условия и начали с ними что-то качать. Так это не работает.

                                                                                            Люди создавали решения в рамках конкретных условий. И я уже доказал то, что реализовать это и для ваших условий труда не составляло. С тем же результатом.
                                                            • 0

                                                              del

                                                        • –1

                                                          Что есть "чистый"? Моя ж гошка на 11 месте.

                                                          • 0
                                                            Я твою и имел ввиду (сейчас в песочном 7-ом «грязная» с хаком на С++). В финале твоя на 11, без вопросов.
                                                            • 0
                                                              Ах вон оно что. Я уже как-то не слежу. Надо будет глянуть, что там за хак.
                                                              • 0
                                                                Хм, я теперь думаю что (Go -> C++) — это чувак полностью переписал на С++, а не Го с хаком на С++ )
                                                                • 0
                                                                  Возможно)
                                                                  Или там func main() { C.realization() } :)
                                                        • +5
                                                          Автор — молодец, нашёл ошибку там, где другие и не заметили. Даже и с учётом того, что Rust — всё ещё язык экзотический и хорошо разбирались в нём из 45 тыс. просмотревших 45 в лучшем случае. И тем не менее сенсации не получилось:
                                                          1. Голый net/http не используется в highload проектах на golang
                                                          2. Этот тезис я писать не буду, а то ребята из Mail.ru обидятся :-D
                                                          • +9
                                                            Голый net/http не используется в highload проектах на golang

                                                            Хороший тезис! hyper тоже не самая быстрая библиотека. Именно поэтому я тестил на древней версии rustc с дешевым hyper, а не пытался заоптимизировать код по самое не могу.


                                                            Изначальный посыл автора оригинальной статьи был хорош: для каждого языка взять "стандартный компонент" для работы с http, пострелять в него и посмотреть на оверхед рантайма и кривизну библиотеки/языка. Вот мы и посмотрели.

                                                            • +7
                                                              Если уж на то пошло, то лучше было бы протестировать и на свежих версиях — оба языка не сидели на месте все это время.
                                                            • 0

                                                              лайк за второй пункт.


                                                              я изначально и написал читаемый пример на Crystal, а страдать уйней не стал )

                                                            • +7
                                                              Заголовок не соответствует содержанию. Правильно было назвать статью так: «Mail.ru опять обобралась». Куда этим людям поисковик делать если они публично позорятся в каждой публикации.
                                                              • +1
                                                                А это были одни и те же люди?
                                                                • 0

                                                                  не все так просто )

                                                              • +6

                                                                Ради интереса попробовал хаскелевский Snap. 12.8 мс на запрос против гошных 12.5 на моей машине. Написать что ли тоже мини-статью? :)


                                                                А не могли бы вы ещё обновить пример с Rust, чтобы собрать его с современными зависимостями? Мне это сходу сделать не удалось, а в Rust я ни в зуб ногой, к сожалению.

                                                                • +3

                                                                  И это даже как-то пессимистично. Уменьшил число процессов до двух с автодетекта, получил 10.5 мс для хаскеля. Очень неплохо.

                                                                • +5
                                                                  Может быть глупость спрошу, я этих языков не знаю, но где в Go обработка not_found и проверка метода?
                                                                  • +1

                                                                    Привет! Спасибо за найденную ошибку, это действительно fail. Безусловно, это нужно будет исправить и обновить результаты замеров.


                                                                    Однако, раз уж ваш комментарий перерос в отдельную публикацию, хочется указать на некоторые моменты, которые вы решили не цитировать, но которые, тем не менее, определяют контекст сравнения.


                                                                    Самое главное – статья 2015 года не называлась "какой язык быстрее". Она была о выборе языка программирования, и производительность написанных серверов была лишь одним из критериев. Это написано прямо в самом первом ее абзаце.


                                                                    Если иметь это в виду, то разница, которую вы получили с исправлением, никак не влияет на наш выбор Go. Более того, в тестах так же есть обработчики без логики (GET /), в которых Rust был (и наверное есть) быстрее.

                                                                    • +8
                                                                      Самое главное – статья 2015 года не называлась "какой язык быстрее". Она была о выборе языка программирования, и производительность написанных серверов была лишь одним из критериев. Это написано прямо в самом первом ее абзаце.

                                                                      Перечитал. В статье большую часть объема занимает тестирование производительности.
                                                                      Производительность идет первым параметром в итоговой таблице для сравнения, строки отсортированы именно по нему. Причем Go уступает скале и ноде по всем приведенным параметрам, кроме все той же производительности.
                                                                      Дезинформировать читателей нехорошо.

                                                                      • –4

                                                                        Большую часть статьи занимают бенчмарки только по тому, что это наименее субъективная вещь. Сама статья, как это сказано в заключении, не имеет шансов быть объективной. Скажите, о какой дезинформации вы говорите?

                                                                        • +7
                                                                          это наименее субъективная вещь

                                                                          В которой у вас объективный эпик фейл.


                                                                          Скажите, о какой дезинформации вы говорите?
                                                                          "производительность написанных серверов была лишь одним из критериев"

                                                                          Фраза в кавычках — дезинформация в чистом виде. Производительность была главным критерием, а не просто "одним из".


                                                                          Если иметь это в виду, то разница, которую вы получили с исправлением, никак не влияет на наш выбор Go

                                                                          Это утверждение с помощью статьи проверить нельзя (в статье ранжирование в первую очередь по производительности очевидно, методики расчета общего рейтинга нет), так что тоже дезинформация. Нельзя ссылкой на статью обосновать то, чего там нет.

                                                                          • 0

                                                                            Как можно называть дезинформацией то, что субъективно? То, что из статьи вынесены субъективные оценки – на мой взгляд плюс, хоть и имеет побочные эффекты в виде ошибочных интерпретаций.


                                                                            Если бы ваше предположение о "главном критерии" было бы верным, то почему бы нам, например, не выбрать C или ассемблер?

                                                                            • +2
                                                                              Если бы ваше предположение о "главном критерии" было бы верным, то почему бы нам, например, не выбрать C или ассемблер?

                                                                              Очевидно потому, что они не удовлетворяли критериям отсева перед финальным сравнением. Вот финалистов вы отранжировали по производительности.


                                                                              Как можно называть дезинформацией то, что субъективно?
                                                                              "Содержание субъективных оценок было намеренно опущено в этой статье, дабы не делать очередной наброс и не провоцировать холивар. Тем более что если бы такие оценки не учитывались, то по критериям, указанным выше, результат остался бы прежним."
                                                                              Если иметь это в виду, то разница, которую вы получили с исправлением, никак не влияет на наш выбор Go

                                                                              1. В статье ранжирование было по производительности,
                                                                              2. После исправлений первое и второе места поменялись,
                                                                              3. Разница не поменяла выбора, для которого якобы достаточно информации из статьи.

                                                                              Проблема не в результате выбора, а в том, что после исправлений он перестал быть очевиден из статьи. Такие дела.

                                                                      • +14

                                                                        Как бы она не называлась, ущерб русскоязычному сообществу Rust вы нанесли. Нравится вам Go — пишите на нём. Кто ж спорит.

                                                                        • –3

                                                                          Не пойму вашей позиции. Мы выбрали язык, на котором вот уже два года пишем сервисы. На момент выбора – Go в почте не было. На момент выбора – большинство специалистов писали на C, Perl и JavaScript. Как я уже сказал, независимо от ошибки, которую вы нашли, мы бы все равно выбрали Go. И не потому, что выбор предвзят, а по совокупности разных критериев. Вы хотите, чтобы мы передумали и начали писать на Rust?

                                                                          • +6
                                                                            Вы свое решение уже приняли. Тут, видимо, вопрос о тех, кто только решает, что выбрать.
                                                                            • –1
                                                                              Выходит, человек поделившись своим трудом нанес ущерб сообществу Rust и навредил тем, кто решает что выбрать, я правильно понял?
                                                                              • +10

                                                                                Это больше похоже на подтасовку результата под Go

                                                                                • –4
                                                                                  Не драматизируйте. Это больше похоже на баг. Который кстати никто не заметил, что характеризует размер и активность сообщества rust.
                                                                                  • +8

                                                                                    Вы только доказываете, что была подтасовка в сторону Go, ведь там не было таких детских багов(всем известно, что регулярки медленные в любом языке), соответственно, автор не разобрался с Rust, а просто наваял побыстрому кода для большего хайпа.

                                                                                    • –7
                                                                                      Про хайп не знаю, я не спец в этом. Вряд ли хайп помог бы автору починить проблему на проде.
                                                                                      Про баги — там могло не быть детских багов по двум причинам: а) автор знает Go намного лучше чем Rust (это плюс в сторону выбора Go) и б) Go не допускает глупых ошибок (это плюс в сторону выбора Go).
                                                                                      • +2

                                                                                        В этом случае не стоит проводить тесты производительности вообще.
                                                                                        Что делаешь — делай хорошо.

                                                                                        • +1

                                                                                          Все верно. Пишешь код – пиши без багов!

                                                                                          • +4

                                                                                            Ерничать не нужно.
                                                                                            Если производительность не влияет на выбор, не надо включать ее в сравнение.
                                                                                            Если влияет, надо обеспечить минимальный уровень качества тестов.
                                                                                            Вы не обеспечили, причем за счет простой ошибки.
                                                                                            Если уровень знаний о расте настолько низок — его не должно было быть в финальном сравнении.

                                                                                        • +1

                                                                                          Ну да, круто засовывать всё в main и утверждать, что Go не допускает глупых ошибок.

                                                                            • –8
                                                                              Про ущерб сообществу это просто смешно. Оно слабое, в том числе по этой причине выбор был сделан правильно. Поясню:
                                                                              В изначальной статье 3им пунктом было «Большое сообщество, позволяющее быстро найти ответы на вопросы». Т.е. сильное сообщество нужно, чтобы делать задачи без лишних головняков, а также сильное ревью и так далее. Так вот, это сообщество нашло эту кажется простую ошибку спустя только 2 года. Выходит, проблема была и до статьи, что только добавляет очков к Go.
                                                                              • +11
                                                                                Так вот, это сообщество нашло эту кажется простую ошибку спустя только 2 года. Выходит, проблема была и до статьи, что только добавляет очков к Go.

                                                                                То есть если я найду в любой паблик статье про произвольный язык ошибку, я смогу говорить, что у него плохое сообщество — потому что не просматривает все статьи и не ищет в них ошибки с остервенением? Вот это реально был бы фанатизм.

                                                                                • +2

                                                                                  В времена оны сообщество Golang было в России отнюдь не на порядок сильнее сообщества Rust. И уж тем более сообщества C++11(14, 17).


                                                                                  Вообще, сейчас современный C++ идеален (не ущербен по сравнению с Go).