Comments 282
Простите, любители Scala и Node.js, этот холивар не про вас.

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

Ноду через модуль 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");

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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")

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

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

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

UFO landed and left these words here
Это было на хабре, просто автора совершенно справедливо вынудили спрятать такую чушь в черновики.

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

UFO landed and left these words here
Речь о коде для теста производительности, результаты которого используются в этой статье, на которую в свою очередь ссылаются в статье о Vapor.
Интересно, какой должен быть уровень паранойи, чтобы обходить и перепроверять все высказывания в статье, на которую ссылаешься мельком?

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


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

Хз, в highloadcup только два раста в топ-50 (причем лучший — на 31 месте), при этом 10 решений на Го (лучший «чистый» Го на 15-ом).
Это говорит скорее о том, что в Rust мало людей
Так-то я и Perl вывел в top 50 ;)
Это говорит скорее о том, что в Rust мало людей

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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


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

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

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

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

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

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

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

А в C++ не так?

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

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

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

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


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

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


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

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


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

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


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


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

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

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

Но изначально-то шла речь о пулах произвольных объектов.

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

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

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

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

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

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

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

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

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

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


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

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

Дело было в 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.

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

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

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

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

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

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

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


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

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


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

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

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

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

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

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

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

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

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

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


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


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

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

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

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

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

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

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


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

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


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

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


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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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


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


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


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


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

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

Это был не я.

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

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

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

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

Это был не я.

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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


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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Бинго!

Есть нюансы.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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


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

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

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


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

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

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

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


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


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


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

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

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

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

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

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


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

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


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

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

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


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

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

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


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

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

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

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

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

Вы свое решение уже приняли. Тут, видимо, вопрос о тех, кто только решает, что выбрать.
Выходит, человек поделившись своим трудом нанес ущерб сообществу Rust и навредил тем, кто решает что выбрать, я правильно понял?
Не драматизируйте. Это больше похоже на баг. Который кстати никто не заметил, что характеризует размер и активность сообщества rust.

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

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

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

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

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

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

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

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


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

«Идеален» и «не ущербен по сравнению с Go» — это всё-таки не синонимы, как бы не хотелось сторонникам Go :)


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

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

Действительно, как? Мне на самом деле кинули ссылку на статью как аргумент. Да, я в 2015 году пропустил эту статью, но тот человек — нет. И у него сформировалось определенное мнение на тему Go vs Rust.

Проблема в том, какое определённое мнение ваши реплики, а также Crandel, сформируют о самом сообществе Rust у присутствующих здесь. СпокойнЕе надо быть. Тем более, что ребята из мэйл.ру не встали в позу, а спокойно, как адекватные люди, сказали "Упс, да, ошибочка".

Тем более, что ребята из мэйл.ру не встали в позу, а спокойно, как адекватные люди, сказали "Упс, да, ошибочка".

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

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

Скорее всего, Вы недооцениваете важность и значимость самого процесса оценивания.

Вы бы все же сделали бенчмарк обновленных версий — интересно посмотреть, как поменялась производительность.
Ну и все же о Node и Scala не забывайте

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


Гораздо важнее вопрос синтаксиса. И тут к обоим языкам огромное количество притензий. Какие-то из недостатков важны в конкретных проектах, какие-то не очень, но по массе притензий к ним весьма много.

Претензии по части синтаксисиа скорее к расту.
У го отсуствие обобщений, присутсвие null, обязательный сборщик мусора вызывают куда больше вопросов, чем любые проблемы с синтаксисом.

Да я неудачно выразился. Скорее синтаксис и возможности.

Гораздо важнее вопрос синтаксиса. И тут к обоим языкам огромное количество притензий
Про вопросы бизнеса, для которого это все делается, тоже забывать не стоит: время=сложность=стоимость разработки, последующей доработки, сопровождения.

попробуйте Crystal, не факт, что вам подойдет, да и до первой версии еще несколько месяцев, но прямо глоток свежего воздуха.

Ждем от mail.ru замеры статьи про Go инструменты для того, чтобы подрубаться к приложению в продакшене под нагрузкой, всякие там интроспеции, профилирование, поглядеть куда утекает память. Вроде бы это pprof.
А то, что Go на hello world http показывает высокий RPS все уже и так знают.
Гораздо важнее вопрос синтаксиса. И тут к обоим языкам огромное количество притензий.

Претензий по части синтаксиса и к их конкурентам (системные яп) много

"Системные яп" — после всех срачей вокруг "системности" go, точно хорошая идея продолжать так говорить? Понятно же, что на практике все под системностью языка что-то свое понимают и это провоцирует споры на пустом месте.

Системный язык — это то, на чём пишется система (феноменально, да?), то есть то, что не привязано ни к какому конкретному языку. И, собственно, системный язый язык у нас тут один — это Rust. Ни на чём другом вы ни библиотечку для Fortran'а, ни модуль в ядро Linux'а не напишите.

Будут ли их реально на Rust писать? Хороший вопрос — поживём, увидим. Но ни на Scala, ни на Go их написать нельзя. Вернее можно «на спор» — но это уже будет в чистом виде «вырезание гланд автогеном через задний проход».

Go в этом смысле немного похож на пресловутые Лисп-машины: в отличие от многих других языков поддержка в виде C ему «для жизни» не нужна — но и поддержать какой-либо другой язык, кроме титулярного, они не могут.
Система — это не только ядро и библиотеки, это еще и системная оболочка, системные утилиты, а также средства разработки. Которые могут быть написаны практически на любом языке.
Система — это не только ядро и библиотеки, это еще и системная оболочка, системные утилиты, а также средства разработки.
Нет. Есть чёткое определение: В отличие от прикладного программного обеспечения, системное не решает конкретные практические задачи, а лишь обеспечивает работу других программ, предоставляя им сервисные функции, абстрагирующие детали аппаратной и микропрограммной реализации вычислительной системы, управляет аппаратными ресурсами вычислительной системы.

