Как стать автором
Обновить

Сравним C++, JS, Python, Python + numba, PHP7, PHP8, и Golang на примере расчёта “Простое Число”

Время на прочтение4 мин
Количество просмотров16K

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

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

Измерять можно довольно много параметров и для разных целей.

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

В данной статье мы рассмотрим ускорение программы с использованием 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++ --version
Configured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/4.2.1
Apple clang version 11.0.3 (clang-1103.0.32.62)
Target: x86_64-apple-darwin19.6.0
Thread model: posix
InstalledDir: /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 = 0; i < N; i++) {
        isPrime(i);
    }
    end = clock();
    cout << (end - start) / ((double) CLOCKS_PER_SEC);
    cout << " sec \n";
    return 0;
}

Go (golang)

go version
go version go1.15.4 darwin/amd64
package main

import (
   "fmt"
   "math"
   "time"
)

func isPrime(num int) bool {
   if num == 2 {
      return true
   }
   if num == 1 || num%2 == 0 {
      return false
   }
   to := int(math.Sqrt(float64(num)))
   for div := 3; div <= to; div += 2 {
      if num%div == 0 {
         return false
      }
   }
   return true
}

func do(N int) {
   for i := 0; i < N; i++ {
      prime := isPrime(i)
      if prime {
         // fmt.Printf("%+v: %+v\n", i, prime)
      }
   }
}

func main() {
   st := time.Now()
   do(10_000_000)
   fmt.Printf("%+v\n", time.Since(st))
}

Спасибо за комментарий про int32/int64 -https://habr.com/ru/post/532432/#comment_22432438

Go - int32

func isPrime(num int32) bool {
	if num == 2 {
		return true
	}
	if num <= 1 || num%2 == 0 {
		return false
	}
	to := int32(math.Sqrt(float64(num)))
	for div := int32(3); div <= to; div += 2 {
		if num%div == 0 {
			return false
		}
	}
	return true
}

Время: 2.365265757s

Go - int64

func isPrime(num int64) bool {
	if num == 2 {
		return true
	}
	if num <= 1 || num%2 == 0 {
		return false
	}
	to := int64(math.Sqrt(float64(num)))
	for div := int64(3); div <= to; div += 2 {
		if num%div == 0 {
			return false
		}
	}
	return true
}

Время: 7.115535174s

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

<?php

function 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 --version
Python 3.8.5
import math
from time import perf_counter


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


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)

Python with "numba"

import math
from time import perf_counter

from 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 (вариант с int64).

А Вы, что думаете по этому поводу? Что здесь можно добавить или изменить для полноты эксперимента?

Интересно ли Вам посмотреть показатели производительности по работе с текстом, картинками и сетью?

Теги:
Хабы:
Всего голосов 29: ↑14 и ↓15+3
Комментарии91

Публикации

Истории

Работа

Data Scientist
78 вакансий
PHP программист
106 вакансий
Python разработчик
121 вакансия
Программист C++
109 вакансий
QT разработчик
4 вакансии
Go разработчик
123 вакансии

Ближайшие события

15 – 16 ноября
IT-конференция Merge Skolkovo
Москва
22 – 24 ноября
Хакатон «AgroCode Hack Genetics'24»
Онлайн
28 ноября
Конференция «TechRec: ITHR CAMPUS»
МоскваОнлайн
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань