L'interprétation d'un test en C, Clojure, Python, Ruby, Scala et les autres

Avertissement

Je sais que les indices de référence sont le mal. Ils peuvent afficher uniquement les résultats pour des cas très particuliers étroit de la situation. Je ne suppose pas qu'une langue est mieux que les autres en raison de la certains stupide banc. Cependant je me demande pourquoi les résultats sont si différents. Veuillez voir mes questions au fond.

Math référence description

Indice de référence des calculs mathématiques simples pour trouver des paires de nombres premiers qui diffèrent par 6 (dite sexy nombres premiers)
E. g. sexy nombres premiers inférieurs à 100 serait: (5 11) (7 13) (11 17) (13 19) (17 23) (23 29) (31 37) (37 43) (41 47) (47 53) (53 59) (61 67) (67 73) (73 79) (83 89) (97 103)

Tableau des résultats de la

Dans le tableau: temps de calcul en secondes
En cours d'exécution: tous sauf le Facteur était en cours d'exécution dans VirtualBox (la version unstable de Debian amd64 invité, Windows 7 x64 accueil)
PROCESSEUR: AMD A4-3305M

  Sexy primes up to:        10k      20k      30k      100k               

  Bash                    58.00   200.00     [*1]      [*1]

  C                        0.20     0.65     1.42     15.00

  Clojure1.4               4.12     8.32    16.00    137.93

  Clojure1.4 (optimized)   0.95     1.82     2.30     16.00

  Factor                    n/a      n/a    15.00    180.00

  Python2.7                1.49     5.20    11.00       119     

  Ruby1.8                  5.10    18.32    40.48    377.00

  Ruby1.9.3                1.36     5.73    10.48    106.00

  Scala2.9.2               0.93     1.41     2.73     20.84

  Scala2.9.2 (optimized)   0.32     0.79     1.46     12.01

[*1] - j'ai peur d'imaginer combien de temps cela prend-il

Listes de Code

C:

int isprime(int x) {
  int i;
  for (i = 2; i < x; ++i)
    if (x%i == 0) return 0;
  return 1;
}

void findprimes(int m) {
  int i;
  for ( i = 11; i < m; ++i)
    if (isprime(i) && isprime(i-6))
      printf("%d %d\n", i-6, i);
}

main() {
    findprimes(10*1000);
}

Ruby:

def is_prime?(n)
  (2...n).all?{|m| n%m != 0 }
end

def sexy_primes(x)
  (9..x).map do |i|
    [i-6, i]
  end.select do |j|
    j.all?{|j| is_prime? j}
  end
end

a = Time.now
p sexy_primes(10*1000)
b = Time.now
puts "#{(b-a)*1000} mils"

Scala:

def isPrime(n: Int) =
  (2 until n) forall { n % _ != 0 }

def sexyPrimes(n: Int) = 
  (11 to n) map { i => List(i-6, i) } filter { _ forall(isPrime(_)) }

val a = System.currentTimeMillis()
println(sexyPrimes(100*1000))
val b = System.currentTimeMillis()
println((b-a).toString + " mils")

Scala opimized isPrime (la même idée comme en Clojure optimisation):

import scala.annotation.tailrec

@tailrec //Not required, but will warn if optimization doesn't work
def isPrime(n: Int, i: Int = 2): Boolean = 
  if (i == n) true 
  else if (n % i != 0) isPrime(n, i + 1)
  else false

Clojure:

(defn is-prime? [n]
  (every? #(> (mod n %) 0)
    (range 2 n)))