Так-то можно любыми словами называть всё, что угодно — только тогда общаться тяжело становится. Системная оболочка, возможно, её может быть отнесена к системе (хотя кто и где видел системную оболочку, написанную, скажем, на Prolog'е?), а вот уже системные утилиты и средства разработки — точно нет. Потому что ими человек пользуется, а не другие программы.
Администрирование компьютера — это не практическая задача, никто обычно не покупает компьютер чтобы его администрировать (разве что в учебных целях).
Тем не менее администрированием занимается человек, а не других программы, как правило.
Но он этим занимается для того, чтобы обеспечить работоспособность тех самых других программ.
Проблема в том, что для этого он может использовать вообще любое ПО, так что подобное расширение сделает само понятие бессмысленным.
А это откуда определение, вас не затруднит уточнить? А, википедия.
Всю жизнь трансляторы и компоновщики, интерпретаторы командной строки и т.п. входили в системное ПО, а тут на тебе — уволили.

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

А вы материал по своей же ссылке читали? Видимо, нет:
Отнесение того или иного программного обеспечения к системному условно, и зависит от соглашений, используемых в конкретном контексте. Как правило, к системному программному обеспечению относятся операционные системы[⇨], утилиты[⇨], системы программирования[⇨], системы управления базами данных[⇨], широкий класс связующего программного обеспечения.
Всю жизнь трансляторы и компоновщики, интерпретаторы командной строки и т.п. входили в системное ПО, а тут на тебе — уволили.
Именно так. Потому что если их включать, то становится непонятно что вообще туда не будет попадать. В английской версии написано более аккуратно: по историческим причинам в некоторых огранизациях термин «системный программист» используется по отношению к людям, которых более правильно было бы назвать «системными администраторами» — в этом случае программное обеспечение, которые эти люди используют, тоже могут быть названы «системным» [...] В некоторых публикациях в категорию системного программного обеспечения также относятся средства для разработка (такие как компилятор, компоновщик, или дебаггер).

«По историческим причинам» — это как раз и означает, что «да — было, помним, но… пора бы об этом и забыть».
Удивительного ничего нет: в нашей, русскоязычной школе программирования сложилась своя терминология.
«исторические причины» указаны по поводы термина системный программист/системный администратор; мы же с вами о том, что такое системное ПО. Здесь же указано: «В некоторых публикациях в категорию системного программного обеспечения также относятся средства для разработка (такие как компилятор, компоновщик, или дебаггер).»
Вы приводите цитату, указывающую вариативность и берете оттуда только ту часть, что вам в строку.

Потому что если их включать, то становится непонятно что вообще туда не будет попадать

Браузер, при помощи которого мы с вами общаемся. Текстовый редактор, музыкальный проигрыватель и так далее. Все прикладное ПО.
Браузер, при помощи которого мы с вами общаемся.
Да ладно. А с помощью чего я кодревью делаю, как вы думаете?

Текстовый редактор
Используется для правки всё того же кода.

музыкальный проигрыватель
Позволяет мне размечать файлы при создании образов операционки.

и так далее
Для того, чтобы говорить «и так далее» нужно привести хоть один пример хоть чего-нибудь, что не является «системным по» в вашем определении.

Все прикладное ПО.
Может быть использовано для разработки. Даже игры — мы с их помощую скорость JIT'а меряем.
Собственно ровно в той статье в википедии текстовые редакторы прямо так и недвусмысленно отнесены к системному ПО. Так что если уж берётесь отличать системные текстовые редакторы от несистемных — приводите критерии.
Именно так. Потому что если их включать, то становится непонятно что вообще туда не будет попадать.

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

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

Ну вот Goшники как-то не очень согласны с этим определением, дофига где Go называется системным языком — от того поначалу было много срачей про ЦА языка и его применимость вообще. А срачи на пустом месте — плохо, потому я и прицепился.


модуль в ядро Linux'а… Будут ли их реально на Rust писать? Хороший вопрос — поживём, увидим

Знаю по крайней мере про этот (обучающий?) пример: https://github.com/tsgates/rust.ko

Ну вот Goшники как-то не очень согласны с этим определением, дофига где Go называется системным языком — от того поначалу было много срачей про ЦА языка и его применимость вообще.
Это их проблемы. Для того, чтобы Go можно было назвать системным языком нужно дать такое определение, которое было бы
1. Включающим в себя язык Go (что очевидно).
2. Логичным (давайте не будем извращаться и придумывать определения типа «язык программирования системный, если в его названии не более трёх букв»).
3. Не включающим в себя какой-либо распространённый язык программирования (а иначе зачем вообще такое понятие, которое все языки скопом записывает в системные?).

Вот с пунктом номер 3 — обычно жёсткий напряг.

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

Rust ощущается как язык с порогом вхождения. Нужно повернуть мозг в правильную позицию, чтобы заработало. И этот порог — вовсе не в странных 'закорючках, :: которые &mut местами :(i32) смущают. Закорючки бытро учатся. А вот лежащая в основе модель памяти требует капитального перетряхивания сознания, причём как при переходе с Python/ruby/php/perl, так и при переходе с C.
Как мне показалось, модель памяти Rust сильно, сильно проще таковой в небезопасных языках. Не в последнюю очередь потому что правила обращения с памятью, которые в них «неплохо бы выполнять, иначе администрация не несет ответственности», в Rust зашиты в компилятор.
Точно не проще. Она может быть «проще в практическом применении» если учитывать время на отладку, но она точно не проще для понимания.

Попробуйте объяснить, что такое str (не &str!) с точки зрения программы.
lifetimes, ownership & borrow — всё это центральные абстракции памяти rust'а, поскольку они определяют момент выделения/очищения памяти.

Повторю, это порог вхождения. Когда осознаёшь и привыкаешь — становится легче, но сишная модель грубее и проще. Сайд-эффекты её при этом ужасающи, но для объяснения на пальцах — сильно проще.
Про сишную не скажу, но вот многострадальный JSR 133 я понимаю и помню ровно пять минут после прочтения — рассказываемые там вещи слишком эфемерны и теоретизированы. Практика же к ним находится в не то чтобы очень простой и понятной книге Гётца, но даже эта книга несоизмеримо проще написанного почти юридическим с точки зрения понимания простыми смертными языком JSR 133. И вот по сравнению с джавовским сотонизмом модель памяти Rust легка и понятна с полуслова. Всего три постулата:
1. Не твоё — не трогай.
2. Дал попользоваться — жди когда вернут.
3. Беспорядочный доступ запрещен. Нужно меняться между потоками — вот каналы. Нужно делиться между потоками — вот мьютексы. Хочешь настоящего беспорядка — unsafe и ССЗБ.
4. (опционально) Компилятор Rust — друг. Это понимание приходит со временем , особенно после общения с javascript.
С str вообще все просто — это обычный массив в хранении, но с определенными правилами в обработке. В случае литерала данные массива зашиты в постоянную память, наружу торчит толстая ссылка (указатель + размер массива), которая жива от скобки до скобки, или пока перемещающие функции ее не съедят.
То что вы показываете это не модель памяти, а принципы использования.

Сишная модель памяти базируется на арифметрике указателей и стеке. У раста к этому добавляется понятие владения.
Вот это понятие владения очень сильно упрощает практические аспекты использования. Во всяком случае, для людей вроде меня, которые из «как» выводят «что». Возможно, наоборот будет не настолько просто — все же объяснить вышеупомянутое «не трогай — занято» куда проще практическими примерами, чем абстрактными проекциями «времен жизни» на статической модели памяти. Даже при том, что вж в рантайме нет.
но сишная модель грубее и проще
Вы это серьёзно? Чем memory_order_acq_rel от memory_order_seq_cst отличатеся — не напомните?

Сайд-эффекты её при этом ужасающи, но для объяснения на пальцах — сильно проще.
Нифига не проще. Проще она была в далёком прошлом — когда процессор в системе был один и кеширования в нём не было предусмотрено вообще. В современном же мире сишная модель (почти ничем не отличающаяся от того, что железо предоставляет, на самом деле) — ужасающе сложна и 99.99% программистов на C её не понимают от слова «вообще». Отсюда и «сайд-эффекты».
str — это безразмерный (unsized) тип, представляющий строковое значение, а &str — ссылка на него. Примерно как void и void* в Си, только без костылей.

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

Вероятно потому, что с void сишник знакомится в младенчестве и просто принимает на веру, что «надо писать void, когда функция не возвращает значения, а void* это… ну просто указатель, когда не знаем, на что указываем». В итоге человек к этому привыкает и перестает об этом думать.
Аналогия с void мне нравится, однако: void не подразумевает чего-то. То есть я могу сказать, что функция возвращает void (т.е. ничего), я могу делать операции с указателями на void, но чего-либо с типом void в C нет. А в rust'е каждая константная строка — это str. Я с трудом себе могу представить декларацию void-с-содержимым в Си. В расте такое есть (let a = «hello»).
Правильно. А справа, после знака равно — str. И когда я делаю slice от String, у меня тоже str. Так что же такое str?

Пока что лучшая аналогия, которую я придумал, звучит так: str — это как земля (в контексте недвижимости). Можно передать права на неё, можно выкопать из неё всю почву, построить и снести дом, но нельзя переместить землю, нельзя увезти с собой. Она просто есть. На неё можно показывать, в ней можно хранить, можно запрещать или разрешать ходить по ней, но это и всё.

Прошу прощения, изначально я "промахнулся" с контекстом и поспешил ответить ))
Как выше написали, буквально str — это тип. Тип, который невозможно (без unsafe) получить с "неправильными" данными. Но сами данные — в этом случае придётся держать за указателем — либо с контролируемым временем жизни за ссылкой &str, либо за владеющей ссылкой Box<str>, Rc<str> etc, либо за сырым указателем *const/mut str.
str — тип неизвестного на этапе компиляции размера, поэтому простого способа использовать его как обычную переменную, лежащую на стеке, нет (да и нужно ли?).
Если у нас есть нечто типа str, значит под капотом — набор байт, являющийся корректной utf-8 строкой. И с этим типом ассоциирован ряд функций, которые умеют с этим utf-8 корректно работать.
Rust позволяет программисту с помощью типов выразить некоторую логику состояния, описать логику переходов между состояниями и дать определённые гарантии в связи со всем этим. Тут была неплохая статья на тему конечных автоматов, которая раскрывает идеи такого подхода.
Если типы не прошли проверку — это не скомпилируется, а функции, принимающие на вход другой тип — не будут работать не со своими данными, что снимает ряд недоразумений и позволяет большой ряд ошибок ловить на этапе компиляции и не путать тёплое с быстрым.
Если же мы займёмся произвольным приведением указателей — плакали наши гарантии, пользы от Rust будет сильно меньше, чем от C (учитывая его полувековую историю). Но Rust как и C не запрещает работать с указателями, в некоторых ситуациях это неизбежно (это и фундамент низкоуровневых библиотек, и всевозможный FFI). Всё, что для этого требуется — чётко обозначенное условие потери гарантий (или точнее — взятие на себя ответственности) — unsafe.

