Всем привет, решил поделиться с вами результатами синтетического теста производительности свежих версий PHP и Node.js.

Конфигурация сервера:

Простой VDS — 1 ядро процессора 2ГГц, 1Гб оперативы, 10Гб SSD.
ОС: Debian 8.6.
Так же произведены базовые настройки ядра, чтобы сервер в принципе мог обрабатывать большое кол-во соединений.

Испытуемые:

— PHP 7.1.1 FPM
— Node.js 7.4.0

Первый этап:

Тут операции, которые в основном использует backend. А именно: склеивание строк, сетевой ввод-вывод, арифметика и работа с массивами.

Код для Node.js:

var fs = require('fs');
var mysql = require('mysql2');

console.time('Node.js ' + process.version + ': склеивание строк 1000000 раз');
var str = '';
for (var i = 0; i < 1000000; i++) {
	str += 's';
}
console.timeEnd('Node.js ' + process.version + ': склеивание строк 1000000 раз');


console.time('Node.js ' + process.version + ': сложение чисел 1000000 раз');
var count = 0;
for (var i = 0; i < 1000000; i++) {
	count++;
}
console.timeEnd('Node.js ' + process.version + ': сложение чисел 1000000 раз');


console.time('Node.js ' + process.version + ': наполнение простого массива 1000000 раз');
var array = [];
for (var i = 0; i < 1000000; i++) {
	array.push('s');
}
console.timeEnd('Node.js ' + process.version + ': наполнение простого массива 1000000 раз');


console.time('Node.js ' + process.version + ': наполнение ассоциативного массива 1000000 раз');
var array = {};
for (var i = 0; i < 1000000; i++) {
	array['s' + i] = 's';
}
console.timeEnd('Node.js ' + process.version + ': наполнение ассоциативного массива 1000000 раз');


console.time('Node.js ' + process.version + ': чтение файла 100 раз');
var content;
for (var i = 0; i < 100; i++) {
    content = fs.readFileSync('./someFile.txt');
}
console.timeEnd('Node.js ' + process.version + ': чтение файла 100 раз');


console.time('Node.js ' + process.version + ': mysql query (SELECT NOW()) 100 раз');
// create the connection to database
var connection = mysql.createConnection({host:'localhost', user: 'root', database: 'test', password: 'password'});

function promiseQuery(query) {
    return new Promise((resolve, reject) => {
            connection.query(query, function (err, results, fields) {
            resolve({err, results, fields});
        });
	});
}
for (var i = 0; i < 100; i++) {
    var a = promiseQuery('SELECT NOW()');
    a.then(({err, results, fields}) => {
        //console.log(results);
    });
}
console.timeEnd('Node.js ' + process.version + ': mysql query (SELECT NOW()) 100 раз');
connection.end();

Код для PHP:

<?php
$phpVersion = "v" . explode('-', PHP_VERSION)[0];


$start = microtime(1);
$str = '';
for ($i = 0; $i < 1000000; $i++) {
	$str .= 's';
}

echo "PHP $phpVersion: склеивание строк 1000000 раз: " . round((microtime(1) - $start) * 1000, 3) . "ms \n";


$start = microtime(1);
$count = 0;
for ($i = 0; $i < 1000000; $i++) {
	$count++;
}

echo "PHP $phpVersion: сложение чисел 1000000 раз: " . round((microtime(1) - $start) * 1000, 3) . "ms \n";



$start = microtime(1);
$array = array();
for ($i = 0; $i < 1000000; $i++) {
	$array[] = 's';
}

echo "PHP $phpVersion: наполнение простого массива 1000000 раз: " . round((microtime(1) - $start) * 1000, 3) . "ms \n";


$start = microtime(1);
$array = array();
for ($i = 0; $i < 1000000; $i++) {
	$array["s" . $i] = 's';
}

echo "PHP $phpVersion: наполнение ассоциативного массива 1000000 раз: " . round((microtime(1) - $start) * 1000, 3) . "ms \n";


$start = microtime(1);
for ($i = 0; $i < 100; $i++) {
    $fp = fopen("./someFile.txt", "r");
    $content = fread($fp, filesize("./someFile.txt"));
    fclose($fp);
}

echo "PHP $phpVersion: чтение файла 100 раз: " . round((microtime(1) - $start) * 1000, 3) . "ms \n";


$start = microtime(1);
$mysql = new mysqli('localhost', 'root', 'password', 'test');
for ($i = 0; $i < 100; $i++) {
	$res = $mysql->query("SELECT NOW() as `now`");
	$now = $res->fetch_assoc()['now'];
}

echo "PHP $phpVersion: mysql query (SELECT NOW()) 100 раз: " . round((microtime(1) - $start) * 1000, 3) . "ms \n";

Результаты:

image

Как видно, PHP выигрывает по всем пунктам, кроме операции сложения.

Второй этап:

Нагрузочное тестирование «Hello world». Nginx 11.7 + PHP 7.1.1 FPM vs Node.js. 1000 запросов в 1000 потоков. #ab -n 1000 -c 1000…

Код PHP:

<?php
echo "Hello world";
?>

Код Node.js:

const http = require('http');

const hostname = '127.0.0.1';
const port = 3000;

const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello World');
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});

Результаты

Прогнал по 10 тестов для PHP и для Node.js и выбрал лучшие результаты у обоих.

Node.js:

image

PHP:

image

Как видим и тут PHP выигрывает на 23% или на 628 запросов в секунду. Много это или мало судить вам.

Делитесь в комментах своими мыслями по этому поводу.
Поделиться с друзьями
-->

