Pull to refresh
33
0
Oleg Butko @deviator

Программист

Send message
Нашёл один момент
vibe 0.7.28 вышел 27 февраля (hot fix версия), а этот тест производился 25 февраля
возможно эти 2 бага сильно влияли на производительность
Было бы классно, если бы Вы поделились с нами)
я не против минусов, только когда я понимаю за что
по сути вы правы, надо было приводить все параметры
все 3 варианта собирались с помощью dub --build=release

ps: а насчёт libevent я добавил в текст статьи ещё когда проверял сборки с ldc и gdc
Боюсь спросить где вы пытаетесь исать…
dlang
dconf
за что минус, я не понял. скомпилировал, сравнил, ответил на вопрос…
Прошу заметить, что я не пытался сделать максимально производительный или правильный код, а пытался сделать эквивалентный (в силу своих познаний) код на двух разных языках.

Хотя теоретически D — должен работать быстрее GO.

мне хотелось поднять вопрос не «кто быстрее в целом» (который я для других языков уже пытался поднять), а именно для веб… и тут, по всей видимости, компилятор и язык не будут играть большой роли, а будут играть: подход к сборке мусора, архитектура библиотеки (в vibe радуют compile-time шаблоны страниц), алгоритмы роутинга и тд

Хотелось проверить всё в целом, в сумме (кроме работы с базой данных). На самом деле я думал D проиграет в веб значительно, просто было интересно во сколько раз, но оказалось не всё так просто.
Вот что я искал, перед тем как писать эту статью…
Правильно ли я понял, что Вы сейчас прооперировали среднепальцевыми значениями, даже не запустив код (ни мой, ни тот, корректный, который Вы написали)?
тут первый комментарий, второй пункт, хотя, возможно, чисто субъективное мнение отвечающего, я подумал, что так и обстоят дела
Результаты сборок с ldc и gdc практически не отличаются скоростью.
Мне кажется шаблонизатор тут малую роль играет, там очень мало кода (подставляется лишь 1 переменная в коротком шаблоне). Я всё-таки пытался сравнить именно работу остальной части приложений.
Не думаю, что убрав так называемый «мусор» можно добиться хоть какого-нибудь прироста производительности, так как это влияет только на время компиляции, а не на работу приложения. Но на всякий случай измерил и ощутимой разницы не обнаружил (разница в пределах 5%, от запуска к запуску).

Насчёт больше/меньше текста: значение Document Length одинаково, оба приложения отдают 182 байта.

А вот по поводу шаблонов в го ничего сказать не могу. Может быть вы подскажите как реализовать «честное» сравнение?
4 секунды мало, потому что сборщик мусора не успевает включиться в бой?
А вторую часть не до конца понял. Веб-сервера не встроены ни в D ни в Go — это библиотечные реализации. В целом я их хотел их сравнить: кто шустрее, меньше памяти ест, процессор грузит. Или вы имели ввиду что ещё можно что-то сравнивать в производительности веб приложений? Я только начинаю этим заниматься, поэтому не знаю, какие ещё могут быть критерии.
А учитывая загрузку процессора, так совсем впереди


Слева D, справа Go
Не совсем понимаю значение, этого флага: если соединение остаётся поднятым сервер должен меньше работы выполнять?
Когда соединение остаётся открытым? Например если я установлю связь с сервером через websocket?

Прогнал такой тест: ab -k -c500 -n100000 http://localhost:8100/