То есть я могу сказать, что функция возвращает void (т.е. ничего)

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


но чего-либо с типом void в C нет

И это иногда немножко бьёт в темплейтах :(

Ох, тут начинается что-то крайне любопытное. Вот у нас есть функция sin(1/x). У нас есть функция lim x->0 f, которая принимает f и возвращает её предел к 0.

Что возвращает lim x->0 sin(1/x)?

lim x->0 sin(1/x) возвращает особое значение "результат операции не определен" или "операция недопустима". В программировании его можно представить как NaN, как исключение или как None в монаде Optional.

Вы рассказываете, что оно в программировании возвращает. Можно ещё exception сделать.

А что оно в математике «возвращает»? Ничего. Тот самый void.

Другой пример. Выражение «пусть а равно нулю» какое значение возвращает? Тоже никакого.
А что оно в математике «возвращает»? Ничего. Тот самый void.

Вы путаете тип и значение. Тип данных — это множество всевозможных значений. Выражение lim x->0 f как функция от f может вернуть любое вещественное число или "ничего" (на самом деле там еще три вида бесконечности может получиться, но для простоты их пропустим). При подстановке конкретного параметра sin (1/x) получается второй вариант, но это не означает что тип возвращаемого значения — "ничего". Тип возвращаемого значения — R | { NaN }, множество действительных чисел расширенное на 1 элемент.


Выражение «пусть а равно нулю» какое значение возвращает?

Вообще говоря, это не выражение, это определение. Но если рассматривать его как операцию присваивания и забыть про монаду IO, то такое выражение может вернуть только 1 значение — "успех операции". Поэтому тип такого выражения — Unit, в памяти компьютера он занимает 0 байт.

Вы путаете void в понимании Си и в понимании ФП. Да, в ФП под void обычно подразумевается ненаселенный тип, что-то чего в принципе не существует.


В Си, C++ и C# void ведет себя скорее как нормальный тип размера 0 байт — у него есть ровно 20=1 возможное значение, и именно этого значение называют "ничего".


В Java void ведет себя странно: будучи примитивным типом, он ведет себя так же как и в Си, но при "упаковке" до Void его единственное значение пропадает (остается только общее для всех ссылочных типов значение null). К счастью, синтаксис языка не позволяет делать такую упаковку, а потому это несоответствие остается лишь теоретической странностью.


И да, соглашусь что было бы неплохо разрешить использование void в темплейтах или дженериках

В Си, C++ и C# void ведет себя скорее как нормальный тип размера 0 байт — у него есть ровно 20=1 возможное значение, и именно этого значение называют «ничего».

Из чего именно следует то, что он ведёт себя именно так? Это какие-то другие Си, а в моих void — это не значение.

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


Поскольку в Си функции могут как возвращать void, так и принимает его в параметрах — это определенно не ненаселенный тип, а что-то другое.

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

Я не про это спрашивал.

Поскольку в Си функции могут как возвращать void, так и принимает его в параметрах — это определенно не ненаселенный тип, а что-то другое.

Это так же не ответ. Я спрашивал не то — как не ведёт себя void, а то — почему вы решили, что он ведёт себя именно так, как вы описали.

Давайте попроще. Вы написали «void ведет себя скорее как красный». Вас спросили «почему вы решили, что воид — это красный. В моих си он не красный, а чёрный».

И вы ответили что? Оно не может быть зелёным потому что это не как «бла-бла». Поскольку в Си функция не как «бла-бла» — это не зелёный, а что-то другое.

Но вам не говорили о том, что оно зелёное. Вас спросили — почему вы говорите о том, что оно скорее красное. Всё просто.

А по поводу

Поскольку в Си функции могут как возвращать void, так и принимает его в параметрах

Нет. Никто и никак принимать void не может. void в аргументах не означает, что функция принимает какой-то воид.

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

Существо понятие «ничего» и существует тип. Ничего. У ничего нет значений. Ничего нельзя никак и нигде использовать. Это поведение, которое не соответствует другим типам.

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

А теперь включаем логику. Когда значение в функции, либо где-либо ещё появляется? Правильно — в момент его использования. До того момента — значение ничего не значит и ни на что не влияет.

Тем самым вызов функции void f(); В результате имеет тип воид, а значит не имеет значения. Ничего не мешает чему-то не иметь значения и мы можем это использовать, но мы не может использовать значение.

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

Если ещё проще. Типы описывают наборы принимаемых значений. И этот набор может быть пуст. И это воид. Это не значит, что такого типа не существует, либо что у него появляется какое-то значение.
В теории типов существует лишь два объекта, про которые можно сказать что они означают «ничего». Это ненаселенный тип, у которого нет значений — и тип нулевого размера, у которого 1 значение.

Я не говорил что void в Си — это в точности один из них, разумеется это не так. Но void гораздо больше похож на второй вариант чем на первый.
В теории типов существует лишь два объекта, про которые можно сказать что они означают «ничего». Это ненаселенный тип, у которого нет значений — и тип нулевого размера, у которого 1 значение.

И воид — это именно тот тип, у которого нет значений.

Но void гораздо больше похож на второй вариант чем на первый.

Вот я и спрашиваю — чем же он похож. Я объяснил чем он похож на первый. Теперь ваша очередь.
Это не ответ и я уже объяснял почему. Подобное меня удивляет.

А по поводу темы — ниже я пруфцанул из стандарта. Т.к. апеллировать к логике и пониманию перестало иметь смысл. Хотя сомневаюсь, что и до этого оно имело смысл.
Вот вам еще аргументы.

1. Размер ненаселенного типа — минус бесконечность. Это очень странный тип данных.

2. Когда вызывается функция которая принимает void — на стеке выделяется 0 байт для ее аргументов.

3. Когда вызывается функция которая возвращает void — на стеке резервируется 0 байт для возвращаемого значения.

4. В языке существует тип «указатель на void». Для ненаселенного типа такая конструкция абсурдна.
Я уже сказал, что никаких аргументов у вас нет.

1. Размер ненаселенного типа — минус бесконечность. Это очень странный тип данных.

Никакого размера у void нету. Мимо.

2. Когда вызывается функция которая принимает void — на стеке выделяется 0 байт для ее аргументов.

Опять же мимо. Никакие стэки к си отношения не имеют и ничего не выделяется. Срочно стоит почитать букварь.

3. Когда вызывается функция которая возвращает void — на стеке резервируется 0 байт для возвращаемого значения.

Аналогично.

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

callabi находится за пределами языка и вообще это деталь реализации. С таким же успехом я могу сказать, что в вашем ФП у void не бесконечный размер и точно так же выделяется ноль.

Что из это следует? Да ничего. Отсутствие аргументов.

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

Ничем не отличается.


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

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

Ничем не отличается.

Хорошо. Сколько должно выделяться под никакое значение? Чёткий ответ.
Не можете. Потому что, как я уже говорил, в ФП функцию принимающую Void нельзя вызвать

А что с ней делать? Смотреть на неё?

Для чего вообще нужен void?

Кстати — как там дела с NaN? Интегрируется ли он в логику ФП? И каким образом.
Вы не найдете областей памяти в работающей программе, которые можно было бы классифицировать как значение ненаселенного типа.

Прям как в Си.

Как удобно врать и манипулировать. Говорим о типе а потом бам — о значении.

Как удобно врать и манипулировать. Говорим о типе а потом бам — о значении.

Это вы так делаете, не я.


Раз пошли переходы на личность — дискуссию заканчиваю.

Это вы так делаете, не я.

Где же?
Раз пошли переходы на личность — дискуссию заканчиваю.

Где же?

А что с ней делать? Смотреть на неё?

Смотреть. Использовать как элемент построения доказательства (или контрпримера) в пруверах. Всякое такое. В реальном продакшен-коде таких фукнций нет.


Для чего вообще нужен void?

В С или в языках с более целостной системой типов?


Кстати — как там дела с NaN? Интегрируется ли он в логику ФП? И каким образом.

Как одно из значений типа Double, например.

Смотреть. Использовать как элемент построения доказательства (или контрпримера) в пруверах. Всякое такое. В реальном продакшен-коде таких фукнций нет.

Каким образом её можно использовать в пруферах, если позвать нельзя?

В С или в языках с более целостной системой типов?

В ФП.

Как одно из значений типа Double, например.

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

Как именно нан интегрируется в модель ФП, в функции и значения с т.з. математики?

Я, всё же, до сир пор не понимаю чем не устраивает void в Си. Наверное только тем, что он не такой как в хаскеле. Все свойство, которые определил человек выше у него есть.

По мне так логика функций в Си намного более полная, нежели в ФП.

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

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

Я может какой-то альтернативно-одарённый и не способен понять всей мощи ФП, но смотря на всё это — когда мы можем нарушать свои правила во имя поддержания языка. Ведь в рамках этих правил он попросту бесполезен. А после выдавать эти правила за какую-то истину. По мне — это глупо. Как вам — не знаю.

Т.е. я вижу причины по которым воид сделан иначе, но я не вижу причин того — что воид с Си какой-то не такой. Он такой же.

Либо человек как-то неправильно это объяснял, либо я как-то не понял. Но вроде понял. У воида нет значений. Воид использоваться не может. Отсутствие значения не наделяет воид каким-то значением, либо не делает его значением.

И неважно какое там значение — тип это не значение. Зачем подменять понятия и спрашивать с типа свойств значения? Ничего непонятно.
Каким образом её можно использовать в пруферах, если позвать нельзя?

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


В ФП.

В ФП Void почти не используется. Даже отдельной библиотекой в том же хаскеле сделан, например: void.


Как именно нан интегрируется в модель ФП, в функции и значения с т.з. математики?

Как и любой другой элемент типа Double. У NaN есть представление, в конце концов, и в алгебру даблов он вполне себе вписывается.


Я, всё же, до сир пор не понимаю чем не устраивает void в Си.

Если вкратце, тем, что вы не можете написать void a = void {};.


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

Про это я уже писал рядом про сайд-эффекты.


Я может какой-то альтернативно-одарённый и не способен понять всей мощи ФП, но смотря на всё это — когда мы можем нарушать свои правила во имя поддержания языка. Ведь в рамках этих правил он попросту бесполезен. А после выдавать эти правила за какую-то истину. По мне — это глупо. Как вам — не знаю.

Я это не распарсил.

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

Её же нельзя вызвать — как я её могу вызвать?

Как и любой другой элемент типа Double. У NaN есть представление, в конце концов, и в алгебру даблов он вполне себе вписывается.

Оно не вписывает никуда. Вы просто экспортировали логику из ieee754 в даблы. И я спрашиваю не про логику даблов, а про математику и ФП.

Хотя я заранее знаю ответ — никак, но всё же. Вдруг он есть, а я просто не знаю.

Если вкратце, тем, что вы не можете написать void a = void {};.

Естественно — это не имеет смысла. Логика операции = — присваивание значения, которого у воида нету.

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

Про это я уже писал рядом про сайд-эффекты.

Вы писали обратное тому, что пишу тут и там я.

Я это не распарсил.

Если проще. Реальный мир состоит из NaN, сайд-эффектов и прочего, что в логику чисто функциональную не влезает. Выше я писал о конкретных противоречиях в ваших выкладках.

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

Но при этом мы декларируем что? Что логика ФП правильная, а какая-то иная не правильна. При этом логика ФП за рамками самого ФП( в рамках реального мира) несостоятельна.

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

Т.е. как происходит. У нас там снизу не чистота, но мы верим в то, что у нас чистота. Как?

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

Хороший вопрос. Функцию Void -> Void — никак. Функцию a -> Void — можете. Просто вы её написать не можете. Язык с totality checker'ом это вообще не проглотит, а в хаскеле любой тип также населён значением undefined, либо, пардон за сленг, бесконечной рекурсией (которая на самом деле одна из реализаций undefined), либо чем-нибудь таким. В любом случае, конструктивно вызвать вы и такую функцию не сможете.


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

В реальном мире NaNов нет, кстати. Но это так.


Выше я писал о конкретных противоречиях в ваших выкладках.

Я их как-то не увидел, и до сих пор не вижу.


Что логика ФП правильная, а какая-то иная не правильна.

Первична не логика ФП, а логика, ну, некоторой математической теории. ФП тут может возникать лишь как модель этой логики, не более.


У нас там снизу не чистота, но мы верим в то, что у нас чистота. Как?

Hask is not a category, увы.

Пошел за пруфами в стандарт:

The void type comprises an empty set of values; it is an incomplete object type that
cannot be completed.


Стандарт считает так же. Вариант номер раз.
Это показывает лишь намерения создателей стандарта но не то что у них фактически получилось.

Объясню доходчивей.


В C объявление void f(void) — это не объявление функции, которая принимает void и возвращает его, это объявление набора инструкций, у которого есть адрес с названием f. И всё.


Допустим, мы пишем такой С код:


//clang 3.8.0

#include  <stdio.h>

f(x) {
   printf("%d\n", x);
}

int main(void)
{
    f(42);
    return 0;
}

И да, это валидный C код. Оно компилируется и работает.


Что за херня? Как это возможно?

А вот так. По-дефолту, если не указывать тип, он равен int.
И вот мы пришли к тому, о чем говорил наш функциональщик: к стеку. Если мы хотим "функцию", которая ничего не возвращает, опустить тип нельзя. На стеке будет выделяться sizeof(int) байт(а т.к. в моем примере в конце функции я не сделал return VALUE_OF_ANY_INT, то функция вернёт мусор, что делает использование ее возвращаемого значения неопределенным поведением). А сишники очень любят экономить и оптимизировать. Поэтому в C ввели синтаксический мусор в виде void. При этом void* — это не указатель на void, это свой особый тип, при попытке разадресации которого вы получите ошибку:


Compiler Error: 'void*' is not a pointer-to-object type 

Поэтому halt выше сказал:


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

И перестаньте думать о сишных функциях как о функциях вообще. C — это кроссплатформенный ассемблер, функции — это именованый адрес инструкции.

Почему я должен думать о void как о «синтаксическом мусоре» если все его свойства кроме одного прекрасно объясняются тем что это тип нулевого размера?

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


Исключение — невозможность создания переменной типа void.

Ну а мое высказывание "синтаксический мусор" объясняет все свойства и исключение.

Нет, с таким подходом каждое свойство должно объясняться отдельно.
Это не мешает писать на Си всё. От ядер операционных систем до «кошечек». Операционка на Java?, на Rust?, на Go? Три раза хе хе хе!!!
Это вы к чему сказали? Я что, где-то утверждал обратное? Какое отношение загадки типизации имеют к распространенности языка?
Сорри, хабра искажает картину комментирования. Это был ответ на другой пост.

Давайте с другой стороны. Пусть мощность множеств A и B — 0. Какова мощность множества функций из A в B? В силу некоторых соображений (матлогических и категориальных, в частности) эта мощность равна единице. То есть, существует единственная такая функция.


Пусть теперь мы вместо B (явно объявленное возвращаемое значение функции) рассмотрим возвращаемое значение вместе со всеми сайд-эффектами (у нас же не чистый язык, в конце концов), которое есть декартово произведение B на некоторое другое множество. Декартово произведение пустого множества с любым множеством пусто, поэтому мощность множества void(void) функций даже с учётом сайд-эффектов должна быть равна единице, что, как мы знаем, не так, их поболе будет.

Декартово произведение пустого множества с любым множеством пусто, поэтому мощность множества void(void)

Какие-то манипуляции. Мы взяли аргумент и результат. Потом каиким-то образом к ним добавились сайд-эфекты. С т.з. вашей логики сайд-эфекты находятся за рамками вашей логики и ваша логика для них не работает.

Точно так же их будет поболее при любых других аргументах — из этого ровным счётом ничего не следует. Вы получите те же результаты и без воид.

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

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

Сайд-эффекты это не что-то левой — это всё входит в множество аргументов/возвратов функции.

Точно так же всё это зафейлится и на void(1bit, 64bit) — либо мы за множество возьмём оба аргумента? Тогда то же будет и с сайд эффектами.

В любом случае — я не понимаю что из этого должно следовать?

Формальное теоретико-множественное рассуждение.

Какие-то манипуляции.

Ясненько. Понятненько.

Ясненько. Понятненько.


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

Вы попытались играть со мною в логику — выдавая свои определения за какие-то формальные рассуждения. Вас спросили — каким образом вы сайд эффекты записали в аргументы/возврат функции. Ведь в рамках вашей логики функция имеет только один выход — возврат. Иного не дано.

А то, что с сайд-эффектами — это совершенное иное определение, нежели то, что вы использовали вначале.

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

Всё это маскируется под «формальными рассуждениями», которые состоят на самом деле из банального жонглирования понятиями.

В любом случае — ладно. Я в очередной раз для себя определил потолок способностей евангелистов.
Ваше определение чего-то чем-то не стоит ничего.

Моё определение чего? Классической наивной теории множеств или теории множеств Цермело-Френкеля? Широко известной теории типов? Ну там, я не знаю, Бенджамина Пирса почитайте, что ли, я, к сожалению, не такой умный, чтобы самостоятельно все эти глупости выдумывать.


Вы попытались играть со мною в логику — выдавая свои определения за какие-то формальные рассуждения.

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


Вас спросили — каким образом вы сайд эффекты записали в аргументы/возврат функции. Ведь в рамках вашей логики функция имеет только один выход — возврат.

Нет. Написал специально для этого случая: «Пусть теперь мы вместо B (явно объявленное возвращаемое значение функции) рассмотрим возвращаемое значение вместе со всеми сайд-эффектами (у нас же не чистый язык, в конце концов), которое есть декартово произведение B на некоторое другое множество.». Вот то самое другое множество и есть множество всех сайд-эффектов.


А то, что с сайд-эффектами — это совершенное иное определение, нежели то, что вы использовали вначале.

Нет. Начиная от монадического формализма заканчивая тупейшим декартовым произведением.


Что до последних ваших трёх абзацев, коли хотите пассивную агрессию — получите пассивную агрессию: во-первых, так вот и перестаньте подбивать свои дыры в понимание, а вместо этого прочтите какую-нибудь базовую книгу по матлогике (вроде лекций Верещагина и Шеня), а затем догонитесь упомянутым ранее Пирсом, коли такие дискуссии вам интересны. Во-вторых, специалисты вроде вас с евангелистами вроде меня едва ли пересекутся где-то в реальной жизни, так что не думаю, что ваше определение потолка будет иметь какие-то сайд-эффекты.


Да и вообще, моя любимая картинка в тему:


Тыц
Вы путаете void в понимании Си и в понимании ФП. Да, в ФП под void обычно подразумевается ненаселенный тип, что-то чего в принципе не существует.

Так я вроде сразу написал: «В С можете, в математике нет.»


В Си, C++ и C# void ведет себя скорее как нормальный тип размера 0 байт — у него есть ровно 20=1 возможное значение, и именно этого значение называют «ничего».

Ну да, сиподобный void — это как () в каком-нибудь хаскеле.

операции с указателями на void

Это тавтология. Попробуйте разадресовать void* и получите ошибку компиляции:


Compiler Error: 'void*' is not a pointer-to-object type 
Сишники вполне себе обращаются с void, но почему-то все становится плохо, когда эту идею пытаются развить и строго объяснить с позиции системы типов и идеологии языка.

Поподробнее об этом, пожалуйста. Что именно становится плохо?

Вероятно потому, что с void сишник знакомится в младенчестве и просто принимает на веру

Некой мистической «строгостью» понимание не ограничено, логика не ограничена.

«надо писать void, когда функция не возвращает значения, а void* это… ну просто указатель, когда не знаем, на что указываем»

А ещё надо писать void, когда функция не принимает аргументов. Давайте поиграем в игру.

void определяет отсутствие результата, либо возврата. Как хотите это называйте. Что такое тип у указателя? Это тип возвращаемого операцией разыменования значения.

Что такое void * — это указатель для которого нет операции разыменования. *(void *) == void, то же самое что void f() {}; f(). Результата нет.

Мы не не знаем на что указываем — мы ни на что не указываем. «указатель указывает» — это детский садик. Это как функция у которой нельзя взять результат, так это — указатель, у которого так же нельзя взять результат.

int f(); и int * p; — они идентичны. Только у одной операция скобочки, а у второй звёздочка.

Тут скорее всего проблема в том, что как и сишник принимает на веру — так и представления о си — это вера. И то, что у кого-то есть вера — из этого не следует то, что без веры в си нет логики.
Ах да, забыл про арифметику. Опять же это сайзоф поверх типа «до» звёздочки. И sizeof(void) в итоге.

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

Я скептически отношусь к подобным тестам, когда берут любые две реализации сервера, без изучения их работы. Т.к. есть такие штуки как zero-copy, пулы памяти, пулы потоков, ленивые вычисления и т.д.

Выше уже писали, что изначальная задумка была сравнить дефолтные инструменты в той и другой экосистеме, а не one shot велосипеды. В такой постановке вопроса это вполне имеет смысл (при корректном проведении и правильных выводах).
Я так понимаю, что речь о сравнении стандартного инструментария наиболее подходящего для решения поставленной задачи. Иначе можно докатиться до холиваров и сравнения того, что входит, а что не входит в стандартную библиотеку/поставку языка.

Как бы это ни было смешно, но буквально "из коробки", как и всё, что на crates.io :-)