(defn sexy-primes [m]
  (for [x (range 11 (inc m))
        :let [z (list (- x 6) x)]
        :when (every? #(is-prime? %) z)]
      z))

(let [a (System/currentTimeMillis)]
  (println (sexy-primes (* 10 1000)))
  (let [b (System/currentTimeMillis)]
    (println (- b a) "mils")))

Clojure optimisé is-prime?:

(defn ^:static is-prime? [^long n]
  (loop [i (long 2)] 
    (if (= (rem n i) 0)
      false
      (if (>= (inc i) n) true (recur (inc i))))))

Python

import time as time_

def is_prime(n):
  return all((n%j > 0) for j in xrange(2, n))

def primes_below(x):
  return [[j-6, j] for j in xrange(9, x+1) if is_prime(j) and is_prime(j-6)]

a = int(round(time_.time() * 1000))
print(primes_below(10*1000))
b = int(round(time_.time() * 1000))
print(str((b-a)) + " mils")

Facteur

MEMO:: prime? ( n -- ? )
n 1 - 2 [a,b] [ n swap mod 0 > ] all? ;

MEMO: sexyprimes ( n n -- r r )
[a,b] [ prime? ] filter [ 6 + ] map [ prime? ] filter dup [ 6 - ] map ;

5 10 1000 * sexyprimes . .

Bash(zsh):

#!/usr/bin/zsh
function prime {
  for (( i = 2; i < $1; i++ )); do
    if [[ $[$1%i] == 0 ]]; then
      echo 1
      exit
    fi
  done
  echo 0
}

function sexy-primes {
  for (( i = 9; i <= $1; i++ )); do
    j=$[i-6]
    if [[ $(prime $i) == 0 && $(prime $j) == 0 ]]; then
      echo $j $i
    fi
  done
}

sexy-primes 10000

Questions

  1. Pourquoi Scala est si vite? C'est à cause de le typage statique? Ou c'est juste à l'aide de la JVM de manière très efficace?
  2. Pourquoi une telle différence entre Ruby et Python? Je pensais que ces deux ne sont pas un peu complètement différent. Peut-être que mon code est faux. Merci de m'éclairer! Merci. UPD Oui, c'était une erreur dans mon code. Python et Ruby 1.9 sont assez égale.
  3. Vraiment impressionnant saut de productivité entre les versions Rubis.
  4. Puis-je optimiser Clojure code par adjonction de ce type de déclarations? Il va les aider?
  • 1) Votre question n'est pas un bon ajustement pour DONC, à mon humble avis. 2) Pourquoi vous n'avez pas fourni Clojure et Scala versions?
  • Je veux dire que les versions linguistiques, comme 2.8, 2.9, ...
  • Pourriez-vous ajouter vos options de compilation pour chaque langue?
  • Non pas qu'il n'a rien à voir avec vos contrôles de performances, mais à partir d'un algorithme de point de vue, vous avez vraiment besoin de vérifier quelque chose comme n/2 de savoir s'il est premier ou non (bien qu'il ne sera probablement pas faire une énorme différence).
  • Si vous jetez un oeil à shootout.alioth.debian.org vous trouverez que vos résultats sont tous dans le typique par rapport plages
  • en fait jusqu'à sqrt(n) mais qui peut prendre un certain temps à calculer. Aussi votre code C imprime les primes comme il les trouve, alors que vos autres langues de calcul dans les listes et les imprime ensuite les sortir. Alors que C est sans surprise le plus rapide, vous pourriez être en mesure de l'obtenir plus rapidement.
  • vraiment, sqrt(n)? Je suppose que le sens ... Génial. Je souhaite que je pourrais préféré commentaire, donc je me souviendrai toujours (ou au moins être en mesure de le trouver). Je pensais naïvement c'est pourquoi j'ai dit n/2 (qui est juste un peu de décalage, donc ça devrait être rapide à calculer).
  • (Et bien sûr, le Crible d'Eratosthène .. mais ce micro-benchmark est un peu un test de stress de l'itération et des opérations mathématiques. Cependant, ils ne sont toujours pas "juste" comme dans certains sont plus paresseux.)
  • Pourrais-je vous envoyer un Aller à la source pour que vous exécutez sur votre VirtualBox? Je suis très rapide sur mon ordinateur portable, mais il est évidemment très différente de la configuration et je voudrais savoir comment il se compare à d'autres langues, sous votre configuration.
  • J'ai juste couru mon Go version et votre version de C (qui ressemblent beaucoup aussi) et j'ai pratiquement eu la même vitesse dans les deux d'entre eux. J'ai seulement essayé le 100k version: C: 2.723s Go: 2.743s.
  • Avec 1000*1000 (1M au lieu de 100K): C: 3m35.458s Go: 3m36.259s
  • Ajout d'une version de PHP. 100K: PHP: 1m3.766s Vraiment lent par rapport à C et.
  • sqrt() est généralement un bien optimisé fonction de la bibliothèque. Je pense que le coût d'un seul sqrt() sur un seul flotteur valeur sera beaucoup moins cher que de le faire répétées module d'opérations qui ne sont pas nécessaires, et plus les chiffres en cours de vérification obtenir, plus les économies. Je viens d'essayer le code de ma réponse, et changé le vérifier sqrt() juste n - 1 et des temps d'exécution plus que doublé (de 19 secondes).
  • J'ai essayé votre optimisation (case à n/2), et de réduire le temps de moitié dans tous les cas. Évidemment, proportionnellement, le taux de rendement entre les langues est le même qu'avant, mais je pense que vous s'est avéré un point: en terme de performance, écriture intelligente code est plus important que de choisir un rapide de la langue.
  • attendez jusqu'à ce que vous entendez à propos de déterministe variantes de Miller-Rabin test de primalité. Ils sont relativement simples à mettre en œuvre par exemple, Francky du code Python dans la discussion du problème 387 sur projecteuler.net.
  • Pour le Facteur, veuillez jeter un oeil à math.primes vocabulaire. Il a déjà un très bien optimisé prime? mot.
  • Vous n'avez pas besoin de calculer sqrt pour cette case. Vous pouvez calculer le carré de i comme dans for (i = 2; i * i <= x; ++i) ...
  • pourrait se déplacer vers le codegolf.pile... et d'essayer de voir quelle langue est plus rapide, ou qui peut mettre en œuvre le plus rapide pour chaque langue, etc. Je pense qu'il serait intéressant de mieux voir les suggestions, il y
  • Comparaisons de performances sont amusants, mais celui-ci, en particulier, s'appuyer sur une mise en application arbitraire des détails qui ne sont spécifiques à ce problème.
  • Je vous suggère de les annoter Scala optimisé isPrime avec @tailrec, pour vous assurer que vous êtes à l'aide de la queue de la récursivité. Il est facile, à tort, faire quelque chose qui empêche la queue de la récursivité, et cette annotation doit vous avertir si cela arrive.
  • Bon point! Pour le faire j'ai devrait import scala.annotation.tailrec
  • code Python qui utilise unoptimized Crible d'Eratosthène fonctionne en 0.03 secondes (30 millisecondes) pour 100K c'est à dire, 500 fois plus rapide que la version C ci-dessus.
  • Ouais, c'est génial. Mais vous utilisez du meilleur algorithme. Le but était de comprendre comment bien peut-on optimiser le simple (stupide) de l'algorithme dans des langues différentes.
  • Je comprends que. C'est pourquoi le commentaire de @ pst qui a fait état de cet algorithme.
  • btw, tapé version de Python explicite pour la boucle est 15 à 20 fois plus rapide (accepté réponse pour Clojure utilise également des types). Cette version utilise exactement le même algorithme.
  • J'ai posté résultats de l'exécution de l'indice de référence sur RPython, Pypy, Cython, Jython, Disponible 2.x, Disponible 3.
  • Dans la version 11 qui pastebin d'erreur indique que vous devez avoir enlevé le : Boolean type de l'annotation de la isPrime méthode (requis sur les méthodes récursives en raison de limitations dans l'inférence de type). Le site d'appel { _ forall isPrime } devrait être OK avec tout Scala version
  • J'ai ajouté un pur code C référence. Cython base de la variante a presque les mêmes performances que la C. Remarque: la seule différence entre le pur Python et Cython variantes est @cython.locals(n=int, j=int) décorateur qui raconte Cython sur les types statiques c'est à dire, il est semblable à l'optimisation de Clojure code à cet égard (mais plus rapide).

InformationsquelleAutor defhlt | 2012-07-25