2017-02-16 2 views
2

Я изучаю Clojure и лучше разбираюсь в своем прогрессе. Я решил начать решение проблем Project Euler на языке (некоторые из которых я уже решил в C++ и Python). Проблема 1 выглядит следующим образом:Как проверить (сравнить) производительность Python против Clojure?

Если мы все натуральные числа меньше 10, кратные 3 или 5 , мы получаем 3, 5, 6 и 9. Сумма этих мультипликаторов составляет 23.

Найти сумму всех чисел, кратных 3 или 5 ниже 1000.

Вот мой первый пробег на решение Clojure:

(defn main1 
    ([n] 
    (reduce + 
     (filter 
     #(or 
      (= 0 (mod % 3)) 
      (= 0 (mod % 5))) 
     (range n))))) 

Я тогда lõoke d в моей версии Python кода, который выглядит следующим образом:

import sys 
import operator 
from functools import reduce 


def main(n=1000): 
    """ 
    returns solution up to but excluding n 
    """ 
    genexp = (num for num in range(1, n) if ((num % 3 == 0) or (num % 5 == 0))) 
    total = reduce(operator.add, genexp) 
    return total 


if __name__ == "__main__": 
    if len(sys.argv) > 1: 
     print(main(int(sys.argv[1]))) 
    else: 
     print(main()) 

Игнорирование дополнительных CLI-арг вещи я добавил в версии Python, единственным основным отличием является то, что я использовал filter в Clojure вместо генератор. Я предполагаю, что я мог бы использоваться в Python с filter тоже, но только ради аргумента, я сделал свой Clojure код более похож на код Python:

(defn main2 
    ([n] 
    (reduce + (for [i (range n) 
        :let [div3 (= 0 (mod i 3)) 
         div5 (= 0 (mod i 5))] 
        :when (or div3 div5)] 
       i)))) 

Это заставило меня задуматься - как я могу это тест функции для их сравнения? Для Python, это достаточно легко:

$ time python python/p0001.py 10000000 
23333331666668 

real 0m2.693s 
user 0m2.660s 
sys 0m0.018s 

$ time python python/p0001.py 100000000 
2333333316666668 

real 0m26.494s 
user 0m26.381s 
sys 0m0.050s 

Он идет до n=100,000,000 в разумные сроки (до 30 секунд). Как я могу запустить аналогичный тест для моих функций Clojure? Я предполагаю, что мне придется сначала скомпилировать код Clojure, прежде чем запускать его. Было бы это даже справедливым сравнением, учитывая, что код Python здесь не скомпилирован JIT?

В другом примечании, насколько идиоматичным является мой код Clojure (обе версии)? И какие хорошие рекомендации по стилю кода? Есть ли в стиле pep8 стиль-путеводитель? Или даже что-то похожее на версию Clojure pep20: «Zen of Python»?

+3

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

+1

относительно вашего «как идиоматично мой код клоюра»? (1), если у вас нет функции многозначности, не часто встречается '(' вокруг вектора аргументов '([n] ...' (2) '(= 0 ...' часто выражается как ' (нуль? ... '(3) ваш отступ в основном правилен, но вы ищете руководство по стилю clojure, и вы получите хорошую информацию. (4) При выражении чисел со многими нулями рассмотрите использование научной нотации.' 100 000 000' в clojure тоже «1e8» .Все хорошая работа! Я бы предпочел рекомендацию по критерию. Я использую его почти ежедневно. Версия 100 000 000 показывает время 1,84s здесь FYI. – Josh

+2

FYI: Это работает примерно в 1.1s для меня: '(пусть [п 100000000] (трансдукции (фильтр # (или (ноль (бэр% 3)) (ноль (бэр% 5)))) + (диапазон п?))) 'он использует преобразователи, чтобы избежать создания какой-либо коллекции. – ClojureMostly

ответ

6

Встроенная функция time хороша для первого разреза:

(time (main1 100000000)) => 2333333316666668 
"Elapsed time: 15824.041487 msecs" 

Вы также можете использовать the excellent criterium library

(ns xyz.core 
    (:require [criterium.core :as cc])) 

(cc/quick-bench (main1 999)) 

Evaluation count : 3894 in 6 samples of 649 calls. 
      Execution time mean : 154.680082 µs 
    Execution time std-deviation : 750.591607 ns 
    Execution time lower quantile : 153.982498 µs (2.5%) 
    Execution time upper quantile : 155.870826 µs (97.5%) 
        Overhead used : 7.898724 ns 

Found 1 outliers in 6 samples (16.6667 %) 
    low-severe 1 (16.6667 %) 
Variance from outliers : 13.8889 % Variance is moderately inflated by outliers 
+3

Это 100-кратное различие во времени между «разогретым» и холодным временем автономной работы - это то, почему вы действительно не можете получить значимые контрольные точки в Clojure без тщательного рассмотрения. Отличный ответ. –

+0

Или вместо этого используйте единственный подходящий инструмент для измерения производительности на JVM, созданный инженерами Oracle по производительности - JMH – SerCe

Смежные вопросы