Внесу свои пять копеек. Я портировал все оригинальные тесты на современные библиотеки, и замерил производительность на своем макбуке. Скажу сразу, что я еще не закончил, плюс макбук — далеко не серверная платформа. Тем не менее, предварительные результаты уже готовы: github.com/nuald/simple-web-benchmark#preliminary-results

Вообще, средние результаты в текущем виде меня не особо волнуют, но нельзя не заметить, что все-таки Rust может проигрывать Go в каких-то отдельных случаях (например, в зависимости от операционной системы или конфигурации железа). Но возможно, что в мой портированный код закралась ошибка или я что-то упустил, так что буду рад, если найдете и укажите на это.
Прислал pull request — код на языке D (vibe-d).

app.d
import vibe.d;
import std.regex;

auto reg = ctRegex!"^/greeting/([a-z]+)$";

shared static this()
{
    auto settings = new HTTPServerSettings;
    settings.port = 3000;

    listenHTTP(settings, &handleRequest);
}

void handleRequest(HTTPServerRequest req,
                    HTTPServerResponse res)
{
    if (req.path == "/")
        res.writeBody("Hello, World!", "text/plain");
    else if (auto m = matchFirst(req.path, reg))
        res.writeBody("Hello, " ~ m[1], "text/plain");
}


dub.json
{
    "name": "vibedtest",
    "targetName": "vibedtest",
    "targetType": "executable",
    "targetPath": "bin",
    "dependencies": {
        "vibe-d": "~>0.8.1"
    },
    "versions": [
        "VibeDefaultMain"
    ]
}
На github.com/nuald/simple-web-benchmark появились результаты тестирования D на macbook.
D получился даже медленней node-js.

