Комментарии 91
А можно, пожалуйста, добавить для версии PHP флаги с которыми он запускался? Конкретнее: Флаги jit, кли-опкеша и jit буфера?
P.S. Сам проверил этот код (windows, i7 6700k), получилось соотношение примерно такое же, как приведённое, так что думаю, что у вас настройки этих флагов были схожие:
// PHP 8.0: ~4 секунды с копейками
php bench.php -dopcache.enable_cli=1 -dopcache.jit_buffer_size=10M -dopcache.jit=1255
// PHP 7.4: ~26 секунд с копейками
php bench.php -dopcache.enable_cli=1
P.P.S. Для сравнения
$ node -v
v14.5.0
$ node test.js
2.454
/usr/local/Cellar/php/8.0.0/bin/php -dopcache.enable=1 -dopcache.enable_cli=1 -dopcache.jit=1255 -dopcache.jit_buffer_size=100M ./php/index.php
У меня от раза к разу время было «рядом». То есть 7-8 и 21-22 сек, соответственно PHP8 и PHP7.
Ну а в целом, слишком простой пример, думаю, тут просто негде разгуляться нормальному оптимизатору.
Приличный C/C++ компилятор к чертям выкинет весь цикл, так как isPrime функция без побочных эффектов и результат нигде не используется.
main: # @main
push rbx
call clock
mov rbx, rax
# nothing here
call clock
sub rax, rbx
...
(https://godbolt.org/z/4Wfcsj, clang 11.0.0 -O2)
Хотя в любом случае не вижу смысла в таких простых бенчмарках, где тестятся 2 вложенных цикла. Здесь нечего вообще оптимизировать, кроме как выкинуть вызов функции, а значит JIT компиляторы конечно будут выдавать результат на равне с C++.
А вот там где кода много, плюс всякие структуры и классы, которые оптимизатор может попробовать разобрать на части, в общем там, где GCC и CLang/LLVM делают десятки, если не сотни оптимизационных проходов по коду вылизывая его, там уже будет видно кто есть кто, ибо JIT компилятор обязан быть быстрым по-определению и ему доступны только самые быстрые и очевидные оптимизации.
Машина, на которой производились запуски: MacBook Pro
Я пару лет назад подобный простой вычислительный питоновский (python3) тестик (с арифметическими действиями) прогонял на всех своих компах, включая imac. Так вот в макос у меня этот тестик почему то выполнялся в два раза дольше, чем на том же маке в линуксе, который работал в Parallels. Я обратил внимание на такую большую разницу, но в причинах не разбирался, т.к. в итоге вообще отказался от макос (не моё).
Думаю, тестирование лучше было бы проводить на серверном железе с каким-нибудь линуксом (например, дебиан) — там, где собственно эти языки по большей части актуальны (особенно PHP).
О, сравнение лошадей со стрекозами!
Автор, а у вас Undefined Behavior в коде на C++ :)
Я подозреваю, что тут что-то неладное: for (int i ; i < N; i++) {
;-)
0.011027 sec
Т.е. быстрее мака автора в 235 раз. Неплохо =).
4.88964 sec
с -O2:
1.93054 sec
с -O3:
0.006242 sec
Я это к тому, что настолько сильно влияет оптимизация и нет смысла сравнивать без оптимизации. Тем более с другими языками.
Самая сильная оптимизация — не делать ненужную работу.
А почему шарп и джаву не добавили а тест? Было бы интересно.
all(not num % div for div in range(3, int(num**0.5)+1, 2))
34 сек — с for
5.2 сек — с all()
3*3*3*5*5*7*11*13*17*19*23*29*31*37*41*43*47 == 0xbfffdd7cc41833d5
— это максимальное число, умещающееся в uint64 и делящееся без остатка на _все_ нечетные от 3 до 47 включительно. Таким образом твой ошибочный код прервётся, выполнив (49-3)//2 == 23 взятий остатков — это максимум, любое другое uint64 даст меньше.
Ближайшее большое простое — 0xbfffdd7cc4183473, _правильный_ код проверки его на простоту потребует int(0xbfffdd7cc4183473**.5)//2-1 == 1859772841 взятий остатков, т.е. в 80_859_688 раз больше.
Делаем выводы, расходимся )
Если надо что-то считать, то считать надо с максимальным использованием ресурсов.
Сможете переделать тест на загрузку 4+ ядер на какой-либо чуть более серверной ОС (Debian, CentOS/RHEL, Ubuntu наконец)?
Будет как минимум два сценария с существенным различием в цифрах:
1. Заранее распределяем «какой поток какой набор цифр считает» и запускаем их одновременно — соотношение будет максимально близко к вашим результатам
2. Цифры для проверки приходят снаружи приложения (или из отдельного потока) и динамические распределяются между worker потоками — тут начнёт играть роль умение языка координироваться между потоками.
То есть прогнать в них пустой цикл с такой же итерацией
А собственно вычисление — дело конкретных библиотек. И тогда вопрос стоит уже об эффективности той или иной библиотеки на конкретной операции
Ну и как выше отметили — код
for (int i ; i < N; i++)
ставит результат под сомнение )))
C++: div <= sqrt(double(num))
Go: div <= int(math.Sqrt(float64(num)))
Node.js: div <= Math.sqrt(num)
PHP: $i <= sqrt($num) + 1
Python: int(math.sqrt(num)) + 1
(в Питоне правая граница не включается в интервал)Глядишь, на N = 10_000_000 что-то по времени и набежит дополнительно для РНР
using BenchmarkTools
function isPrime(num::Int64)::Bool
(num == 2) && return true;
(num <= 1 || num % 2 == 0) && return false;
@simd for i = 3:2:round(Int, sqrt(num))
(num % i == 0) && return false;
end
return true;
end
function measure()
for i = 1:10_000_000
isPrime(i);
end
end
@benchmark measure()
versioninfo()
Куда же без Julia… Код выше выдаст оценку времени в REPL.
Впрочем, есть оптимизированный алгоритм расчёта простых чисел, который почти в 3 быстрее. https://github.com/JuliaMath/Primes.jl/blob/master/src/Primes.jl#L144
Автор статьи, сходи, пожалуйста, по ссылке, нажми кнопку плэй и напиши время которое получилось.
А что с расходом памяти?
Я подозреваю, что тут что-то неладное: for (int i; i < N; i++) {
В фортране такой цикл не сделаешь даже с помощью Do While: еще при компиляции будет неинициализированная переменная. Ну да ладно. Написал i=0… а тест все равно не работает. Функция не вызывается, т.к. ее результат игнорится. Короче, пришлось добавить в цикл счетчик простых чисел, который потом идет на печать. Только после этого фортран заработал. Что меня удивило, он оказался даже чуть медленнее, чем С++: 9.0 сек против 7.1. Правда, тут может быть эффект ключей компиляции: в фортране у меня стоит отладочный режим, а в Си я все делал по умолчанию, так как язык не знаю совсем и сборку настраивать не умею :-((
Но в реальной жизни мне обычно нужны чуть более сложные вычисления, а не только целочисленные. Поэтому я добавил в цикл небольшой массивчик и парочку синусов, чтобы исключить всякий шанс на такую оптимизацию, при которой эти строки просто игнорятся (верхнюю границу цикла пришлось поправить, естественно):
// test_c.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#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 _tmain(int argc, _TCHAR* argv[])
{
int N = 50000; //000
int counter = 0;
float array1[50000];
for (int ii = 0; ii < N; ii++) {
array1[ii]=(float)ii;
}
clock_t start, end;
start = clock();
float summa=0;
for (int i = 0; i < N; i++) {
if (isPrime(i)) {
counter++;
array1[i]=array1[i]+(float)i; // Противо-оптимизатор
summa=0;
for (int ii = 0; ii < N; ii++) {
summa +=sin(array1[ii])/ii; // Сумма "элементов" массива
}
for (int ii = 0; ii < N; ii++) {
array1[ii]=array1[ii]+sin(summa)/ii; // Еще чуть-чуть синусов
}
}
}
end = clock();
cout << (end - start) / ((double) CLOCKS_PER_SEC);
cout << " sec \n";
cout << counter;
cout << " primes \n";
cout << summa;
cout << " \n";
return 0;
}
А это фортран. Да, по сравнению с С он достаточно многословный (особенно описание переменных), зато код читать много проще и ошибки типа Undefined Behavior заведомо невозможны, если специально не искать для этого особые трюки. Я уж не говорю про автоматический контроль выхода индекса за границу массива и прочие мелкие радости, которые в моем фортране по умолчанию включены, так как на скорость не влияют особо...:
logical function isPrime(num)
integer*8 num, i8, sqrt_num
c
if (num <= 0) stop (-13) ! Проверка входа
isPrime=.true.; if (num <= 3) return ! Многие верят, что число 1 - простое
isPrime=.false.;
c
if (mod(num,2) == 0) return ! все четные числа >2 очень непростые...
c
sqrt_num = int(sqrt(real(num))) ! Этот блок кода не проверял.
do i8=3,sqrt_num,2 ! просто доверился автору теста,
if (mod(num,i8) == 0) return ! что если остаток от деления == 0,
end do ! то число не простое.
c
isPrime=.true.; return
end
c
c.....ПРО ГРАММА:
integer*8, parameter :: test_limit=50000 ! сначала было 10000000
logical isPrime
integer*8 prime_counter, num, ii
integer i_hour1, i_min1, i_sec1, i_iisec1
integer i_hour2, i_min2, i_sec2, i_iisec2
real*8 :: array(test_limit)=[(i,i=1,test_limit)], tmp_array(test_limit)
real*8 :: time, summa
c
c В цикле будем считать простые числа, иначе ОН оптимизирует цикл:
call gettim(i_hour1,i_min1,i_sec1,i_iisec1) ! Да, у нас вот такой геморрой
prime_counter=0
do num=1,test_limit
if (isPrime(num)) then
summa=0. ! Никуда ты у меня не денешься,
prime_counter=prime_counter+1 ! будешь по-честному вызывать isPrime ;-)
array(num)=array(num)+num ! И вот это тоже будешь считать
forall(ii=1:test_limit) tmp_array(ii)=sin(array(ii))/ii
summa=sum(tmp_array) ! Массив меняется на каждом шаге!
array=array+sin(summa)/num ! И контрольный выстрел. Теперь
end if ! ты у меня хрен что оптимизируешь!!!
end do
c
call gettim(i_hour2, i_min2, i_sec2, i_iisec2)
time=(i_iisec2-i_iisec1)/100.+(i_sec2-i_sec1)+(i_min2-i_min1)/60.+(i_hour2-i_hour1)/3600.
c
c Результат пошлем на консоль:
write(6,'(i12,2(a,f5.2))') prime_counter,' штук; ',time,' сек; ',summa
end
Вот выдача сишной программы:
68.293 sec
5133 primes
-1.#IND
А вот это — фортрановской: 5134 штук; 4.61 сек; 0.08
Разница во времени счета, хм… не очень понятная. Ведь согласно древним учениям, компилятор Си автоматически оптимизирует циклы?! Или нет?!? К сожалению, я не сумел найти правильные слова (т.е. библиотечные функции), чтобы как-то свернуть эти Си-шные циклы. Видимо, до уровня «зеленого чайника» в Си я пока не дотягиваю :-((
А без этих «правильных слов» фортран получается на порядок быстрее :-(((
Что же в сухом остатке? Если я новичок в языке, то выходит, что счетные задачи надо писать именно на фортране. Мало того, что сам язык много проще, так еще и библиотеки учить не нужно. Тупо взял простейшие операторы и получил результат. За который в других языках еще нужно бороться… В реальной науке задачи обычно все-таки больше похожи на мой тест, а не на первоначальный…
isPrime=.true.; if (num <= 3) return ! Многие верят, что число 1 - простое
На Хабре я новичок, но вполне понимаю, что оценивая мое сообщение минусами, вы хотите как-то поправить мое поведение. И я совершенно не против исправиться, чтобы лучше соответствовать стилю сообщества. Но к сожалению, у меня не хватает интуиции, чтобы понять: что именно я сделал неправильно? Я ведь не могу исправить ошибку, если не знаю — в чем она заключается? Может ли кто-нибудь подсказать, хотя бы в ЛС?
Да, я не включал оптимизации в Си — но я честно написал, что просто не знаю, как это сделать. Все-таки, ради этого теста я первый раз в жизни создал проект на C в Студии. До этого вообще на С не писал — даже «Hello, world!» не было. Поэтому я даже не сомневался, что все необходимые оптимизации по умолчанию уже включены. Во всяком случае, в фортране после его установки (интеграции в Студию) было именно так. Посоветуйте, что именно надо включить в настройках проекта в VS 2008 — я выложу результаты теста с оптимизацией.
Или надо было указать версию ОС и т.д.? У меня ничего особенного, обычная Win 7-64. Подробности про процессор я не стал писать, т.к. у меня в другом окне идет счет на несколько суток, и на тестовые задачи выделяется только часть ресурсов системы. Поэтому для надежности я запускал каждый тест по несколько раз. Но т.к. основной процесс дает очень стабильную загрузку, то результаты отличаются в пределах 10%.
Компилятор С — встроенный из VS2008. Компилятор фортрана — Intel Visual Fortran XE 13. Да, он считается неплохим компилятором, но ему уже десять лет. Вряд ли другие хорошие современные компиляторы фортрана дадут разницу на порядок.
Что еще я не указал?
Я надеюсь, что здесь не минусуют просто за факты, которые кому-то не нравятся? Да, в одной конкретной ситуации фортран оказался на порядок быстрее Си. Но я-то тут при чем? Я всего лишь собрал и запустил четыре теста, два из которых близки к моим реальным задачам. Потом честно выложил исходные коды и их результаты.
Или здесь считается неприемлемым ироничный тон комментариев, и что я чуть-чуть отошел от ТЗ и учел 1, как простое? Ну да, я склонен следовать Олегу Янковскому, который советовал улыбаться, ибо все глупости на Земле делаются с серьезным выражением лица (Тот самый Мюнхгаузен).
Мы же не роботы…
Я надеюсь, что здесь не минусуют просто за факты, которые кому-то не нравятся?Минусуют, даже за то что просто не понимают, о чем прочитали.
Но все же стоит просто потыкать и посмотреть опции компиляции. Он них не убудет. Да и в 2020 студия 2008 оказывается архаичной (фортран то вне времени) =)
фортран то вне времени
Как раз компилятор фортрана от интела не плохо умеет векторизировать и параллелить наивный код. Поэтому чем новее компилятор тем лучше он может использовать фичи нового железа. Но для программы из топика это умение не пригодится.
Какие еще настройки посоветуете проверить, кроме вкладки «Оптимизация»?
Я смотрел еще вкладку «Code Generation», но там список доступных настроек в проектах Си и Фортрана разный. Архитектура SSE2 в обоих случаях включена.
Никому не интересно какие результаты дает студия 2008…
Вы вместо теста простых чисел тестируете синусы. В таком случае лучше уже написать один простой тест для синусов вместо непонятной каши. Кроме того есть компилятор Си от Intel где реализация синусов будет скорее всего идентичной вашему одноименному компилятору Фортрана.
Никому не интересно какие результаты дает студия 2008…Спасибо за прямоту, — теперь дошло, наконец ;-)
И про тест Вы правы, конечно. На фортране я так не пишу обычно. Меня сбила с толку попытка думать на Си, не зная про него почти ничего. Ок, больше не буду ;-)
1) Сравнение оптимизированной сборки на фортране с неоптимизированной на C. Вот Вы серьезно не в состоянии поискать в сети как включить оптимизацию в проекте в вижуалке? Или Вы думаете, что это сравнимо по сложности с изучением языка и является серьезным поводом для новичка забросить изучение C и учить фортран?
2) Запуск бенчмарков проводился одновременно с выполнением каких-то вычислений, которые съедали большую часть аппаратных ресурсов — разница от запуска к запуску 10%. О каких объективных результатах вообще может идти речь?
3) Вы изменили тест чуть менее чем полностью. Добавили бесполезное жонглирование синусами, под предлогом того, что C выкидывал вызов isPrime при оптимизации, хотя для этого достаточно просто дописать перед ним
bool volatile result = isPrime(i);
что, опять же, не является какой-то секретной информацией, достаточно использовать интернет-поисковик. Такие действия как-бы намекают, что изначальный результат сравнения Вам не понравился, и Вы решили чуть подправить реализации, что бы он был поприятнее.
4) Ваши программы неэквивалентны. Причем не то что бы Вы чего-то недоглядели — нет, Вы добавили в комментарий неодинаковых выводы программ, и это Вас абсолютно не смутило.
Суммируя, Вы сравниваете две неэквивалентные программы с разными настройками оптимизации, написанные по в процессе подправленном ТЗ, и запускаемые в среде с дефицитом вычислительных ресурсов. И после этого в «ироничном тоне» делаете выводы о производительности языков в целом, которые, оказывается, не просто Ваши субъективные догадки, а прямо таки «факты, которые кому-то не нравятся». Думаю, Ваш комментарий минусят потому, что он — тотальная профанация.
Вот Вы серьезно не в состоянии поискать в сети как включить оптимизацию в проекте в вижуалке?Я же говорю, что первый раз в жизни собираю программу на Си. Поэтому для меня эти ключи — темный лес. Все, что я умею в VS — это выключать оптимизацию при отладке фортран-программ (иначе дебагер не показывает значения переменных). Больше я в настройках проекта ничего не трогаю. Как говорится, работает — не трогай. Но скорость фортран-вычислений при этом меняется максимум на десятки процентов. Никак не в разы. Во-вторых, при «чистом» тесте (исходный вариант) скорость у фортрана и Си получилась примерно одинаковая. Поэтому я предположил, что на скорость таких простых расчетов оптимизация не влияет особо. В фортране именно так все. Именно поэтому возникла версия, что нужна не оптимизация, а вызов каких-то библиотечных функций для работы с массивами (которые в фортране = простой оператор).
И еще насчет свойств проекта: конечно, я туда лазил. Но там очень много настроек. Самая очевидная — это «включить оптимизацию» в верхней строке. Но при ее включении проект перестает компилироваться. Сейчас еще раз посмотрел текст сообщения:
"cl : Command line error D8016 : '/Ox' and '/RTC1' command-line options are incompatible
Более глубоко я просто не рискнул залезать. Но раз это ключевой вопрос, то попробую сейчас и другие варианты этой опции. Если есть еще советы — тоже с удовольствием сделаю. 2) Запуск бенчмарков проводился одновременно с выполнением каких-то вычислений, которые съедали большую часть аппаратных ресурсов — разница от запуска к запуску 10%. О каких объективных результатах вообще может идти речь?Ну вообще-то, разброс на 5% в Винде будет всегда, если в системе еще хоть что-то работает. Я же не промышленный тестировщик. Поэтому почтовая программа, файловый менеджер и т.п. активны всегда. Сейчас дополнительно висит «тяжелый» процесс, а разброс при десятке запусков не превышает 10%. Мне показалось, что это достаточная стабильность, чтобы результат представлял интерес.
3) Вы изменили тест чуть менее чем полностью.Наверно, я не очень точно написал в своем сообщении. Сначала я запустил строго тот тест, который предложил топикастер. Программа на фортране была ПОЧТИ идентичной, с той разницей, что туда пришлось добавить счетчик простых чисел. Как я написал, в этом варианте программа на Си работала чуть быстрее.
Добавили бесполезное жонглирование синусами, под предлогом того, что C выкидывал вызов isPrime при оптимизации, хотя для этого достаточно просто дописать перед ним bool volatile result = isPrime(i);Вообще-то я попытался написать, что все было не совсем так. Синусы я добавил вовсе не по этой причине, а потому, что хотел тестировать операции с плавающей точкой и массивами, которых в изначальном тесте не было. Но которые все время нужны мне на практике. Т.е. это фактически другой тест, дополнительный к первому. Наверно надо было это более четко сказать.
4) Ваши программы неэквивалентны. Причем не то что бы Вы чего-то недоглядели — нет, Вы добавили в комментарий неодинаковых выводы программ, и это Вас абсолютно не смутило.Надеюсь, Вы не про число «1» спрашиваете? Это одна проверка при вызове функции — она на скорость не может заметно влиять.
Если же по существу (про синусы в цикле) — то я просто не знаю Си настолько хорошо, чтобы сделать полностью эквивалентную программу. Я надеялся, что кто-то из присутствующих не поленится в этот код заглянуть и подскажет, что именно там надо поправить, чтобы программа стала точно эквивалентной фортрану. Но я старался, чтобы количество итераций цикла и объем вычислений внутри него было одинаковыми.
Суммируя, Вы сравниваете две неэквивалентные программы с разными настройками оптимизации, написанные по в процессе подправленном ТЗ, и запускаемые в среде с дефицитом вычислительных ресурсов.Ну вот да. Если не считать того, что сначала я честно запустил эквивалентные программы. И только потом сделал дополнительный тест с измененным ТЗ.
Хотя, насчет дефицита ресурсов я бы поспорил. Дело даже не в том, что у меня не было выбора. Но в реальной жизни реальные программы почти всегда работают в условиях дефицита ресурсов. Поэтому тестирование на загруженном компе, наверно, ТОЖЕ имеет право на существование? Понятно, что разброс результатов при этом растет, но я занимаюсь статистической обработкой сигналов и прекрасно понимаю, что этот разброс надо контролировать и как это сделать. Если несколько запусков дают разницу в результатах менее 10%, то для меня это приемлемая надежность. Особенно когда сравниваемые величины отличаются на порядок и ясно, что причину различий надо искать в чем-то другом.
Итого:
1) Спасибо за разъяснения! Постараюсь учитывать в будущем.
2) Если есть советы по настройке оптимизации (режим «для чайника»=on)- немедленно сделаю и обновлю результат.
3) Если есть поправки к си-коду «теста с синусами» — тоже немедленно сделаю. Если вдруг для кого-то синтаксис фортрана не совсем очевиден, могу пояснить, что оператор
forall(ii=1:test_limit)
— это фактически цикл, только чуть более короткая запись. А оператор array=array+sin(summa)/num
прибавляет некоторое значение к каждому элементу массива. Кстати, возможно, что именно здесь была одна из проблем, если при Си-вычислениях синус считался каждый раз заново (хотя он всегда одинаковый).Действительно, у меня есть ошибка в коде на Си: вместо
array1[ii]=array1[ii]+sin(summa)/ii;
должно быть array1[ii]=array1[ii]+sin(summa)/i;
Впрочем, на скорость это не повлияло особо. Вместо 68с стало 61с. А нужно, чтобы было 4-5с. Что еще может быть?Также проверил все доступные ключи оптимизации: не компилируется все равно. При оптимизации на скорость ошибка:
cl : Command line error D8016 : '/O2' and '/RTC1' command-line options are incompatible
То же самое и при оптимизации на размер кода: cl : Command line error D8016 : '/O1' and '/RTC1' command-line options are incompatible
В custom-настройки, уж извините, без мудрого советчика не рискну сам залазить :-((У меня плоховато с английским — прочитать страницу по ссылке я умею, а вот гуглить на международном получается плохо. Да, действительно, после переключения в Release все ключи оптимизации работают! Можно выбрать и полную оптимизацию, и оптимизацию на скорость! Впрочем, при любом выборе си-шная программа все равно выполняется за 19-20 сек, а фортрановская — за 5.4-5.5с (фоновый процесс изменился, поэтому время счета сегодня немного другое).
Также для полноты выкладываю заново свои тесты с массивами и плавающей точкой (внимание, это НЕ оригинальный тест топикастера!):
// test_c.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#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 _tmain(int argc, _TCHAR* argv[])
{
int N = 50000; //50000 000
int counter = 0;
float array1[50000];
for (int ii = 0; ii < N; ii++) {
array1[ii]=(float)ii;
}
clock_t start, end;
start = clock();
float summa=0;
for (int i = 0; i < N; i++) {
if (isPrime(i)) {
counter++;
array1[i]=array1[i]+(float)i; // Противо-оптимизатор
summa=0;
for (int ii = 0; ii < N; ii++) {
summa +=sin(array1[ii])/ii; // Сумма "эл-тов" массива
}
for (int ii = 0; ii < N; ii++) {
array1[ii]=array1[ii]+sin(summa)/i;
}
}
}
end = clock();
cout << (end - start) / ((double) CLOCKS_PER_SEC);
cout << " sec \n";
cout << counter;
cout << " primes \n";
cout << summa;
cout << " \n";
return 0;
}
Фортран:
logical function isPrime(num)
integer*8 num, i8, sqrt_num
c
isPrime=.true.; if (num == 2) return ! Все как в Си
isPrime=.false.; if ((num <= 1).or.(mod(num,2) == 0)) return
c
sqrt_num = int(sqrt(real(num))) ! Этот блок кода не проверял.
do i8=3,sqrt_num,2 ! просто доверился автору теста,
if (mod(num,i8) == 0) return ! что если остаток от деления == 0,
end do ! то число не простое.
c
isPrime=.true.
end
c
c.....ПРОГРАММА:
integer*8, parameter :: test_limit=50 000
logical isPrime
integer*8 prime_counter, num, ii
integer i_hour1, i_min1, i_sec1, i_iisec1
integer i_hour2, i_min2, i_sec2, i_iisec2
real*8 :: array(test_limit)=[(i,i=1,test_limit)], tmp_array(test_limit)
real*8 :: time, summa
c
c В цикле будем считать простые числа, иначе ОН оптимизирует цикл:
call gettim(i_hour1,i_min1,i_sec1,i_iisec1) ! Да, у нас вот такой геморрой
prime_counter=0
do num=1,test_limit
if (isPrime(num)) then
summa=0. ! Никуда ты у меня не денешься,
prime_counter=prime_counter+1 ! будешь по-честному вызывать isPrime ;-)
array(num)=array(num)+num ! И вот это тоже будешь считать
forall(ii=1:test_limit) tmp_array(ii)=sin(array(ii))/ii
summa=sum(tmp_array) ! Массив меняется на каждом шаге!
array=array+sin(summa)/num ! И контрольный выстрел. Теперь
end if ! ты у меня хрен что оптимизируешь!
end do
c
call gettim(i_hour2, i_min2, i_sec2, i_iisec2)
time=(i_iisec2-i_iisec1)/100.+(i_sec2-i_sec1)+(i_min2-i_min1)*60.+(i_hour2-i_hour1)*3600.
c
c Результат пошлем на консоль:
write(6,'(i12,2(a,f5.2))') prime_counter,' штук; ',time,' сек; ',summa
end
Ура, я научился прятать часть сообщения в спойлер! ;-)))
Правда, строки кода при этом стали короче (меньше символов в строке)
и читать программу не очень удобно. С этим пока не разобрался :-((
К тому же, в программе на C Вы используете стандартную функцию sin. Во-первых, эта функция принимает на вход число типа double, а данные в нее передаются в типе float — то есть, в программе на C часто выполняются преобразования float <-> double, которые довольно недешевые. Во-вторых, эта функция сама по себе может быть реализована как угодно, возможно тут лучше воспользоваться специализированной библиотекой.
И вообще, Ваш пример написан довольно сумбурно, например, сделать счетчики циклов double-переменными напрашивается само собой.
Так вот, я правильно понял, что вместо float мне надо везде написать double? Это я сделал. Но время счета на Си не изменилось почти. Теперь 18-19с вместо 19-20. Так что лишнее преобразование типов, наверно влияло, но главная проблема не в нем.
Похоже, дело именно в вычислении синуса? Раз уж Вы взяли надо мной шефство, можете еще подсказать: как именно вызывать специализированную библиотечную функцию вместо стандартного sin? Нужно добавить какой-то #include? И/или изменить имя функции? Или что?
суть — будет вызываться команда сопроцессора, без обработки крайних случаев по IEEE
опция -ffast-math для gcc. для VC не скажу — поискать быстрая математика.Спасибо!!! Это именно то, что надо!!! Теперь Си-шная процедура (с исправленным делением на 0) работает 5с —
Я предполагал, что там какая-то ошибка в операторе печати, но надеялся, что кто-нибудь подскажет.Дебаггером воспользоваться религия не позволяет? Интервалы циклов проверьте.
Но я же говорю, это моя первая программа на Си. Хотя программой, конечно, это сложно назвать ;-((А откуда тогда жалобы на минусы к комментариям? Какие программы — такие и оценки.
Похоже, дело именно в вычислении синуса?Не знаю в чем дело, у меня программа на C выполняется менее полутора секунд (Ryzen 3600). Возможно Вы и платформу не переключили на x64 — на x86 у меня чуть больше 5с.
А откуда тогда жалобы на минусы к комментариям? Какие программы — такие и оценки. /blockquote>
Честно говоря, я не жаловался, а только пыталсяпонять причинуМинус — это ведь совершенно нормальный способ вежливо намекнуть человеку, правда? Поэтому никаких обид. Вопросы были связаны с тем, что я не мог эти намеки понять, хотя честно пытался. Теперь понял ;-) Благо, мне в соседней ветке дополнительно пояснили ;-)
Насчет платформы — тоже спасибо за совет, попробую! Правда, пока не смог найти, где это. Но 5с уже и так получилось: у меня нужная опция называетсяFloating Point Model = Fast
Еще в самом первом листинге видно, что программа на Си вместо числа выдает какой-то странный код.
Первый результат в гугле
-1.#IND (a synonym for NaN)
Смотрим на код. Вы делите на ноль там где делится на ii
в цикле.
Ну и тупой же я… :-(((
Увы, нет привычки пользоваться гуглом: по фортрану мне хватает книжек и локальной справки, а если там чего-то нет, то и в гугле обычно ничего не находится… Нужно было в свое время английский учить :0(
ох это же фортран 77… Всё таки не вымерли ещё мамонты на хабре!
Но фортран, скорее, все же 2003. В фортране-77 многих использованных операторов еще не было — таких, как forall, или инициализация переменных/массивов при объявлении. Да и арифметические массивные операции тоже, кажется, появились чуть позже…
Да я шучу, просто врезались в глаза строчки начинающиеся на "c ".
А вообще, про историю развития фортрана неплохо написано вот тут. Кто интересуется археологией — очень рекомендую взглянуть ;-)
Добавил импорт prange, и флаг parallel=True.
from numba import njit, prange
......
@njit(parallel=True)
def do(n):
for i in prange(n):
is_prime(i)
У меня на i3-8100 получается 1.416 с.
Понятно, что сама функция не ускорилась, просто пример как легко c использованием numba распараллелить задачу. Наверное, подобное можно и с другими примерами провернуть, но наверное они усложнились больше чем код с numba.
div=3
while div<int(math.sqrt(num)) + 1:
if not num % div:
return False
div+=2
Не так красиво, но ускорение того стоит.
А оно точно выдает такой же результат, как обычная версия? И мы, вроде, договорились без параллельности :)
Конечно, сразу сделал проверку. Самая простая — посчитать количество найденных простых чисел.
Оно оказалось таким же — 664579 всего. Но при этом вариант с циклом while замедлился с 0.2 до 2.0 с. Однако это все равно быстрее оригинального кода с циклом, который выполняется с 3.4-3.5 с. Прирост не такой фантастической, но все же почти в 2 раза быстрее оригинального кода с циклом for. Видимо, фантастический прирост на порядки наблюдался из-за того что оптимизатор как то слишком упрощал код, так как результат функции не использовался. Но как только я начал подсчет чисел, он замедлился в 10 раз, так как результат функции начал использоваться.
Под катом код — можете сами проверить что не используется распаралеливание:
import os
import math
from time import perf_counter
import numpy as np
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 is_prime_fast(num):
if num == 2:
return True
if num == 1 or not num % 2:
return False
div=3
while div<int(math.sqrt(num)) + 1:
if not num % div:
return False
div+=2
return True
@njit()
def do(n):
count=0
for i in range(n):
count+=int(is_prime(i))
return count
@njit('i8(i8)')
def do_fast(n):
count=0
for i in range(n):
count+=int(is_prime_fast(i))
return count
if __name__ == '__main__':
N = 10_000_000
st = perf_counter()
print(do(N))
end = perf_counter()
print(end - st)
st = perf_counter()
res = do_fast(N)
end = perf_counter()
print(res)
print(end - st)
Долгое время пытался ускорить свой кривой код. Где-то получилось использовать страйды и перемножение матриц вместо простых итераций по пикселям, но код получается «write only». С горя уже начал думать переписать узкие места на C++.
Теперь добавил одну строчку jit(nopython=True, cache=True) и все просто летает!
time without JIT: 17.295716762542725
time with JIT: 1.3494205474853516
Срочно переписываю все на Нумбу. Хотя и переписывать-то почти ничего не надо, просто расставить декораторы.
Получилось вот так:
import kotlin.math.sqrt
fun main(args: Array<String>) {
val startTime = System.currentTimeMillis()
run(10000000)
val totalTime = (System.currentTimeMillis() - startTime) / 1000
/**
* 10000000 - Время выполнения: 5 сек.
* 20000000 - Время выполнения: 15 сек.
* 30000000 - Время выполнения: 28 сек.
*/
println("Время выполнения: $totalTime сек.")
}
fun isPrime(number: Int): Boolean {
if (number == 2) {
return true;
}
if (number == 1 || number % 2 == 0) {
return false;
}
var to = (sqrt(number.toDouble()) + 1).toInt();
var i: Int = 3;
while (i <= to) {
i += 2
if (number % i == 0) {
return false;
}
}
return true;
}
fun run(number: Int) {
var i: Int = 0
while (i <= number) {
i++
isPrime(i)
}
}
В примере на C++ у вас используется 32 битный int, а в Go int не является типом с фиксированной размерностью, а по сему на 64-х битном CPU он соответствует int64 и как следствие результат «хуже»!
Ниже корректный пример который выполняется на моем i7-8565U за 1.691030168s, что практически эквивалентно C++.
package main
import (
"fmt"
"math"
"time"
)
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
}
func main() {
st := time.Now()
for i := int32(0); i < 10_000_000; i++ {
isPrime(i)
}
fmt.Printf("%+v\n", time.Since(st))
}
Сравним C++, JS, Python, Python + numba, PHP7, PHP8, и Golang на примере расчёта “Простое Число”