Я читал статью how slow Haskell it is in playing with Collatz conjecture, в которой говорится, что если вы продолжаете умножать три и плюс одно на нечетное число или делить четное на два, вы в конечном итоге получите один. Например, 3 -> 10 -> 5 -> 16 -> 8 -> 4 -> 2 -> 1.Haskell: Почему Int выполняет хуже, чем Word64, и почему моя программа намного медленнее, чем C?
Программа, приведенная в этой статье, предназначена для вычисления самой длинной последовательности Collatz в заданном диапазоне. Версия C:
#include <stdio.h>
int main(int argc, char **argv) {
int max_a0 = atoi(argv[1]);
int longest = 0, max_len = 0;
int a0, len;
unsigned long a;
for (a0 = 1; a0 <= max_a0; a0++) {
a = a0;
len = 0;
while (a != 1) {
len++;
a = ((a%2==0)? a : 3*a+1)/2;
}
if (len > max_len) {
max_len = len;
longest = a0;
}
}
printf("(%d, %d)\n", max_len, longest);
return 0;
}
Компиляция с Clang O2, она работает на моем компьютере в течение 0,2 с.
Версия Haskell, приведенная в этой статье, генерирует всю последовательность как список явно, а затем вычисляет длину промежуточного списка. Он в 10 раз медленнее, чем версия C. Однако, поскольку автор использовал LLVM в качестве бэкэнд, который я не установил, я не смог воспроизвести это. Используя GHC 7.8 и бэкэнд по умолчанию, он запускает 10 секунд на моем Mac, что на 50 раз медленнее, чем версия C.
Затем я пишу версию с помощью хвостовую рекурсию и не создает промежуточный список:
collatzNext :: Int -> Int
collatzNext a
| even a = a `div` 2
| otherwise = (3 * a + 1) `div` 2
collatzLen :: Int -> Int
collatzLen n = collatzIter n 0
where
collatzIter 1 len = len
collatzIter n len = collatzIter (collatzNext n) (len + 1)
main = do
print $ maximum $ [collatzLen x | x <- [1..1000000]]
Составитель с GHC 7.8 и O2, он работает в течение 2 секунд, в 10 раз медленнее, чем версия C.
Интересно, когда я изменил Int
в аннотации типа до Word
, он потратил только 1, 2 раза быстрее!
Я пробовал BangPatterns для явной строгой оценки, но значительного прироста производительности я не заметил - я полагаю, что строгий анализ GHC достаточно умен, чтобы справиться с таким простым сценарием.
Мои вопросы:
- Почему
Word
версия так быстрее по сравнению сInt
один? - Почему эта программа Haskell настолько медленная, по сравнению с тем, что в C?
Просто комментарий, это не так, чтобы сравнивать любое время выполнения менее 1 секунды. Может быть, загрузка библиотек требует времени? Кто знает. Попробуйте синхронизировать определенные части кода и использовать большие проблемы или вычислить их несколько раз. –
Вы пробовали профилировать? –
@ VladimirF Поскольку я использовал оптимизацию -O2, я думаю, что даже 0,1 секунды означает много. @ N.m. Мне не хватает опыта в профилировании программ Haskell. Я немного погуглил и проверил руководство GHC. Простой профилирование времени показывает, что 73.3% времени тратится на 'collatzLen.collatzIter', а 24.3% в' collatzNext'. Прошу прощения, но это единственное, что я могу получить с моим нынешним пониманием профилирования Haskell ... –