Потестировал D (vibe.d) по сравнению с Go.
На Windows 7 x64 — vibe.d собранный DMD под x86 чуть быстрее Go только если выбрана конфигурация vibe.d libevent. Иначе в два раза медленней.

На Linux x86_64 — vibe.d собранный DMD и в default конфигурации и в libevent показывает 45K запросов в секунду, a Go — 50K запросов. Но зато D использует 90% CPU, a Go — 120%.

Теоретически сборка Vibe.d с помощью компилятора LDC вместо DMD должна показать лучшие результаты. Проверить пока не получилось.
go version go1.9
rustc 1.22.0-nightly
hey -n 200000 -c 256 -t 10 http://127.0.0.1:3000
Go
Average: 0.0040 secs
Requests/sec: 63371.1408

Rust
Average: 0.0036 secs
Requests/sec: 68601.3122
hey -n 200000 -c 256 -t 10 http://127.0.0.1:3000/greeting/hello
Go
Average: 0.0040 secs
Requests/sec: 63361.0934

Rust
Average: 0.0044 secs
Requests/sec: 55537.4970
Прислал pull request — код на языке Crystal.
Server.cr
require "http/server"

reg = %r(^/greeting/([a-z]+)$)

server = HTTP::Server.new(3000) do |context|  
  context.response.headers["Content-Type"] = "text/plain"  
  context.response.status_code = 200    
    
  if context.request.path == "/" 
    context.response.print "Hello world!"
  else 
    context.response.print "Hello, #{context.request.path.match(reg).not_nil![1]}"
  end