Комментарии (128)


  1. SerafimArts
    28.01.2017 03:10
    +1

    В первых тестах вы не используете FPM, а используете zts cli версию. А во второй пачке тестов, думаю, корректнее было бы сравнивать Ratchet + PHP, а не Nginx + PHP.


    P.S. Моё личное мнение, преимуществ у ноды две: Это наличие асинхронного апи (а не кусками, как в пыхе) и поддержка в браузерах.


    1. riky
      28.01.2017 04:36
      +3

      асинхронное апи это плюс для производительности но минус для скорости разработки.
      посмотрим что async/await привнесет, может реально будет проще.


      1. SerafimArts
        28.01.2017 05:10
        -4

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


        1. SerafimArts
          28.01.2017 18:46
          +1

          Господа минусующие, что такое асинхронность и как она работает?


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


          Или есть подтверждение обратного и на постоянное переключение контекстов не требуются ресурсы компьютера?


          1. robert_ayrapetyan
            28.01.2017 20:05
            +2

            В «синхронной» модели требуются тысячи потоков и переключение контекста между ними, по вашему это быстрее?


            1. SerafimArts
              28.01.2017 20:25
              -2

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


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


              Подытоживая, наличие асинхронности позволит:
              1) Одновремнно принимать и обрабатывать большее количество запросов.
              2) На порядок быстрее выполнять быстрые операции, если рядом с ними ещё есть тяжёлые. Синхронный цикл работы застопится на тяжёлых и не даст выполниться более легковесным до завершения предыдущих.


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


              1. robert_ayrapetyan
                28.01.2017 20:49
                +2

                Вы тут сейчас уже пытаетесь выкрутиться, но вернемся к первоначальным утверждениям:
                1. Асинхронность — наоборот снижает, как производительность…
                Это ложь.
                2. Постоянное переключение контекста между разными операциями требует дополнительных издержек
                Никакого переключения контекста в потоке не происходит, именно поэтому асинхронные операции быстрее аналогичной реализации на потоках.


      1. Fesor
        28.01.2017 22:04
        +2

        но минус для скорости разработки.

        скорее это просто повышает порог вхождения. В целом уже сейчас в node есть async/await и с ним выходит весьма удобно. В целом же разработка чего-то большого на ноде сейчас требует намного более высокий уровень знаний подходов нежели в PHP.


        Как пример — для node еще нет нормальных ORM. Есть парочка многообещающих но они еще не сильно стабильны. Зато есть качественные query builder-ы, прокси и т.д. То есть при желании можно под проект реализовать DAO и спокойно жить но подавляющее большинство php разработчиков (да и разработчиков на javascript) так не особо умеют делать.


        1. vlasenkofedor
          30.01.2017 12:07

          Как пример — для node еще нет нормальных ORM

          Пример Sequelize почти 9 тысяч звезд


          1. Fesor
            30.01.2017 12:19
            +1

            под нормальной ORM я подразумеваю не очередную имплементацию active record/row-data-gateway а data mapper. На данный момент из наиболее итригующих и лично мне нравящихся решений является TypeORM но оно еще сильно сырое.


            Ну то есть Sequelize в поем понимании годиться как раз для DAO/row data gateway, сверху всеравно надо заворачивать в свои объекты что бы это тестить можно было нормальо.


  1. amaksr
    28.01.2017 03:33
    +7

    У меня из командной строки получился обратный результат:

    >c:\php\php test.php
    PHP v7.0.4: concatenation 1000000 times: 132.97ms
    PHP v7.0.4: addition 1000000 times: 174.055ms
    PHP v7.0.4: adding to array 1000000 times: 154.771ms
    PHP v7.0.4: adding to hash 1000000 times: 249.67ms

    >node test.js
    Node.js v6.9.4: concatenation 1000000 times: 50.109ms
    Node.js v6.9.4: addition 1000000 times: 2.692ms
    Node.js v6.9.4: adding to array 1000000 times: 22.845ms
    Node.js v6.9.4: adding to array hash 1000000 times: 877.773ms


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


    1. amaksr
      28.01.2017 03:44

      В тесте adding to hash если заменить
      array['s'+i] = 's';

      на
      array[''+i] = 's';

      то результат улучшается с 877.773ms до 203.794ms, а если на
      array[i] = 's';

      то до 34.362ms
      Интересно почему…


      1. Maiami
        28.01.2017 04:52
        +1

        Просто v8 сильно оптимизирует работу с обычными, не ассоциативными, массивами


        1. v-derckach
          30.01.2017 16:08

          php7 тоже сильно оптимизирует работу с не ассоциативными массивами. Как раз недавно на хабре была статья о том, как правильно оптимизировать код для php7.


          1. Maiami
            31.01.2017 01:37
            +1

            Выше спрашивали почему так у js, а то что в php7 примерно похожие оптимизации — это стало понятно еще с выходом первой беты php7


        1. zBit
          30.01.2017 19:12

          В JS не такого понятия как «ассоциативный массив»


      1. Fortop
        28.01.2017 04:55

        Потому что конверсия типов идет в индексе массива.
        И операция эта затратнее сложения или присвоения


        1. seokirill
          28.01.2017 09:40

          А разве не играет роли, что процессор оьноядерный оО?!


          1. Fortop
            28.01.2017 13:31
            -1

            В данном конкретном случае — не играет


          1. Fesor
            28.01.2017 22:09
            +1

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


            Это я к тому что не только количество ядер процессора имеет значение. Если вы хотите пример — развертка циклов. Это когда за одну итерацию цикла вы делаете не 1 операцию, а инлайните например 4 штуки.


            for (let i = 0, length = arr.length; i < length; i += 16) {
                arr[i+0] = arr[i+0] * 2; // ну или любая другая операция
                arr[i+1] = arr[i+1] * 2; 
                // ...
                arr[i+15] = arr[i+15] * 2;
            }

            Такие банальности давали прирост порядка 40% при обходе массива пикселей картинок к примеру. Причем такие приемы работают в nodejs чуть лучше чем в php.


  1. Maiami
    28.01.2017 06:53
    +5

    Если добавить немного «логики» в вычисления, и запоминать результат вычислений, то разница уже не в пользу php (кроме ассоциативного массива, с которым в js всегда было не очень хорошо, и на замену сейчас рекомендуется использовать Map там где критична скорость)

    node 7.3.0

    test1 (str): 67.853ms
    str len: 1000000 sb
    
    test2 (sum): 8.685ms
    sum: 2500000
    
    test3 (array): 53.455ms
    array len: 1000000 z
    
    test4 (obj): 1036.474ms
    obj1: b
    obj2: a
    
    test Map: 575.344ms
    obj1: b
    obj2: a
    

    php 7.1.1
    test1 (str): 83.961ms
    str len: 1000000 sb
    
    test2 (sum): 69.857ms
    sum: 2500000
    
    test3 (array): 63.453ms
    array len: 1000000 z
    
    test4 (obj): 206.375ms
    obj1: b
    obj2: a
    


    для js например
    let intns = 1000000
    
    console.log(`\n`)
    console.time('test1');
    var str = '';
    for (var i = 0; i < intns; i++) {
    	if (str.length % 2 === 0) {
    		str += (i % 2 == 0) ? 's' : 'z';
    	}
    	else {
    		str += (i % 2 == 0) ? 'a' : 'b';
    	}
    }
    console.timeEnd('test1');
    console.log('str len: ' + str.length + ' ' + str[0] + str[intns - 1])
    
    console.log(`\n`)
    console.time('test2');
    var count = 0;
    for (var i = 0; i < intns; i++) {
    	if (count % 2 == 0) {
    		count += (i % 2 == 0) ? 1 : 2;
    	}
    	else {
    		count += (i % 2 == 0) ? 3 : 4;
    	}
    }
    console.timeEnd('test2');
    console.log(`sum: ${count}`)
    
    console.log(`\n`)
    console.time('test3');
    var array = [];
    for (var i = 0; i < intns; i++) {
    	array.push((i % 2 == 0) ? 's' : 'z');
    }
    console.timeEnd('test3');
    console.log(`array len: ${array.length} ${array[intns - 1]}`)
    
    console.log(`\n`)
    console.time('test4');
    var obj = {};
    for (var i = 0; i < intns; i++) {
    	obj['s' + i] = (i % 2 == 0) ? 'a' : 'b';
    }
    console.timeEnd('test4');
    console.log(`obj1: ${obj['s' + (intns - 1)]}`)
    console.log(`obj2: ${obj['s' + 0]}`)
    
    console.log(`\n`)
    console.time('test Map');
    var objMap = new Map();
    for (var i = 0; i < intns; i++) {
    	objMap.set('s' + i, (i % 2 == 0) ? 'a' : 'b');
    }
    console.timeEnd('test Map');
    console.log(`obj1: ${objMap.get('s' + (intns - 1))}`)
    console.log(`obj2: ${objMap.get('s' + 0)}`)
    


    1. nikitasius
      30.01.2017 13:18

      Я не разбираюсь ни в node.js, ни в php, но уверен, что тесты зависят от реализации.
      вот результат на Java (1.8.0_102) с рабочего ноута (Intel® Core(TM) i5-5200U CPU @ 2.20GHz)

      test1 (StringBuilder): 39 ms
      str len: 1000000 sb
      
      test2 (sum): 8 ms
      sum: 2500000
      
      test3 (Stack): 52 ms
      stack len: 1000000 z
      
      test3 (Array): 20 ms
      array len: 1000000 z
      
      test4 (array fill): 21 ms
      obj1: b
      obj2: a
      
      test Map (hashmap-Integer): 119 ms
      obj1: b
      obj2: a
      test Map (hashmap-String): 402 ms
      obj1: b
      obj2: a
      


      И я надеюсь, что на node.js и на php можно улучшить результаты.

      • test 1 — на нем при использовании String (как в примере теста) Java просто завалится, ибо будет ад и содомия по созданию строк! Поэтому надо использовать StringBuilder.
      • test 2 — без комментариев.
      • test 3 — при использовании обычного Array колоссальный выигрышь, если же сделать через пуши (на примере Stack из Java), то падение производительности.
      • test 4 — так как map вы выделили в отдельный тест, у меня не хватило логики и знаний, чем заменить массивы с ключем типа Object (хотя бы через String) в Java без map, честное слово хз, поэтому оставил как просто заполнение массива ну и размер на 's' (код символа s) больше (что никак не отразилось на скорости). Тест же массива мепы со строковым ключем в 5м тесте.
      • test Map — обычный хешмап (размер равен вашему 1,000,000 при коэффициенте загрузки 0.7f против 0.75f базовых).


      код программы
      import java.util.Calendar;
      import java.util.HashMap;
      import java.util.Map;
      import java.util.Stack;
      
      public class TestSpeed {
          TestSpeed() {
          }
      
          public static void main(String[] args) {
              try {
                  int intns = 1000000;
                  long time1 = Calendar.getInstance().getTimeInMillis();
                  StringBuilder str = new StringBuilder("");
                  for (int i = 0; i < intns; i++) {
                      if (str.length() % 2 == 0) {
                          str.append((i % 2 == 0) ? 's' : 'z');
                      } else {
                          str.append((i % 2 == 0) ? 'a' : 'b');
                      }
                  }
                  long time11 = Calendar.getInstance().getTimeInMillis();
                  System.out.printf("test1 (StringBuilder): %d ms\n", (time11 - time1));
                  System.out.printf("str len: %d %s%s\n\n", str.length(), str.charAt(0), str.charAt(intns - 1));
      //------
                  long time2 = Calendar.getInstance().getTimeInMillis();
                  int count = 0;
                  for (int i = 0; i < intns; i++) {
                      if (count % 2 == 0) {
                          count += (i % 2 == 0) ? 1 : 2;
                      } else {
                          count += (i % 2 == 0) ? 3 : 4;
                      }
                  }
                  long time22 = Calendar.getInstance().getTimeInMillis();
                  System.out.printf("test2 (sum): %d ms\n", (time22 - time2));
                  System.out.printf("sum: %d\n\n", count);
      //------
                  long time3 = Calendar.getInstance().getTimeInMillis();
                  Stack array = new Stack<>();
                  for (int i = 0; i < intns; i++) {
                      array.push((i % 2 == 0) ? 's' : 'z');
                  }
                  long time33 = Calendar.getInstance().getTimeInMillis();
                  System.out.printf("test3 (Stack): %d ms\n", (time33 - time3));
                  System.out.printf("stack len: %d %s\n\n", array.size(), array.get(intns - 1));
      
                  time3 = Calendar.getInstance().getTimeInMillis();
                  char[] array2 = new char[intns];
                  for (int i = 0; i < intns; i++) {
                      array2[i] = (i % 2 == 0) ? 's' : 'z';
                  }
                  time33 = Calendar.getInstance().getTimeInMillis();
                  System.out.printf("test3 (Array): %d ms\n", (time33 - time3));
                  System.out.printf("array len: %d %s\n\n", array.size(), array.get(intns - 1));
      //------
                  long time4 = Calendar.getInstance().getTimeInMillis();
                  char[] obj = new char[intns + 's'];
                  for (int i = 0; i < intns; i++) {
                      obj['s' + i] = ((i % 2 == 0) ? 'a' : 'b');
                  }
                  long time44 = Calendar.getInstance().getTimeInMillis();
                  System.out.printf("test4 (array fill): %d ms\n", (time44 - time4));
                  System.out.printf("obj1: %s\n", obj['s' + (intns - 1)]);
                  System.out.printf("obj2: %s\n\n", obj['s' + 0]);
      //------
                  long time5 = Calendar.getInstance().getTimeInMillis();
                  Map<Integer, String> objMap = new HashMap<>(intns, 0.7f);
                  for (int i = 0; i < intns; i++) {
                      objMap.put('s' + i, (i % 2 == 0) ? "a" : "b");
                  }
                  long time55 = Calendar.getInstance().getTimeInMillis();
                  System.out.printf("test Map (hashmap-Integer): %d ms\n", (time55 - time5));
                  System.out.printf("obj1: %s\n", objMap.get('s' + (intns - 1)));
                  System.out.printf("obj2: %s\n", objMap.get('s' + 0));
      
                  time5 = Calendar.getInstance().getTimeInMillis();
                  Map<String, String> objMap2 = new HashMap<>(intns, 0.7f);
                  for (int i = 0; i < intns; i++) {
                      objMap2.put("s" + i, (i % 2 == 0) ? "a" : "b");
                  }
                  time55 = Calendar.getInstance().getTimeInMillis();
                  System.out.printf("test Map (hashmap-String): %d ms\n", (time55 - time5));
                  System.out.printf("obj1: %s\n", objMap2.get("s" + (intns - 1)));
                  System.out.printf("obj2: %s\n", objMap2.get("s" + 0));
              } catch (Throwable t) {
                  t.printStackTrace(System.err);
              }
          }
      }
      


      1. Maiami
        30.01.2017 14:05

        test1
        В js по сути тоже самое, вместо resultStr += newStr при большом кол-ве объединений строк лучше использовать

        arr.push(newStr);
        resultStr = arr.join('');
        

        Это и памяти израсходует намного меньше и выполнится быстрее

        Результаты вашего теста на той же машине, что и php и js (если вам интересно):
        test1 (StringBuilder): 46 ms
        str len: 1000000 sb
        
        test2 (sum): 16 ms
        sum: 2500000
        
        test3 (Stack): 42 ms
        stack len: 1000000 z
        
        test3 (Array): 17 ms
        array len: 1000000 z
        
        test4 (array fill): 15 ms
        obj1: b
        obj2: a
        
        test Map (hashmap-Integer): 143 ms
        obj1: b
        obj2: a
        test Map (hashmap-String): 394 ms
        obj1: b
        obj2: a
        


        То что java выигрывает по скорости вполне ожидаемо


      1. Source
        30.01.2017 14:10
        +1

        Обычно при сравнении производительности просто пишут эквивалентный код на разных языках, цели написать его оптимально при этом нет, скорее наоборот. Поэтому StringBuilder применять в данной случае нельзя, т.к. автор поставил задачу замерить именно скорость "ада и содомии по созданию строк".
        А то Вы такими темпами второй тест редуцируете до строчки count = 1000000 и так далее.
        В общем, сравнения должны быть честными, вне зависимости от того, что сравнивается.


        1. nikitasius
          30.01.2017 14:45
          -1

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

          PHP и node.js съедают str+="a";, а Java каждый раз создает новую строку.
          От таких тестов смысла нет.

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

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


          1. Fortop
            30.01.2017 14:56
            -2

            Вы пошли как бы не на третий заход.

            Чуть выше этот вопрос поднимался разными людьми.
            Но дети не хотят по вашему, они хотят лепить…


          1. Shannon
            30.01.2017 15:07
            +2

            Не скажу про php, но js не съедает, а каждый раз создает новую строку, что равносильно тому что в jave

            То о чем вы пишите имеет конкретное название «лучшая реализация алгоритма», и там уже важно кто лучше или умнее реализовал тот-то момент, а то и правда можно написать просто «count = 1000000». К производительности в лоб 2х языков это не имеет отношения

            Автор глупость сравнивает, но даже в таком случае должно быть разъяснение почему и где ошибки, чтобы на более продуманных тестах такую ошибку не допустить (автором или тем кто читает тему)
            Именно с технической точки зрения, а не с «надо/не надо»

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


          1. Source
            30.01.2017 16:24
            +2

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


            1. Fesor
              30.01.2017 18:02

              Если сравнивать скорость конкатенации строк, то можно узнать, что в Java она медленная.

              неверно. Можно узнать что кривое управление памятью приводит к проблемам с производительностью.


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


              1. Source
                31.01.2017 00:11
                +1

                Тут смысл именно сравнить str += 's' и $str .= 's', как эквивалентные операции. А после того, как выяснили, что конкатенация строк в Java медленная, надо задавать себе правильные вопросы, например: "а почему она медленная?". И разумеется, глупо отвечать "потому что в Java кривое управление памятью"… вообще никогда не списывайте на то, что язык плох. Языки пишут отнюдь не идиоты, кроме того в их разработку вкладываются огромные средства. Поэтому не бывает что-то медленно без причины, это всегда следствие определенных компромиссов, которые дают другие преимущества.


                1. Fortop
                  01.02.2017 11:24
                  -2

                  Там не было сказано, что в Java кривое управление памятью.

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

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


                  1. Source
                    01.02.2017 12:53
                    +2

                    Вы ветку комментариев то читаете или к чему Ваш комментарий? Про кривое управление памятью Fesor писал двумя сообщениями выше.


                    1. Fortop
                      01.02.2017 13:02
                      -2

                      Чукча не читатель, чукча писатель.

                      Про кривое управление памятью Fesor писал двумя сообщениями выше.

                      Именно. И он не писал что в Java кривое управление памятью.

                      У меня сложилось впечатление, что Fesor имеет представление о том как происходит аллокация памяти. Чего не знают обычно пользователи скриптовых и языков с динамической типизацией.

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


                      1. Source
                        01.02.2017 13:49
                        +3

                        У меня сложилось впечатление, что Fesor имеет представление о том как происходит аллокация памяти. Чего не знают обычно пользователи скриптовых и языков с динамической типизацией.

                        Очень самокритично )
                        Так и быть, просвещу Вас: в Java строки иммутабельны и вместо конкатенации по факту происходит создание новой строки, поэтому Ваш пассаж про непрерывный блок памяти вообще мимо кассы.


                        1. Fortop
                          01.02.2017 14:06
                          -2

                          Так и быть, просвещу Вас: в Java строки иммутабельны

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

                          Формулу стоимости обсуждаемой операции в общем виде можно выразить примерно так
                          cost = N*CA+BR*CR+BW*CW
                          где
                          N — количество вызовов аллокации блока
                          CA — стоимость аллокации блока.
                          BR — количество блоков, которые требуется прочитать
                          CR — стоимость чтения блока.
                          BW — количество блоков которые требуется записать
                          CW — стоимость записи блока.

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

                          И все это не отменяет тот факт, что проблема будет не в самой конкатенации, а в управлении памятью при этом.


                        1. Fesor
                          01.02.2017 23:40

                          в Java строки иммутабельны и вместо конкатенации по факту происходит создание новой строки,

                          в PHP тоже, да и в javascript-те.


                          поэтому Ваш пассаж про непрерывный блок памяти вообще мимо кассы.

                          А еще давайте посмотрим дальше.


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


                          Про кривое управление памятью Fesor писал двумя сообщениями выше.

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


  1. PaulZi
    28.01.2017 11:07
    +3

    А ведь недавно, в 5 версии, PHP был жутким тормозом по сравнению с JavaScript.
    Результаты, лично для меня, говорят, что теперь не важно, на чём вы пишите, важно как — говнокод можно реализовать на любой платформе и языке.


  1. jehy
    28.01.2017 11:18
    +3

    Ну сколько же можно сравнивать тёплое и мягкое? При этом путём засовывания молотка в задницу.


  1. youROCK
    28.01.2017 13:02

    В целом, маловероятно, чтобы ванильный PHP был быстрее JavaScript'а для долгоживущих процессов, в которых JIT уже успел всё соптимизировать. Впрочем, если по какой-то причине вы упираетесь в вычисления, можете попробовать HHVM или kPHP, они могут вас удивить.


    1. SamDark
      28.01.2017 14:02
      +2

      kPHP удивляет, но не производительностью. А HHVM по факту сравнялся с PHP 7.


      1. youROCK
        28.01.2017 14:04
        +2

        Ну, все же, всё зависит от того, какой код вы будете бенчмаркать. Если это «обычный код на PHP», то да, от HHVM большого толку, скорее всего, не будет. А вот если это вычисления, да ещё и с указанием scalar type hints и прочее, да ещё и с использованием .hhbc формата, разница может быть очень существенной.


        1. xRay
          28.01.2017 14:09
          +1

          Еще в HHVM нет пачки расширений PHP. И просто так нужные отсутствующие расширения от PHP в HHVM не затолкать.

          И как выше уже сказал SamDark по факту HHVM сравнялся с PHP 7.


      1. AterCattus
        28.01.2017 18:47

        Если довелось попробовать kPHP, то интересно зачем)


  1. xRay
    28.01.2017 14:06
    -2

    Если уж сравнивать с Node.js так надо сравнивать с ReactPHP.

    И вот хороший прогон тестов https://gist.github.com/nkt/e49289321c744155484c#gistcomment-1970430
    По итогам теста PHP 7 быстрее.


    1. Moxa
      28.01.2017 14:42
      +2

      По итогам теста PHP 7 быстрее.

      быстрее чем что?


      1. xRay
        28.01.2017 19:50
        -1

        Чем, с чем сравнивали. Посмотрите по ссылке тест.


        1. Moxa
          28.01.2017 20:19
          +4

          по результатам этих тестов php 7 быстрее, чем php 5.6, но в два раза медленнее nodejs 6


  1. zein
    28.01.2017 15:33

    Как минимум хорошо уже хотя бы то, что их можно сравнивать в принципе.


  1. michael_vostrikov
    28.01.2017 16:43

    Не совсем по теме, но насчет конкатенации строк в PHP была такая мысль.
    Насколько я знаю, там сейчас после каждой конкатенации создается новая строка, то есть перевыделяется память. А что если не конкатенировать сразу, а внутри строкового типа хранить список исходных строк. А итоговое значение получать только при некоторых операциях типа поиск/замена. Что-то вроде string builder-а в C# и Java. Мне кажется, производительность в коде наподобие теста из статьи была бы выше.
    Вроде недавно для encapsed-строк что-то такое сделали, но там это работает в пределах одного выражения. Никто не в курсе, разработчики что-нибудь думали на эту тему?


    1. dim_s
      28.01.2017 23:27

      Так PHP и делает, по крайней мере, когда идет постоянная работа с оператором ".=" (добавление к строке). На уровне реализации языка включается внутренний StringBuilder.


      1. Fesor
        29.01.2017 15:40
        +1

        Если вы напишите код вроде такого:


        $foo = 'foo';
        $foo .= 'bar';
        $foo .= 'baz';

        то PHP будет выполнять следующее:


        line     #* E I O op                           fetch          ext  return  operands
        -------------------------------------------------------------------------------------
           3     0  E >   ASSIGN                                                   !0, 'foo'
           4     1        ASSIGN_CONCAT                                 0          !0, 'bar'
           5     2        ASSIGN_CONCAT                                 0          !0, 'baz'
                 3      > RETURN                                                   1

        то есть по сути у нас будет одна операция присвоения и две операции конкатенации. Никаких буферов и прочего.


        С другой стороны в php7.1 уже добавлена оптимизация для интерполяции строк:


        $foo = "{$foo}{$bar}{$baz}";

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


  1. amakhrov
    29.01.2017 00:38

    console.time('Node.js ' + process.version + ': mysql query (SELECT NOW()) 100 раз');
    
    for (var i = 0; i < 100; i++) {
       // вызов асинронного кода с промисами
    }
    
    console.timeEnd('Node.js ' + process.version + ': mysql query (SELECT NOW()) 100 раз');

    Этот код запускает 100 запросов к БД параллельно, и завершается, не дождавшись их выполнения.
    В этой связи очень странно, что время node-кода больше, чем время php-кода, который все запросы выполнил последовательно. Как так вышло вообще?


    1. MaZaAa
      29.01.2017 10:23
      -2

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


      1. Maiami
        29.01.2017 22:29
        +2

        Нет, промисы дешево стоят, не сильно дороже callback (особенно если использовать bluebird)

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


        1. MaZaAa
          29.01.2017 22:46
          -2

          Я буквально месяц назад замерял разницу в скорости, между нативными промисами, Bluebird и callback'ами, так вот, callback'и выигрываю в разы… Не верите, убедитесь в этом сами. Это довольно легко.


          1. Maiami
            30.01.2017 01:16
            +2

            Уже проверяла, разница минимальна, но в данном тесте это не играет роли, по причинам которые выше написала


    1. Fesor
      29.01.2017 15:42

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


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


      1. amakhrov
        29.01.2017 20:56

        Это не я вызываю, это код автора статьи. И результаты, приведенные в статье: 9ms на php и 13ms на node.


  1. Source
    29.01.2017 02:18

    1000 запросов в 1000 потоков через ab в качестве нагрузочного тестирования? Вы серьёзно?
    Запустите нормальный нагрузочный тест, что-нибудь типа:
    wrk -t 4 -c 100 -d30s --timeout 2000 http://127.0.0.1:8080


    1. MaZaAa
      29.01.2017 10:43
      -1

      # wrk -t 4 -c 100 -d30s --timeout 2000 http://127.0.0.1:3000
      # zmalloc: Out of memory trying to allocate 16000000040 bytes

      У меня не достаточно памяти на сервере чтобы прогнать этот бенчмарк)


      1. Source
        29.01.2017 12:20
        +1

        Попробуйте поставить -t равным кол-ву ядер на сервере, и плавно снижать -c, пока не влезет.
        Главное, время выполнения оставьте, а то ваш вариант теста в течении 300 миллисекунд вообще не отражает реальность.
        P.S. И wrk не с сервера запускайте, а с локального компа.


    1. Maiami
      29.01.2017 22:23
      +1

      Хоть wrk, хоть ab (которые действительно надо запускать не на тестируемой машине, а на удаленной)
      Автор всё равно сравнивает 5 тредов php-fpm против 1-го в ноде
      Нужно либо ноду в кластере запускать, либо у php-fpm дефолтные настройки снизить

      https://habrahabr.ru/post/320670/#comment_10039552


      1. MaZaAa
        29.01.2017 23:19
        -2

        Кластер запускается когда процессор многоядерный. Тоже самое касается и Nginx, где worker_processes рекомендуется ставить = числу ядер. А у меня процессор одноядерный. Тем более, я само собой запускал ноду в кластере и результат — гораздо хуже. Т.к ядро у меня одно.


        1. Maiami
          30.01.2017 01:27
          +1

          И конечно же в случае кластера вы не ставили nginx перед node…

          Чтобы упростить задачу, у php-fpm снизьте до 1 server-start (и 0 чилдов), потому что у вас в 5 раз больше php обработчиков (которые работают как треды, а не как воркеры), а результат всего на 23% лучше, что говорит не в пользу php


          1. Fesor
            30.01.2017 02:55
            +1

            которые работают как треды, а не как воркеры

            Вообще-то как воркеры. Ну то есть там поднимаются полноценые процессы-воркеры.


            а результат всего на 23% лучше, что говорит не в пользу php

            это больше говорит о блокируемых штуках. Вообще весь этот бенчмарк весьма и весьма сомнительный. Как минимум потому что PHP в плане инфраструктуры еще не может тягаться с нодой. А производительности самого языка хватает да.


        1. Maiami
          30.01.2017 02:05
          +2

          Стенд на виртуалке с 1 ядром:

          1 треад:
          php-fpm: 1612 req/sec
          node.js: 3854 req/sec

          5 тредов:
          nginx+php-fpm: 5373 req/sec
          nginx+node.js: 9845 req/sec

          Не знаю уж как вы тестировали


          1. Fortop
            30.01.2017 04:38

            Вообще-то придираться к частностям числодробилки малость бессмысленно.

            Если сильно хочется что-то проверить, то стоит тестировать конкретное приложение (даже не hello world).

            Естественно на двух языках.

            В треде уже давали ссылку на коллективный бенчмарк. Нода быстрее пхп примерно в 2-3 раза в среднем.

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


            1. Maiami
              30.01.2017 05:53
              +1

              Числодробилки тоже надо тестировать правильно, раз уж кто-то взялся их сравнивать:
              https://www.youtube.com/watch?v=HPFARivHJRY


              1. Fortop
                30.01.2017 05:59
                -2

                Уже сравнили


                Но в критически важных местах я просто напишу расширение, которое будет быстрее ноды в те же самые 3-5 раз


                1. Maiami
                  30.01.2017 06:03
                  +2

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


                  1. Fortop
                    30.01.2017 06:05
                    -2

                    Если они сравнивают «свои сценарии использования», то о чем вообще речь?
                    Вот в сценариях топикстартера сравнение такое.
                    Но вы его критикуете.
                    Вы предлагаете заменить их сценарий на ваш?


                    1. Maiami
                      30.01.2017 06:34
                      +2

                      Я лишь добавила в его сценарий необходимый минимум, чтобы тестирование было объективным. А так же указала, что доступ к mysql сделан с ошибкой, и сравнение веб-серверов сделано не правильно

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


                      1. Fortop
                        30.01.2017 06:36
                        -3

                        Давайте будем откровенны и формальны.

                        Вы на базе его сценария создали свой и настаиваете на его правильности.

                        Но ваш сценарий =/= его сценарию


                        1. Maiami
                          30.01.2017 06:50
                          +2

                          Нет, не будем формальны, будем логичны
                          Начало статьи

                          Всем привет, решил поделиться с вами результатами синтетического теста производительности свежих версий PHP и Node.js.

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


                          1. Fortop
                            30.01.2017 06:59
                            -3

                            Ок. Будем логичны.

                            Тогда все еще печальнее для вас.

                            Для определения того что тесты сделаны с ошибками надо знать какая логика закладывалась в них.
                            Потому что «производительность» там тестируется. То что там тестируется производительность логики работающей с ошибками не делает утверждение ложным

                            Вместе с тем.

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

                            Но вы их делаете «до» получения уточнения от автора.
                            Следовательно вы придумали свой личный сценарий и пытаетесь навязать его.

                            Так в чем же у вас логика? :)
                            Пока её отсутствие проявилось


                            1. Maiami
                              30.01.2017 07:09
                              +2

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

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

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


                              1. Fortop
                                30.01.2017 07:18
                                -3

                                Логика в том, что бенчмарки производительности не подразумевают сравнение пустого по смыслу цикла

                                Это ваши личные фантазии.
                                Вы мне напоминаете любителей анаши, которые порицают прочих курильщиков.

                                Более того ваши знания об оптимизаторах как php так и nodejs мною оценены быть не могут, но вы сами признались что в php разбираетесь крайне плохо.
                                На основании чего вы сделали вывод, что в указанном коде php произведёт удаление неиспользуемого кода?

                                Дальше все еще интереснее.
                                Производительность это эффективность выполнения той или иной операции.
                                Но… Скажите мне, с какого бодуна вы решили, что для сравнения производительности необходимо кастрировать php и nodejs убрав влияние их оптимизаторов?
                                Вообще-то, они (оптимизаторы) для того и созданы, чтобы улучшить производительность.

                                Внезапно?

                                P.S. Я понятия не имею что хотел автор.
                                На мой вкус его и ваши позиции бредовые.
                                Но вас не устраивает этот факт :-D


                                1. Maiami
                                  30.01.2017 07:24
                                  +2

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


                                  1. Fortop
                                    30.01.2017 07:30
                                    -3

                                    Девочка, видео я честно просмотрел первые минут 15. Нового ничего для себя не вынес.
                                    Ну не откровение это для меня. А детали V8 меня на данный момент не слишком беспокоят.

                                    Тема беседы в том, что вы дурочки оба. Но одна по какой-то причине считает что права именно она.
                                    Тогда как ошибаетесь вы с автором оба.

                                    Хотите производительности — занимайтесь профилированием конкретных приложений. А не выкидывайте оптимизиторы, opcache, jit-компилятор и прочее.


                                    1. Maiami
                                      30.01.2017 07:41
                                      +3

                                      Тогда странно, что вы умудряетесь говорить очевидные глупости вроде:
                                      «Скажите мне, с какого бодуна вы решили, что для сравнения производительности необходимо кастрировать php и nodejs убрав влияние их оптимизаторов?»

                                      Впрочем фраза «вы дурочки оба» хорошо вас характеризует


                                      1. Fortop
                                        30.01.2017 08:18
                                        -3

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

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

                                        И с этой позиции абсолютно фиолетово, кто из вас двоих глупее.

                                        Доступно?


                                        1. Maiami
                                          30.01.2017 08:25
                                          +3

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


                                          1. Fortop
                                            30.01.2017 08:44
                                            -3

                                            То что вы упорно твердите про отключение оптимизатора (хотя даже намека на это нет, и если бы вы разбирались, то уже поняли бы это)

                                            Девочки такие девочки…
                                            Логика как обычно и рядом не стояла, а за своими словами не следят…
                                            Печалька.

                                            Ну что же цитируем намёки

                                            Если добавить немного «логики» в вычисления, и запоминать результат вычислений, то разница уже не в пользу php

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

                                            Нужно либо ноду в кластере запускать, либо у php-fpm дефолтные настройки снизить

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

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

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

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

                                            Эти синтетические тесты сделаны с ошибкой, в видео рассказывается причина таких ошибкой

                                            Йо? Опять?
                                            Ну откройте же тайну… Чего же надо сделать кроме того о чем говорилось выше — нивелировать влияние оптимизатора…

                                            Ну так что?
                                            Отвечать за свои слова и признавать свою неправоту будем?


                                            1. Maiami
                                              30.01.2017 08:49
                                              +2

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


                                              1. Fortop
                                                30.01.2017 08:51
                                                -4

                                                ЧТД.

                                                Девочка такая девочка…

                                                P.S. Техника построения микробенчмарков это частный случай реализации юнит-тестов.
                                                Почитайте-ка литературу об этом, судя по вам это будет вашим следующим озарением после видеоролика :-D


                            1. michael_vostrikov
                              30.01.2017 11:12
                              +1

                              Для определения того что тесты сделаны с ошибками надо знать какая логика закладывалась в них. Потому что «производительность» там тестируется. То что там тестируется производительность логики работающей с ошибками не делает утверждение ложным

                              Насколько я понял из комментов выше:
                              Есть 2 теста. Один читает из базы, во втором чтения из базы нет, а происходят ошибки. Значит измеряется время разных действий. В статье была заявлена цель сравнить время одинаковых действий. Вывод: тесты сделаны с ошибками, результаты сравнивать бессмысленно.


                              Извините, но по-моему логики нет у вас.


                              1. Fortop
                                30.01.2017 11:51

                                Насколько я понял из комментов выше:

                                Вы сейчас об одной и той же ветке рассуждаете?

                                Проблемы автора, описанные тут
                                https://habrahabr.ru/post/320670/#comment_10040720
                                В данной нити не поднимались.

                                Поэтому попробуйте доказать отсутствие логики в «моих» репликах ещё раз.


                                1. Shannon
                                  30.01.2017 13:11
                                  +1

                                  Вся ветка ни о чем
                                  Логики в ваших репликах не больше чем в смысле тестов из статьи

                                  Автор хотел протестировать производительность
                                  1. чтобы протестировать производительность, нужно совершить какую-то работу
                                  2. чтобы совершить работу нужно чтобы эта работа была сделана, а не выкинута оптимизатором как «результат не используется дальше по коду, можно скипнуть»
                                  3. цикл для того чтобы сэмитировать много работы
                                  4. в цикл добавляется смысл, чтобы оптимизатор попал в более инетерсные условия, чем никому не интересные сложения чисел
                                  5. после цикла работа с результатом

                                  Оптимизатор пускай оптимизирует именно работу, что и интересует в вопросе производительности, тут вообще нечего обсуждать, это азы, даже знаменитый performance.js работает именно так (только чуть сложнее)

                                  Коллеги, не тратье время на тролля, приберегите еду для более интересного случая


                                  1. Fortop
                                    30.01.2017 14:03
                                    -2

                                    чтобы протестировать производительность, нужно совершить какую-то работу

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

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

                                    Даю подсказку — она нулевая.

                                    Те же тесты на debian.org отличаются в лучшую сторону тем, что проверяют производительность на алгоритмах


                                    1. Shannon
                                      30.01.2017 14:19
                                      +2

                                      Нее, так и написал же, «Не «какую-то», а полезную» :D

                                      Оптимизатор такой смотрит на код и думает «а полезная ли это работу, или можно пока на печи поваляться» *и руки в боки у него*

                                      Всё таки наверное можно немного покормить:
                                      С вами бесполезно говорить, вы оперируете терминами «полезная работа, тесты на debian.org отличаются в лучшую сторону», еще чуть чуть и вообще не важно, что в этих комментариях обсуждается конкртено эта статья, и будем мысли Fortop читать, лишь бы знать, что есть полезная работа, а что бесполезная :D

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

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


                                      1. Fortop
                                        30.01.2017 14:28
                                        -2

                                        Мысли читать не нужно.
                                        Нужно заниматься профилированием.

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

                                        Производители бенчмарков типа 3DMark ваши грабли собрали лет десять назад.
                                        Именно поэтому никто особо и не тестирует сейчас сколько полигонов можно отрендерить (читайте выполнить в цикле пачку бесполезных запросов вида SELECT NOW())


                                        1. Shannon
                                          30.01.2017 14:32
                                          +1

                                          Нее, у меня столько еды нет, мне работать надо :D

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


                                          1. Fortop
                                            30.01.2017 14:38
                                            -2

                                            Речь до сих пор идет лишь про то, что даже такой примитив надо тестировать по канонам

                                            Не надо.

                                            Вы пришли к той же позиции, что и девочка выше — «а давайте улучшим пирожок из какашек добавками со вкусом клубники и ванили», и «вы тут его недосолили».


                                            1. Shannon
                                              30.01.2017 14:49
                                              +1

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

                                              Вот и приходится в вариациях «пирожков из какашек» барахтаться (у кого-то получше-повкуснее, как у debian.org, если верить вашему вкусу), но всё равно они шо то ..., шо то…

                                              Я так понимаю еды у вас уже достаточно, потому что «аууу, то что вы говорите настолько очевидно, что в приличном обществе это даже не обсуждают, это всё и так знают» :D


                                              1. Fortop
                                                30.01.2017 14:51
                                                -2

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


                                                Поэтому пользуйтесь тем, что вам даёт community.
                                                Ссылка тут неоднократно приводилась.


                                                1. Shannon
                                                  30.01.2017 14:56
                                                  +1

                                                  Ой, йой, только вот по ссылке точно такая же «бесполезная» работа, с которой вы так яростно боритесь :D

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


                                                  1. Fortop
                                                    30.01.2017 15:08
                                                    -1

                                                    несколько более сложные алгоритмы, а не тестирование простейших операций

                                                    Вопрос корректности фикстур, прогрева кешей и jit-компиляторов открыт, поскольку не читал их методику измерения детально и там есть странные упоминания о запуске через popen(), но…
                                                    можете ознакомиться самостоятельно


                                                    1. Shannon
                                                      30.01.2017 15:14

                                                      Нее, еды вам хватит, уже начали совсем забывать о чем речь


                                                    1. Shannon
                                                      30.01.2017 15:26
                                                      +1

                                                      Блин, у нас в офисе уже ставки делают, будет ли следующий комментарий
                                                      2+2=4 :D
                                                      И про то, что качественные тесты лучше некачественных :D


                                                      1. Shannon
                                                        30.01.2017 15:27

                                                        Завтра дочитаю. Надо всё таки седня работать :D


                                                      1. Fortop
                                                        30.01.2017 15:46
                                                        -3

                                                        Беда в том, что вы и этих 2+2=4 не знаете, и продолжаете настаивать что все перечисленные в треде поправки внезапно сделают тесты правильными.

                                                        У вас там что? Полный офис таких же глупых?


                                1. michael_vostrikov
                                  30.01.2017 13:48
                                  +3

                                  Нажмите стрелочку "вверх" несколько раз и найдите фразу "А так же указала, что доступ к mysql сделан с ошибкой, и сравнение веб-серверов сделано не правильно". Пожалуй, соглашусь с комментом выше.


                                  1. Fortop
                                    30.01.2017 13:55
                                    -2

                                    Что же вы все такие медленные…

                                    На пальцах поясняю.

                                    Понятия не имею, что хотел протестировать автор статьи. Но…
                                    Даже с исправлениями мадам тесты не станут правильными. От слова вообще.
                                    Слишком велика погрешность оценки для приложений и систем при таком подходе.


  1. iShatokhin
    29.01.2017 10:24
    +3

    Тест в один прогон? Чтобы включился JIT, нужно хотя бы парочку прогонов для прогрева, и тогда результаты для Node.js улучшаться. А вообще чистейшая синтетика не показатель реальной производительности будущего сервера.


    1. MaZaAa
      29.01.2017 10:26
      -1

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


      1. iShatokhin
        29.01.2017 13:19

        Как я понял, было выполнено десять запусков тестов по одному прогону, JIT так не работает, т.к. не сохраняет оптимизации между запусками процесса.


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


        Вы пытаетесь заставить работать Node.js как PHP — поднимаете и убиваете процесс на задачу, это не верно. У них разные парадигмы. Node.js процесс должен жить постоянно.


        1. MaZaAa
          29.01.2017 13:49

          Так я и не убивал процесс Node.js, я просто запускал 10 раз подряд ab


    1. Maiami
      29.01.2017 22:21

      Автор сравнивает 5 тредов php-fpm против 1-го в ноде
      Нужно либо ноду в кластере запускать, либо у php-fpm дефолтные настройки снизить

      https://habrahabr.ru/post/320670/#comment_10039552


  1. yeti357
    29.01.2017 13:50
    +1

    Тащемта, не знаю как в php, но в ноде(да и вообще не только в js, в зависимости от реализации) конкатенацию строк из более чем 10 элементов следует выполнять путём заполнения массива и последующего join-а этого массива. Ну и бывает такое что в ноде некоторые операции проседают в производительности при выходе новой версии(issues можно посмотреть на гитхабе), потом чинят, но осадочек остаётся.

    Даже если в целом php стал быстрее, то что с того? Бежать переписывать менять одно не типизированное шило на другое нетипизированное мыло?


  1. mnepohyi
    29.01.2017 21:56
    +1

    Сравнил с c#. Не стал сравнивать запросы к базе, т.к. нет mysql.

    C#
    Склеивание строк 1000000 раз: 6
    Cложение чисел 1000000 раз: 1
    Наполнение простого массива 1000000 раз: 5
    Наполнение ассоциативного массива 1000000 раз: 505
    Чтение файла 100 раз: 3

    Node.js v6.9.2: склеивание строк 1000000 раз: 57.496ms
    Node.js v6.9.2: сложение чисел 1000000 раз: 3.266ms
    Node.js v6.9.2: наполнение простого массива 1000000 раз: 28.342ms
    Node.js v6.9.2: наполнение ассоциативного массива 1000000 раз: 871.787ms
    Node.js v6.9.2: чтение файла 100 раз: 6.732ms

    Код c#

    var timer = new Stopwatch();
    timer.Start();
    var sb = new StringBuilder();
    for (int i = 0; i < 1000000; i++)
    {
        sb.Append('s');
    }
    var str = sb.ToString();
    timer.Stop();
    Console.WriteLine("Склеивание строк 1000000 раз: " + timer.ElapsedMilliseconds);
    
    timer.Reset();
    timer.Start();
    var count = 0;
    for (int i = 0; i < 1000000; i++)
    {
        count++;
    }
    timer.Stop();
    Console.WriteLine("Cложение чисел 1000000 раз: " + timer.ElapsedMilliseconds);
    
    timer.Reset();
    timer.Start();
    var array = new List<char>();
    for (int i = 0; i < 1000000; i++)
    {
        array.Add('s');
    }
    timer.Stop();
    Console.WriteLine("Наполнение простого массива 1000000 раз: " + timer.ElapsedMilliseconds);
    
    timer.Reset();
    timer.Start();
    var dictionary = new Dictionary<string, char>();
    for (int i = 0; i < 1000000; i++)
    {
        dictionary["s" + i] = 's';
    }
    timer.Stop();
    Console.WriteLine("Наполнение ассоциативного массива 1000000 раз: " + timer.ElapsedMilliseconds);
    
    timer.Reset();
    timer.Start();
    string context;
    for (int i = 0; i < 100; i++)
    {
        context = File.ReadAllText("SomeFile.txt");
    }
    timer.Stop();
    Console.WriteLine("Чтение файла 100 раз: " + timer.ElapsedMilliseconds);
    Console.ReadLine();
    



    1. Maiami
      29.01.2017 22:27
      +1

      У вас та же проблема что и у автора, вот тут правильное сравнение с теми же тестами, но уже не пользу php:
      https://habrahabr.ru/post/320670/#comment_10039552

      Тесты в цикле нельзя просто прогнать, нужно еще результат запомнить и прочитать его после цикла, иначе оптимизатор просто выкинет половину из цикла, или вовсе схлопнет цикл
      Из статьи видим, что php это делает более агрессивно чем js, достаточно добавить немного «логики» в вычисления и php уже отстаёт


      1. MaZaAa
        29.01.2017 22:52
        -2

        Добавил чтение результата после циклов, результат тот же, увы


        1. Maiami
          30.01.2017 01:31
          +1

          Не вижу в статье добавление чтения результата. Так же нужно хотя бы примитивную логику добавлять в любые микробенчмарки
          Вот правильное тестирование (ваш тест только добавлена логика и чтение результата) и результат не в пользу php:
          https://habrahabr.ru/post/320670/#comment_10039552


      1. mnepohyi
        29.01.2017 22:54
        +1

        Да, что-то я не подумал совсем об этом.

        Склеивание строк 1000000 раз: 12

        Cложение чисел 1000000 раз: 7
        count: 3500000

        Наполнение простого массива 1000000 раз: 8
        array.length: 1000000

        Наполнение ассоциативного массива 1000000 раз: 518
        dictionary.length: 1000000
        a

        Чтение файла 100 раз: 3

        var timer = new Stopwatch();
        timer.Start();
        var sb = new StringBuilder();
        for (int i = 0; i < 1000000; i++)
        {
            if (sb.Length % 2 == 0)
                sb.Append(i % 2 == 0 ? 's' : 'z');
            else
                sb.Append(i % 2 == 0 ? 'a' : 'b');
        }
        var str = sb.ToString();
        timer.Stop();
        Console.WriteLine("Склеивание строк 1000000 раз: " + timer.ElapsedMilliseconds);
        Console.WriteLine();
        
        timer.Reset();
        timer.Start();
        var count = 0;
        for (int i = 0; i < 1000000; i++)
        {
            if (count % 2 == 0)
                count += i % 2 == 0 ? 1 : 2;
            else
                count += i % 2 == 0 ? 3 : 4;
        
            count++;
        }
        timer.Stop();
        Console.WriteLine("Cложение чисел 1000000 раз: " + timer.ElapsedMilliseconds);
        Console.WriteLine("count: " + count);
        Console.WriteLine();
        
        timer.Reset();
        timer.Start();
        var array = new List<char>();
        for (int i = 0; i < 1000000; i++)
        {
            array.Add(i % 2 == 0 ? 's' : 'z');
        }
        timer.Stop();
        Console.WriteLine("Наполнение простого массива 1000000 раз: " + timer.ElapsedMilliseconds);
        Console.WriteLine("array.length: " + array.Count);
        Console.WriteLine();
        
        timer.Reset();
        timer.Start();
        var dictionary = new Dictionary<string, char>();
        for (int i = 0; i < 1000000; i++)
        {
            dictionary["s" + i] = i % 2 == 0 ? 'a' : 'b';
        }
        timer.Stop();
        Console.WriteLine("Наполнение ассоциативного массива 1000000 раз: " + timer.ElapsedMilliseconds);
        Console.WriteLine("dictionary.length: " + dictionary.Count);
        Console.WriteLine(dictionary["s" + (dictionary.Count - 2)]);
        Console.WriteLine();
        
        timer.Reset();
        timer.Start();
        string context;
        for (int i = 0; i < 100; i++)
        {
            context = File.ReadAllText("SomeFile.txt");
        }
        timer.Stop();
        Console.WriteLine("Чтение файла 100 раз: " + timer.ElapsedMilliseconds);
        Console.ReadLine();
        



      1. Fesor
        29.01.2017 23:14

        Из статьи видим, что php это делает более агрессивно чем js, достаточно добавить немного «логики» в вычисления и php уже отстаёт

        PHP этого не умеет делать пока-что.


        1. Maiami
          30.01.2017 01:47
          +3

          Но что-то он точно умеет делать (по крайней мере opcache точно умеет, который на сколько я знаю с php7 какой-то версии включен по умолчанию)

          Иначе добавление небольшой логики и чтение результата не могло бы привнести такую разницу:

          Пример из статьи 
          node test1: 49ms
          php test1: 13ms
          //php как будто-бы разгромно выигрывает
          
          Пример из статьи + "логика":
          node test1: 67ms
          php test1: 83ms
          //php уже отстает, хотя сложности вычислений почти не добавилось
          

          Оффтоп:
          Тоже самое со всеми остальными тестами (где игнорируется совет мелкие строки всегда не складывать, а пушить в массив и потом джойнить, вместо чтения ответа с mysql читается ошибка соединения, вместо 1xphp-fpm vs 1xnode тестируется 5xphp-fpm vs 1xnode и т. д.)

          Сравнивают всё равно не php-fpm vs node, а нерабочий вариант vs нерабочий вариант и даётся это под видом каких-то выводов


          1. Maiami
            30.01.2017 01:53

            На самом деле по поводу php умеет или нет, спорить не буду, явно вы лучше в этом разбираетесь


          1. Fesor
            30.01.2017 18:17
            +1

            Но что-то он точно умеет делать (по крайней мере opcache точно умеет, который на сколько я знаю с php7 какой-то версии включен по умолчанию)

            с версии 5.5.


            opcache не особо умеет оптимизировать AST. Он оптимизирует поток байткода как может. В целом насколько я знаю оптимизаций вроде "схлопнуть пустой цикл" ближайшие пару лет ждать не придется (хотя они грозятся что LLVM впилят в 8-ой версии). Сейчас больше работа идет над учетом типов и оптимизации виртуальной машины (например есть одна и та же операция которая в зависимости от типа аргументов будет заменяться на более специфичные опкоды).


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


            Пример из статьи + "логика":

            зависит от логики. Да и в целом если мы внимательно посмотрим на код, то там как бы нет таких циклов которые можно выпилить оптимизатором. Даже те же запросы в базу могут вызывать глобальные сайд эффекты. Ну а выйгрыш проигрыш зависит целиком и полностью от способа работы с памятью. В PHP в этом плане сделано хорошо, но есть много других моментов которые будут замедлять код. Многие вещи в этих тестах в PHP будут работать существенно быстрее тупо потому что это прямой вызов Си-ных функций и стоимость абстракции можно почти не учитывать. А в nodejs все же есть абстракция.


            Сравнивают всё равно не php-fpm vs node, а нерабочий вариант vs нерабочий вариант и даётся это под видом каких-то выводов

            Вообще если мы сравниваем ноду и пых в контексте одноразовых скриптов (например когда нам важно время старта) то победитель будет php в силу своей специфики. Но PHP дико проигрывает по уровню инфраструктуры. А это намного более важный показатель.


            1. Fortop
              01.02.2017 11:29

              Но PHP дико проигрывает по уровню инфраструктуры.


              Который раз вижу от вас эту фразу.
              Хотелось бы услышать конкретику. Что имелось в виду под инфраструктурой?
              И usecase где есть существенные отличия.

              Если это статьи то ссылки, если это ваш личный опыт, то может быть это отличная тема для статьи на хабре? :)

              В отличии от пхп с нодой весьма прохладный опыт работы недошедший до чего-то серьезного


  1. sayber
    29.01.2017 22:17

    Все это конечно хорошо, но реальность немного другая.
    Какой бы язык не выбрать, узкое место будет БД.

    Опять же, что то лучше читает, что то выполняет мат. функции и т.д.


    1. MaZaAa
      29.01.2017 23:07
      -1

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


  1. point212
    30.01.2017 13:09
    -1

    Вообще-то тут две разных концепции построения приложения.
    На NodeJS приложение постоянно живёт. Единожды загрузившись, инициализировав фреймворк, все коннекты, подгрузив кэши — оно просто перемалывает запросы.

    А PHP вынужден на каждый запрос создавать кучу соединений, подгружать кучу ресурсов (ОК, даже если они уже в виде готового кода лежат в кэше, а кэш в памяти — все равно это все инициализируется заново).

    Немного нечестно такое сравнивать в лоб.


  1. zBit
    30.01.2017 19:55
    -1

    Пара замечаний по коду NodeJS:
    1. Вместо

    вот этого
    var array = [];
    for (var i = 0; i < 1000000; i++) {
      array.push('s');
    }


    1. Fortop
      01.02.2017 11:37
      -1

      php может работать асинхронно, но не out-of-the-box.
      И мог это всегда (возможно за исключением версий времен php/fi)

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


      1. zBit
        01.02.2017 18:54
        +1

        А вот читать файл асинхронно в общем случае не получится ни в пхп, ни в ноде.
        Как так? В NodeJS можно читать файл асинхронно. Может вы путаете с многопоточностью? Тут я точно знаю, что NodeJS однопоточный, но асинхронные операции не являются блокирующими и когда вы читаете файл синхронно и загружаете его весь в ОЗУ — то это плохо, т.к. синхронное чтение заблокирует поток, а файл с большим размером уронит процесс (out of memory). Самый правильный вариант (ИМХО) — создавать ReadableStream.
        Да и не имеет смысла, вы только ухудшите производительность (для HDD) и мало что выиграете для SSD.
        На чём строится это умозаключение?


        1. Fortop
          01.02.2017 19:00
          -2

          Как так? В NodeJS можно читать файл асинхронно

          Вы правы и неправы одновременно.

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

          Я ваше «синхронно» воспринял как «последовательно».
          Все остальное имелось в виду в этом контексте.
          Читать файл лучше и быстрее последовательно.

          На чём строится это умозаключение?

          Вопрос после уточнения актуален?


          1. zBit
            01.02.2017 20:29
            -1

            Странно, что вы перепутали «последовательно» и «асинхронно» :)