И добавлю то же C++ приложение, но в режиме fastcgi через nginx:
Concurrency Level: 100
Time taken for tests: 1.208 seconds
Complete requests: 10000
Failed requests: 0
Write errors: 0
Total transferred: 1560000 bytes
HTML transferred: 210000 bytes
Requests per second: 8275.49 [#/sec] (mean)
Time per request: 12.084 [ms] (mean)
Time per request: 0.121 [ms] (mean, across all concurrent requests)
Transfer rate: 1260.72 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.9 0 7
Processing: 3 12 2.5 12 32
Waiting: 2 12 2.7 12 32
Total: 3 12 2.2 12 32
Percentage of the requests served within a certain time (ms)
50% 12
66% 12
75% 12
80% 13
90% 13
95% 16
98% 19
99% 21
100% 32 (longest request)
Но как правильно указал автор, не совсем корректно сравнивать ничего не делающее приложение.
В подобном тесте fastcgi дает большие накладные расходы. Ведь NodeJS не был спрятан за http сервером? Если сравнивать C++ приложение, работающее в качестве http сервера, результаты будут значительно лучше. Сравнил с моей поделкой.
NodeJS:
Concurrency Level: 100
Time taken for tests: 1.877 seconds
Complete requests: 10000
Failed requests: 0
Write errors: 0
Total transferred: 1150000 bytes
HTML transferred: 140000 bytes
Requests per second: 5328.73 [#/sec] (mean)
Time per request: 18.766 [ms] (mean)
Time per request: 0.188 [ms] (mean, across all concurrent requests)
Transfer rate: 598.44 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.2 0 6
Processing: 1 19 3.4 17 33
Waiting: 1 19 3.4 17 32
Total: 4 19 3.4 17 33
Percentage of the requests served within a certain time (ms)
50% 17
66% 18
75% 18
80% 19
90% 25
95% 26
98% 30
99% 31
100% 33 (longest request)
C++ приложение:
Concurrency Level: 100
Time taken for tests: 0.493 seconds
Complete requests: 10000
Failed requests: 0
Write errors: 0
Total transferred: 1370000 bytes
HTML transferred: 210000 bytes
Requests per second: 20287.80 [#/sec] (mean)
Time per request: 4.929 [ms] (mean)
Time per request: 0.049 [ms] (mean, across all concurrent requests)
Transfer rate: 2714.29 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 2 0.6 2 7
Processing: 1 3 0.8 3 8
Waiting: 1 2 0.7 2 7
Total: 3 5 0.8 5 11
Percentage of the requests served within a certain time (ms)
50% 5
66% 5
75% 5
80% 5
90% 6
95% 6
98% 9
99% 9
100% 11 (longest request)
Хотя мой код далеко не оптимальный, если сравнивать с nginx.
while (it != m_mapClients.end()) //Перечисляем всех клиентов
{
if (!it->second->Continue()) //Делаем что-нибудь с клиентом
m_mapClients.erase(it++); //Если клиент вернул false, то удаляем клиента
else
it++;
}
Должно быть вот так:
it = m_mapClients.erase(it);
С другой — мне не нравится когда старожилы хабра уводят в минуса людей с желанием писать программы и статьи. Пусть даже уровень этих статей далеко не дотягивает до уровня желаемого.
Я не минусовал но подозреваю, что причина минусов в том, что автор категорически не хочет прислушиваться к мнению более опытных разработчиков (особенно это заметно по комментариям к первой статье). А с таким подходом далеко не продвинуться.
Для сборки программ — конечно следует выбрать что-то вроде cmake/qmake/scons и т.д. Однако make создан не только для компиляции, это просто удобная программа, умеющая отслеживать зависимости. Например вы хотите сайт на markdown писать, make в помощь:
Советую поглядеть вывод make -p, там очень много полезного. Переменная CC по умолчанию содержит имя C компилятора (т.е. на Linux это gcc, также есть и CXX и другие). CFLAGS по умолчанию пустой, но используется вот так:
$ CFLAGS=-g make
$ CFLAGS=-O2 make
Т.е. переменными окружения выбираем, что собирать — релизную или отладочную версию.
Шаблон .c.o указывает, каким образом из .c файлов получить .o файлы.
%< и $@ — это переменные, значение которых содержит имя цели или зависимости.
PS: где-то у меня был более универсальный Makefile с генерацией зависимостей и поиском исходников. Если найду, могу показать.
Ваши выводы не совсем верны. Для вашего php приложения не имеет значения, кто выступает http сервером. Однако в других случаях эта разница может присутствовать. Например при использовании long-polling или когда приложение получает POST запросы (apache вроде бы еще не научился держать тысячи соединений, которые постят по 1Кб в минуту) и т.д.
Вызов Sleep(0), даже при замене на SwitchToThread — плохое решение. Переход на уровень ядра и переключение контекста обходятся очень дорого. А тут оно может выполнится много раз. Почему бы не воспользоваться событиями для WinAPI и condition variables для POSIX?
IMHO, не очень хорошая идея. Придет человек вроде меня и спросит: а такое «Вконтакте»? Вот я понятия не имею, что там за приложение, зачем оно нужно и что от него требуется. Конечно же если вы собираетесь делать подобное приложение, то вам и не подойдет такой программист. Поэтому если и использовать подобный подход, то не «берем популярное, большое (в плане функционала) и сложное (в плане реализации) приложение», а берем именно то приложение, функциональность которого схожа с будущими задачами для этого программиста.
Вообще то WinAPI, как и огромное количество другого системного кода, это C, а не C++, а стало быть наличие там данных макросов — нормальная ситуация.
Вот в том то и дело, что там код на C, поэтому макросы, хоть и вызывают неудобства, но без них не обойтись. А мы говорим о C++, и о коде, который макросов не требует вообще.
Пусть код со скобками для меня привычнее, но это нисколько не мешает воспринять условия в исходном примере кода с однострочными ифами — так очень многие пишут.
Я конечно опять нарываюсь на минусы, но все же нормальный код не должен так выглядеть. В книгах так часто пишут чтобы сократить объем. Но там и примечания ставят соответствующие. Но в статье на сайте нет смысла экономить строки.
С void on_clients() скорее согласен, отступ там желателен, но почему бы не сообщить о таком допущении в личку?
Следуя логике большинства — код компилируется? Да, значит он правильный. Так зачем что-то сообщать? Мои претензии к коду — исключительно мое личное дело, я никому не навязываю свой стиль. Тем более, что стилей очень много. Мне вот венгерская нотация не нравится, но не сообщать же теперь об этом в личку каждому автору, посмевшему ее использовать? :-)
Ситуация с
typedef std::vector<client_ptr> array;
это ещё один привет из C++03.
Я специально показал следующий пример с array из boost. Asio появился в boost начиная с версии 1.35, открываю первый попавшийся пример и вижу вот такие строки:
Сомнительно, что автор пишет о boost asio и не знает о контейнере array. А если знает, то зачем вводить потенциально конфликтный тип данных?
Пожалуй соглашусь только с привидением типов в стиле C при использовании в C++, а вот всё остальное: макросы, goto, и сырые указатели использовать можно если это действительно необходимо.
Я и не говорил, что это нельзя использовать. Разумеется можно, но только там, где это действительно необходимо. Но ведь не сокращать каждый вызов макросом.
p.s: руководствуясь написанным у Вас в пункте 3 вообще не понимаю зачем было писать что либо в данном топике, если данная тема Вам не интересна.
Тема как раз интересна, иначе бы я не стал открывать этот топик и тем более пролистывать до комментариев.
1. Плох сам факт использования макросов. Это как если бы вместо for/while использовать метку и goto, вроде такого:
int i = 0;
label:
// code
if (++i < 100)
goto label;
Казалось бы, ну что тут плохого? Код на C++, компилируется и работает без ошибок. Значит код правильный и использовать можно. Примерно такими же соображениями руководствовался программист, сделавший в где-то в WinAPI макрос с именем max и мне уже много лет приходится писать #ifdef max\n#undef max\n#endif. Чем именно плохи макросы, по моему, написано в любой книге по программированию на C++ и также дана рекомендация не использовать их. Но давайте я не буду учить вас программированию.
2. Тема для холивара — это какой стиль лучше. Тут же, откровенно говоря, стиля нет вообще. Код написан небрежно.
Вот например:
void on_read(const error_code & err, size_t bytes)
{
if ( err) stop();
if ( !started() ) return;
std::string msg(read_buffer_, bytes);
if ( msg.find("login ") == 0) on_login(msg);
else if ( msg.find("ping") == 0) on_ping();
else if ( msg.find("ask_clients") == 0) on_clients();
}
Вот зачем специально запутывать людей, создавая тип array, который на самом деле vector? Это ведь разные типы данных, один динамический массив, другой использует память на стеке. Кстати говоря, члены класса talk_to_client:
Ну и все остальное в том же духе. Конечно кому-то кажется, что человек может писать так, как ему хочется. Использовать макросы и goto, привидение типов в стиле C, free и delete вместо RAII… Надо проработать программистом не один год и в разных командах, чтобы осознать важность этих правил.
И да, я вижу, что это перевод. И даже потрудился заглянуть в оригинал и выяснить, что макросы и такой вот нехороший код идут от туда, а не от переводчика. Но вопрос был совсем не в том, хорош перевод или нет. Я отвечал на комментарий объяснив причину, по которой я стал ставить плюс.
Макросы MEM_FN. Перевод для начинающих, ну и не надо начинающих программистов приучать к макросам. Это же C++.
Форматирование исходных текстов ужасно. Я понимаю, что это нудно и неудобно… но код должен быть оформлен красиво.
Ну и содержимое на столько простое и очевидное, что лично я не вижу смысла в этом переводе. Документация у boost не на столько хороша, как у Qt, но все же имеется.
Поэтому я и написал: «Почитал комментарии и очень удивлен». Я не удивлен статьей, там все как и должно быть. А удивлен тем, что судя по комментариям, и на IT ресурсе люди точно также используют честный ответ на подобный вопрос. И даже советуют не использовать вопросы, ответы на которые можно нагуглить…
Но как правильно указал автор, не совсем корректно сравнивать ничего не делающее приложение.
NodeJS:
C++ приложение:
Хотя мой код далеко не оптимальный, если сравнивать с nginx.
Вы правы, что-то я уже забываю C++03 и полагаюсь на C++11 :-)
Должно быть вот так:
Я не минусовал но подозреваю, что причина минусов в том, что автор категорически не хочет прислушиваться к мнению более опытных разработчиков (особенно это заметно по комментариям к первой статье). А с таким подходом далеко не продвинуться.
Ну и любые подобные задачи. Так что make не перестает быть удобным и полезным инструментом. Хотя да, использую я его все реже и реже… :-)
make -p
, там очень много полезного. Переменная CC по умолчанию содержит имя C компилятора (т.е. на Linux это gcc, также есть и CXX и другие). CFLAGS по умолчанию пустой, но используется вот так:Т.е. переменными окружения выбираем, что собирать — релизную или отладочную версию.
Шаблон
.c.o
указывает, каким образом из .c файлов получить .o файлы.%< и $@ — это переменные, значение которых содержит имя цели или зависимости.
PS: где-то у меня был более универсальный Makefile с генерацией зависимостей и поиском исходников. Если найду, могу показать.
Это и кроссплатформенно, и на C++, и не подвержено проблеме c10k и вообще самый правильный способ.
Вот в том то и дело, что там код на C, поэтому макросы, хоть и вызывают неудобства, но без них не обойтись. А мы говорим о C++, и о коде, который макросов не требует вообще.
Я конечно опять нарываюсь на минусы, но все же нормальный код не должен так выглядеть. В книгах так часто пишут чтобы сократить объем. Но там и примечания ставят соответствующие. Но в статье на сайте нет смысла экономить строки.
Следуя логике большинства — код компилируется? Да, значит он правильный. Так зачем что-то сообщать? Мои претензии к коду — исключительно мое личное дело, я никому не навязываю свой стиль. Тем более, что стилей очень много. Мне вот венгерская нотация не нравится, но не сообщать же теперь об этом в личку каждому автору, посмевшему ее использовать? :-)
Я специально показал следующий пример с array из boost. Asio появился в boost начиная с версии 1.35, открываю первый попавшийся пример и вижу вот такие строки:
Пруф: www.boost.org/doc/libs/1_35_0/doc/html/boost_asio/example/allocation/server.cpp
Сомнительно, что автор пишет о boost asio и не знает о контейнере array. А если знает, то зачем вводить потенциально конфликтный тип данных?
Я и не говорил, что это нельзя использовать. Разумеется можно, но только там, где это действительно необходимо. Но ведь не сокращать каждый вызов макросом.
Тема как раз интересна, иначе бы я не стал открывать этот топик и тем более пролистывать до комментариев.
Ну и предлагаю завершить эту дискуссию :-)
Казалось бы, ну что тут плохого? Код на C++, компилируется и работает без ошибок. Значит код правильный и использовать можно. Примерно такими же соображениями руководствовался программист, сделавший в где-то в WinAPI макрос с именем max и мне уже много лет приходится писать #ifdef max\n#undef max\n#endif. Чем именно плохи макросы, по моему, написано в любой книге по программированию на C++ и также дана рекомендация не использовать их. Но давайте я не буду учить вас программированию.
2. Тема для холивара — это какой стиль лучше. Тут же, откровенно говоря, стиля нет вообще. Код написан небрежно.
Вот например:
и тут уже ниже
Глядя на первый кусок уже и не кажется, что строка
msg += (*b)->username() + " ";
является телом цикла. Ведь даже отступа нет.Тут вообще аргумент функции на новой строке.
Вот зачем специально запутывать людей, создавая тип
array
, который на самом делеvector
? Это ведь разные типы данных, один динамический массив, другой использует память на стеке. Кстати говоря, члены класса talk_to_client:должны быть объявлены вот так:
Ну и все остальное в том же духе. Конечно кому-то кажется, что человек может писать так, как ему хочется. Использовать макросы и goto, привидение типов в стиле C, free и delete вместо RAII… Надо проработать программистом не один год и в разных командах, чтобы осознать важность этих правил.
И да, я вижу, что это перевод. И даже потрудился заглянуть в оригинал и выяснить, что макросы и такой вот нехороший код идут от туда, а не от переводчика. Но вопрос был совсем не в том, хорош перевод или нет. Я отвечал на комментарий объяснив причину, по которой я стал ставить плюс.
Разумеется минус я тоже не стал ставить.