end

server.listen(reuse_port: true)

Включил результаты в графики (пока все для тех же платформ: Мак и Вин, Лин на подходе). Сразу замечу, что я изменил код, чтобы форкаться в самом процессе (WSL не хотела исполнять баш-скрипт), и отключил флаг reuseport — процессы работают и без этого флага (как в маке, так и винде). Если же флаг использовать, то программа вылетает в виндовсе. Возможно, что в линуксе с этим будут проблемы, но тогда и буду смотреть (либо вы можете добавить код, который проверяет ось и использует флаг, если надо). Вообще форки и использование одних и тех же портов — не самая лучшая идея, но этим аспектом лучше заняться после включения линукса в результаты, т.к. это базовая серверная система.

Ну вообще-то компиляция Crystal под Windows все еще в процессе разработки.


Так что проще пока удалить результаты Crystal под Windows, ибо они совершенно не отражают положения дел (все-таки другие ЯП уже имеют готовый Windows target).

Да, думаю, надо вынести WSL в отдельный пункт. Для серверной разработки это не важно, но интересно в целом, насколько MS оптимизировала эту подсистему. Правда прошу заметить, что готовый Windows target не всегда означает, что была выполнена оптимизация (просто возможно скомпилировали с помощью mingw или cygwin, я сам так много раз делал когда портировал Linux библиотеки на Windows).

Ну это ж был скорее тест реализации библиотеки регулярок, нежели http сервера. А в go оно очень так себе.

UFO landed and left these words here
Да, я в курсе, конечно, про линейное время.
Но в тесте сравнивается просто производительность типичной регулярки (не хитро продуманной).
Расслабьтесь.

