Все топовые языки программирования уже давно доказали свои позиции и "определились" с нишами своего использования.
Тем не менее, для каждого программиста важно иметь представление о количественных характеристиках для каждого из языков, которыми он пользуется.
Измерять можно довольно много параметров и для разных целей.
Для каких-то задач важнее будет наличие быстрого просчёта по математическим операциям. А для других - больше пригодится ускоренная работа с сетью и файлами.
В данной статье мы рассмотрим ускорение программы с использованием JIT-компиляции для языков Python и PHP.
В качестве задачи для расчёта возьмём функцию проверки - является ли число Простыми или нет - "is prime". Возьмём базовый алгоритм проверки на то, что число Простое:
-
число не чётное
-
и не делится на меньшее число до корень из искомого (то есть в цикле пройдёмся от 3 до корень из числа)
Нам будет необходимо вычислить ряд Простых чисел - до максимального. Максимальное число в этой задаче будет: 10 000 000.
В алгоритме и в коде ниже можно видеть, что я не применял распараллеливание, для более "честной" оценки времени исполнения.
Машина, на которой производились запуски:
Название модели: MacBook ProИдентификатор модели: MacBookPro14,1Имя процессора: Dual-Core Intel Core i5Скорость процессора: 2,3 GHzКоличество процессоров: 1Общее количество ядер: 2Кэш 2-го уровня (в каждом ядре): 256 КБКэш 3-го уровня: 4 МБТехнология Hyper-Threading: ВключенПамять: 8 ГБВерсия Boot ROM: 428.0.0.0.0Версия SMC (система): 2.43f10
Рассмотрим варианты алгоритма на разных языках программирования:
C++
g++ --versionConfigured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/4.2.1Apple clang version 11.0.3 (clang-1103.0.32.62)Target: x86_64-apple-darwin19.6.0Thread model: posixInstalledDir: /Library/Developer/CommandLineTools/usr/bin
#include <iostream>#include <cmath>#include <time.h>using namespace std;bool isPrime(int num){ if (num == 2) { return true; } if (num <= 1 || num % 2 == 0) { return false; } double sqrt_num = sqrt(double(num)); for (int div = 3; div <= sqrt_num; div +=2) { if (num % div == 0) { return false; } } return true;}int main(){ int N = 10000000; clock_t start, end; start = clock(); for (int i ; i < N; i++) { isPrime(i); } end = clock(); cout << (end - start) / ((double) CLOCKS_PER_SEC); cout << " sec \n"; return 0;}
Node.js
node --version v15.0.1
function isPrime(num) { if (num === 2) { return true; } if (num <= 1 || num % 2 === 0) { return false } for (let div = 3; div <= Math.sqrt(num); div += 2) { if (num % div === 0) { return false; } } return true;}function main(N) { const st = new Date().getTime(); for (let i = 0; i < N; i++) { let prime = isPrime(i); if (prime) { // console.log(i + ': ' + prime); } } console.log((new Date().getTime() - st) / 1000);}(function (){ const N = 10_000_000; main(N)})()
PHP
<?phpfunction isPrime($num){ if ($num == 2) { return true; } if ($num == 1 || $num %2 == 0) { return false; } $to = sqrt($num) + 1; for ($i = 3; $i <= $to; $i += 2) { if ($num % $i == 0) { return false; } } return true;}function run($N){ for ($i = 0; $i <= $N; $i++) { isPrime($i); }}function main(){ $st = microtime(true); run(10000000); echo microtime(true) - $st;}// Да, код не продакшен-реди)) Прошу не шеймить за топорность кода.main();
Python (without "numba")
python3 --versionPython 3.8.5
import mathfrom time import perf_counterdef is_prime(num): if num == 2: return True if num == 1 or not num % 2: return False for div in range(3, int(math.sqrt(num)) + 1, 2): if not num % div: return False return Truedef do(n): for i in range(n): is_prime(i)if __name__ == '__main__': N = 10_000_000 st = perf_counter() do(N) end = perf_counter() print(end - st)
Python with "numba"
import mathfrom time import perf_counterfrom numba import njit@njit(fastmath=True)def is_prime(num): if num == 2: return True if num == 1 or not num % 2: return False for div in range(3, int(math.sqrt(num)) + 1, 2): if not num % div: return False return True@njit(fastmath=True)def do(n): for i in range(n): is_prime(i)if __name__ == '__main__': N = 10_000_000 st = perf_counter() do(N) end = perf_counter() print(end - st)
После запуска на локальной машине можно сравнить время исполнения всех вышеперечисленных программ:
Примечание: меньше - лучше.Здесь можно видеть, что версия на JS довольно "быстрая".
А также:
-
python3 + numba показала огромный прирост в производительности! Обогнав даже Go. Круто!
-
PHP8 с включенным JIT показала себя хорошо. И сравнимо с Go!
Вероятно, что-то ушло из моего поля зрения и в данной задаче я не учёл какой-то момент и js в базовом исполнении оказался быстрее Go.
А Вы, что думаете по этому поводу? Что здесь можно добавить или изменить для полноты эксперимента?
Интересно ли Вам посмотреть показатели производительности по работе с текстом, картинками и сетью?