dlang
Concurrency Level: 500
Time taken for tests: 1.654 seconds
Complete requests: 100000
Failed requests: 0
Keep-Alive requests: 100000
Total transferred: 35300000 bytes
HTML transferred: 18200000 bytes
Requests per second: 60450.25 [#/sec] (mean)
Time per request: 8.271 [ms] (mean)
Time per request: 0.017 [ms] (mean, across all concurrent requests)
Transfer rate: 20838.81 [Kbytes/sec] received

Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 2.1 0 50
Processing: 0 8 14.0 2 178
Waiting: 0 8 14.0 2 178
Total: 0 8 14.6 2 178

Percentage of the requests served within a certain time (ms)
50% 2
66% 8
75% 14
80% 15
90% 19
95% 22
98% 32
99% 92
100% 178 (longest request)

golang
Concurrency Level: 500
Time taken for tests: 3.297 seconds
Complete requests: 100000
Failed requests: 0
Keep-Alive requests: 100000
Total transferred: 32300000 bytes
HTML transferred: 18200000 bytes
Requests per second: 30330.25 [#/sec] (mean)
Time per request: 16.485 [ms] (mean)
Time per request: 0.033 [ms] (mean, across all concurrent requests)
Transfer rate: 9567.06 [Kbytes/sec] received

Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 2.0 0 45
Processing: 0 16 15.7 12 139
Waiting: 0 16 15.7 12 139
Total: 0 16 15.8 12 139

Percentage of the requests served within a certain time (ms)
50% 12
66% 18
75% 25
80% 28
90% 39
95% 48
98% 60
99% 68
100% 139 (longest request)



В этой ситуации D выигрывает в 2 раза.
Да, видимо, так правильней будет сказать.
не думаю, что такую мелочь стоит выносить в отдельный модуль
но я добавил это в desmath
Думаю можно обойтись только делегатами и вынести в отдельную функцию создание класса.
Правка
import std.stdio;
import std.traits;

auto f(string s) {
    return s.length;
}

auto g(string s) {
    return 2*s.length;
}

struct S {
    auto getClosure() {
        return (string s) { return 3*s.length; };
    }
}

class X(R, A...) { // три точки значат, что можно это не один тип может быть, а кортеж типов
    R delegate(A) g;
    A a;

    this(R delegate(A) g, A a) {
        this.g = g;
        this.a = a;
    }

    R run() { return g(a); }
}

auto newX(F,Args...)( F fnc, Args args )
    if( isCallable!F && __traits(compiles, { fnc(args); }) )
    // F может быть даже объектом с методом opCall
    // с помощью __traits мы проверяем можем ли вызывать эту fnc с такими аргументами
{
    return new X!( ReturnType!F, ParameterTypeTuple!F ) // тип объекта всё равно нужно задавать
                 ( (Args a){ return fnc(a); }, args );
    // в любом случае создаём делегат
}

void main() {
    auto x = newX(&f, "a");
    auto y = newX(&g, "ab");
    auto s = S();
    auto d = s.getClosure();
    auto z = newX(d, "abc");
    auto Z = [x,y,z];
    foreach(t; Z) {
        writeln(t.run());
    }
}
Вариант функций создания
Вместо одной универсальной можно написать 2 специальные
auto newX(R,Args...)( R delegate(Args) fnc, Args args )
{ return new X!(R,Args)( fnc, args ); }

auto newX(R,Args...)( R function(Args) fnc, Args args )
{ return new X!(R,Args)( (Args a){ return fnc(a); }, args ); }


Можно озвучить более полную задачу? А то есть маленькое чувство, что Вы стреляете из пушки по воробьям.
Другой подход
import std.stdio;
import std.traits;

auto f(string s) {
    return s.length;
}

auto g(string s) {
    return 2*s.length;
}

struct S {
    auto getClosure() {
        return (string s) { return 3*s.length; };
    }
}

auto apply(F,Args...)( F fnc, Args args )
    if( isCallable!F && __traits(compiles,fnc(args)) )
{ return { return fnc(args); }; }

void main() {
    auto x = apply(&f, "a");
    auto y = apply(&g, "ab");
    auto s = S();
    auto d = s.getClosure();
    auto z = apply(d, "abc");
    auto Z = [x,y,z];
    foreach(t; Z) {
        writeln(t());
    }
}

Не совсем понял аргумент. Если мы говорим про многопоточное программирование с блокировками в общем, то оно само по себе не является простым занятием. Передача сообщений это лишь способ снижения сложности, а конкретней, способ уйти от таких взаимоблокирующих вызовов. Многопоточное программирование без блокировок ещё более сложная задача, что подтверждает Ваш пример (кстати, весьма познавательный). Да и нет у него особой специфики в D. Та же самая функция cas и все те же проблемы, что и, например, в С++. А от велосипедов уйти никогда не удасться на 100%, так как std.concurrency не может покрыть все задачи, а реализовывать непокрытые как-то нужно. Поход, описанный мной, показывает только особенности способа создания очереди сообщений именно в D. Я не говорил, что надо делать всегда именно так. Выбор всегда остаётся за програмистом.

Information

Rating
Does not participate
Location
Москва, Москва и Московская обл., Россия
Registered
Activity