У Go есть преимущества которые пока мало кто может оспорить:
1. Простота языка. Легко понять как свой код написанный год назад, так и код другого программиста.Т.е. низкий порог входа.Соответственно легче поддерживать проекты. Соответственно низкая стоимость поддержки. (Значит бизнес будет его использовать и далее)
2. Синтетические тесты это лишь меряние «письками». даже если Go уступает по этим тестам другим языкам, это не значит что он хуже или наоборот лучше. Это лишь означает что где то в каких то задачах он уступает. Никто не может сказать куда придет 2 версия. Все может кардинально поменяться в сторону производительности в нужном синтетическом тесте ))).

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

Попробуй D.
Киллер фича go — goroutines есть и в D — fibers.
Плюс многое другое, чего в Go нет.
Когда используешь язык на проектах о производительности думаешь гораздо реже, чем об удовстве. Причем здесь речь идет про полрата процента разницы. Это не серьезно. Говорю как человек долекий от обоих языков
Тем, кто сидит на Java или C# смотрят на Go. Ну простите, все равно приложение на Go будет жрать больше памяти и CPU по сравнению с приложением на C++ или Rust. GC — главный тормоз этих языков!
А плюсовики ждут релизов 1.0 всех фреймворков и IDE (и плагинов) для Rust! И безболезненно перейдут на Rust и будут смеяться над мусоросборными языками.
Ну простите, все равно приложение на Go будет жрать больше памяти и CPU по сравнению с приложением на C++ или Rust.
Памяти — да, CPU — нет. Главная проблема GC не в том, что оно вносит задержки, а в том, что эти задержки плохо предсказуемы. Go в этом смысле весьма неплох.
Памяти — да, CPU — нет.

Память в современном, да и в любом другом мире, не отделима от CPU. ЦПУ читает память и время затраченное на её чтение/запись/ожидание прямо пропорционально кол-ву этой самой памяти, при прочих равных. А они равные.

Главная проблема GC не в том, что оно вносит задержки

Задержки стоят ЦПУ, внезапно. Сколько их — одна большая, либо много маленьких — неважно. Хотя много — естественно дороже.
Память в современном, да и в любом другом мире, не отделима от CPU.
Отделима, отделима, ещё как отделима. 10 лет назад самый быстрый CPU — это примерно 5GHz, сегодня — плюс-минус столько же. Правда их побольше стало, но не в 100 раз. А память в «тяжёлых» системах выросла со 128GiB до 128TiB (не так давно этот рубеж штурмовали).

Так что транжирить память — плохо, но транжирить CPU — ещё хуже.

Задержки стоят ЦПУ, внезапно. Сколько их — одна большая, либо много маленьких — неважно.
Для компилятора, рендерера, любой «пакетной» задаче — да, важно когда вся задача будет решена. Для сервера или интерактивной программы — ой как важно. Скорость CPU-то расти перестала! Ожидать что завтра задержки из миллисекундных станут микросекундными больше нельзя…
Отделима, отделима, ещё как отделима.

Нет.

10 лет назад самый быстрый CPU — это примерно 5GHz, сегодня — плюс-минус столько же. Правда их побольше стало, но не в 100 раз. А память в «тяжёлых» системах выросла со 128GiB до 128TiB (не так давно этот рубеж штурмовали).

Вы либо не понимаете того, о чём вам пишут, либо делаете это специально. Вам говорили не об объёмах памяти, а необходимости траты ЦПУ на на работу с ней(памятью).

Так же — вы имели ввиду под ЦПУ — не железяку, а процессорное время. Теперь вы пытаетесь подменять понятия. Зачем?

ЦПУ читает память и время затраченное на её чтение/запись/ожидание прямо пропорционально кол-ву этой самой памяти, при прочих равных. А они равные.

Вам тут чётко и ясно определили связь между ЦПУ и памятью. И из этого выводится то, что имеет ввиду под ЦПУ.

Так что транжирить память — плохо, но транжирить CPU — ещё хуже.

Не так что.

Для компилятора, рендерера, любой «пакетной» задаче — да, важно когда вся задача будет решена. Для сервера или интерактивной программы — ой как важно. Скорость CPU-то расти перестала! Ожидать что завтра задержки из миллисекундных станут микросекундными больше нельзя…


Вы что сказали?

Памяти — да, CPU — нет.

Всё это в контексте оверхада ГЦ по сравнению с ручным управлением памятью.

Вам объяснили, что это неверно. И ответ существует именно в контексте потребления ЦПУ ГЦ. И вам так же сказали, что задержки — это ЦПУ-время. И неважно какие это задержки по продолжительности — в сумме их столько же. А на самом не столько же, а больше(го-вариант). Ведь вызов ГЦ это не только полезная работа, но оверхеды на сам вызов и уже с ним + пакетная обработка быстрее + всё это загаживает кешей больше и прочее и прочее).

Ваши попытки рассуждать о каком-то там влиянии этой задержки на разные кейсы — к теме не относятся. Об этом разговора не было.
ЦПУ читает память и время затраченное на её чтение/запись/ожидание прямо пропорционально кол-ву этой самой памяти, при прочих равных. А они равные.
Вам тут чётко и ясно определили связь между ЦПУ и памятью.
Вот тут прямо и чётко написали чушь. Главный и притом неверный тезис «а они равные» упомянув невнятной скороговоркой в конце.

Нет. Они неравные. Это уже давно всё исследовано: чем больше вы даёте GC памяти, тем меньше ему нужно кучу сканировать и тем быстрее он работает. Примерно на 500% скорость GC сравнивается с аккуратным «ручным» распределением памяти, а если дать памяти ещё больше — то может быть и быстрее.

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

И неважно какие это задержки по продолжительности — в сумме их столько же.
Вы действительно не понимаете что чушь здесь написали? Это же азы всей теории! В случае с GC затраты времени не просто зависят от того, сколько у вас «лишней» памяти, а очень сильно от этого зависят. Как и в случае с обычным распределением памяти, впрочем (локальная память для потока в каком-нибудь tcmalloc'е тоже ведь требует некоторого «запаса»).
Вот тут прямо и чётко написали чушь. Главный и притом неверный тезис «а они равные» упомянув невнятной скороговоркой в конце.

Где? Вы берёте мою цитату и разбираете, с явным указанием что там чушь.

Начнём, пожалуй. Я отвечал на:

Ну простите, все равно приложение на Go будет жрать больше памяти и CPU по сравнению с приложением на C++ или Rust.


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

Т.е. никаким ГЦ контекст не ограничивался и это всё не более, чем попытка врать.

Это уже давно всё исследовано: чем больше вы даёте GC памяти, тем меньше ему нужно кучу сканировать и тем быстрее он работает.

К ГЦ это не имеет никакого отношения. То же самое работает и для обычных аллокаторов.

Нет. Они неравные.

Опять же.
И неважно какие это задержки по продолжительности — в сумме их столько же.

Ни о каких объёмах памяти для ГЦ я не говорил. Я говорил о паузах. Очередная попытка врать.

Это уже давно всё исследовано: чем больше вы даёте GC памяти, тем меньше ему нужно кучу сканировать и тем быстрее он работает.

Ничего не исследовано. Го/жаву вам в руки и вперёд срывать покровы. Можете начать с хайлоадкаппа, а закончить какой-нибудь in-memory database.

Примерно на 500% скорость GC сравнивается с аккуратным «ручным» распределением памяти, а если дать памяти ещё больше —

Не верно. Это результаты протухшие — раз. Для 3-4бенчмарков из spec, которым а) никто не видел, б) вы не воспроизведёте их результаты.

Ни о каком аккуратном режиме речи не шло — никаких свидетельств о том какое он — в статье нету. Это всё не более, чем бла-бла.

Ну и да, ручная аллокация не заканчивается на маллоке. Заканчивается она им только в подобных «исследованиях».

то может быть и быстрее.

Опять же — бла-бла.

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

Я вам не буду говорить о том на что годны эти ссылки. Есть реальный мир. Го вам в руки и вперёд срывать покровы.

Вы действительно не понимаете что чушь здесь написали? Это же азы всей теории! В случае с GC затраты времени не просто зависят от того, сколько у вас «лишней» памяти,

Вот типичный пример вранья. Я говорил о времени пауз в ответ на рассуждения о времени пауз. Ни о каких затратах памяти речь не шла. Нигде. И вы этого не покажете.

И сейчас меня цитируют БЕЗ СЛОВА О ПАМЯТИ, но рассказывают о том, ЧТО Я ГОВОРИЛ О ПАМЯТИ.

а очень сильно от этого зависят. Как и в случае с обычным распределением памяти, впрочем (локальная память для потока в каком-нибудь tcmalloc'е тоже ведь требует некоторого «запаса»).

Обычное распределение памяти находится далеко за рамками маллока. Эти все рассуждения ничего не стоят вне академической среды. Как и х5 по памяти.

Сравнения глупые, некорректные и не реализуемые в реальности. Чисто попускать хелворды.
>Тем, кто сидит на Java или C# смотрят на Go.
Интересно почему?)) После C# Go выглядит еще более убого чем Basic.
Все те приверженцы учений дедушки Оккама, кто пересыщен убогими решениями с автоматизацией работы памяти, гарбидж коллекторами, нестрогой типизацией, виртуальными машинами и прочим дерьмом типа вывертов ООП уже давно пишут на чистом Си и чувствуют себя сыто и комфортно. ))
Бес попутал ) Хотел сказать динамическая типизация, в противовес статической.

Местами слабая (XName, XNamespace), местами утиная (foreach, linq), местами динамическая (dynamic)...

  1. XName просто обертка над string, с тем же успехом можно считать слабой типизацией отсутствие типа "четное число от 8 до 16".
  2. Утиная в foreach используется очень редко при работе со старыми API типа foreach(Match match in Regex.Matches(...)) где гарантируется правильная работа документацией, в остальных случаях за такое бьют по рукам. Про LINQ вообще не понял — это апи полностью построенно над IEnumerable<T>, где T собственно тип, а не что-то иное.
  3. Динамик вижу очень редко, как правило в старом коде, который работает с JSON. Понемногу заменяют на нормальные типизированные модели (тем более в F# есть дата провайдеры которые облегчают это на порядок). В реальном коде не встречается.

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

Причем тут Regex.Matches? Это как раз нормальное перечисление. Я говорю про тот факт, что оператор foreach можно применять к перебору элементов контейнеров, которые не реализуют IEnumerable.


Про LINQ. Запрос вида from x in X from y in Y select x*y будет преобразован в X.SelectMany(x => Y.Select(y => x*y)) независимо от типов X и Y. Это может быть IEnumerable, IQueryable или любой пользовательский тип данных где определены методы SelectMany и Select.


Да, в копилку операторов с утиной типизацией: await вызывает .GetAwaiter().

Причем тут Regex.Matches? Это как раз нормальное перечисление. Я говорю про тот факт, что оператор foreach можно применять к перебору элементов контейнеров, которые не реализуют IEnumerable.

При том, что он возвращает MatchCollection, который не generic, и соответственно в перечислении возвращает object. Так вот чтобы избежать лишних преобразований вместо `object match in Regex.Matches` и ручного каста пишут `Match match in Regex.Matches`. Это правило было введено из-за отсутствия дженериков в 1.0, так бы мы его не увидели. Остальное — дело обратной совместимости. В 2017 году эту конструкцию практически не используют, потому что C# 2.0 вышел уже давно.

Про LINQ. Запрос вида from x in X from y in Y select x*y будет преобразован в X.SelectMany(x => Y.Select(y => x*y)) независимо от типов X и Y. Это может быть IEnumerable, IQueryable или любой пользовательский тип данных где определены методы SelectMany и Select.

А, вы про квери-синтаксис. Почти не пользуюсь им, так что забыл. Тут объяснение опять же в том, что язык не должен зависеть от библиотеки. То есть чтобы он работал над IEnumerable он должен знать про этот интерфейс. А если компилятор знает о каких-то специальных классах это уже попахивает. Хватит того, что он специальным образом со всякими Int32/Decimal/… работает.

Да, в копилку операторов с утиной типизацией: await вызывает .GetAwaiter().

Аналогично, полагаю не захотелись завязывать компилятор на библиотеку. Хотя странно, да, тип в mscorlib, в принципе не так страшно.
Так вот чтобы избежать лишних преобразований вместо object match in Regex.Matches и ручного каста пишут Match match in Regex.Matches. Это правило было введено из-за отсутствия дженериков в 1.0, так бы мы его не увидели.

Вот только к утиной типизации оно отношения не имеет, это просто приведение типа.


Аналогично, полагаю не захотелись завязывать компилятор на библиотеку. Хотя странно, да, тип в mscorlib, в принципе не так страшно.

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


Вы посчитайте сколько в той же mscorlib есть реализаций Awaiter...

Еще раз, оператор работает из-за того, что изначально IEnumerator не был дженериком. Был бы он дженериком — такого бы не было. Оптимизация была бы на уровне JIT, а не csc.

List<T>.Enumerator появился одновременно с IEnumerable<T>.


А форма foreach (Match m in regex.Matches(...)) с оптимизацией не имеет ничего общего.

ImmutableDictionary вообще в 4.5 появился и все еще реализовывает не-дженерик интерфейсы, и что?

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

Еще раз. Форич появился в 1.0. Зачем переделывать то, что работает, да еще и снижать перфоманс. Да, чисто теоретически это дыра в языке. На практике я лично стараюсь 99% кода писать в LINQ-стиле, потому что он как раз-таки типизирован (по крайней мере если мы о method chain говорим). Динамики иногда используются с JSON, хотя кмк зря они их добавили, пользы от них очень мало, а целый рантайм отдельный за собой тянут. Неймспейсы вообще непонятно к чему были упомянуты, но мы с их обсуждения ушли, значит со мной согласны.
И в этих синтетических «попугаях» не учитывают, что вытворяет код и данные приложения под нагрузкой.
Мне кажется такие тесты не очень репрезентативны. Настоящие проблемы начинаются когда очень много маленьких объектов. Если кто работал с Intellij IDEA, то знает, что это приводит к черепашьей скорости работы и выжиранию CPU. И, видимо, провести какую-то оптимизацию не очень просто из-за сложности архитектуры и большого легаси.
Only those users with full accounts are able to leave comments. Log in, please.