Skyduino:~#
Articles
.net framework, Autre, C/C++, Corrigé, programmation, python, test, web

[code] Comment faire la même chose sous différents langages

Bonjour tout le monde !

Il y a quelques temps j’étais parti dans l’idée de faire une sorte de benchmark entre différents langages de programmation.
Dans l’idée c’était pas mal, tester le mordant de différents langages c’est pas totalement inutile, c’est même plutôt intéressant.

Problème : pour mes tests j’utilisais une bête boucle que je faisais tourner à pleine vitesse pendant un temps fixe.
Au final je testais donc le répondant des appels systèmes (pour gérer le temps) et non la puissance du langage (un langage c’est aussi une syntaxe, des fonctionnalités, etc etc …)

Pour certains langages, mes petits tests (que vous avez sûrement déjà du voir en direct si vous me suivez sur twitter ;) ) donnent des résultats plutôt inattendus(vitesse).
Encore plus intéressant, pour un même langage le résultat de deux utilisations différentes peut donner des résultats aussi contrasté que le jour et la nuit.

Ps: ne faite pas la même erreur que moi de dire "X et n fois plus rapide que Y", c’est faux.
Pour tester deux langages il faudrait mettre en parallèle les syntaxes, les fonctionnalités et le fonctionnement interne (interprété, compilé, semi-compilé, …) le tout avec un programme de test bien plus complexe qu’une simple boucle.

Bref, les résultats sont à prendre avec des pincettes !
Je vous laisse juger par vous même ;)

Le code de référence, un code C POSIX :

/* Simple KIPS counter */
#include <time.h>
#include <stdio.h>

int main(void) {

	long ips = 0;
	long time_end = time(NULL) * 1000 + 10000;

	for (;;) {

		if (time(NULL) * 1000 == time_end)
			break;
		++ips;
	}

	printf("KIPS: %lu\n", (ips / 10) / 1000UL);

	return 0;
}

-> langage natif du linuxien, vu comme le saint Graal des langages de programmation par beaucoup de monde.

--

Son adversaire, un code en Java (avec la JRE 7) :

/* Simple KIPS counter */
public class Test_mips {
	public static void main(String[] args) {

		long ips = 0;
		long time_end = System.currentTimeMillis() + 10000;

		while (true) {

			if (System.currentTimeMillis() == time_end)
				break;

			++ips;
		}

		System.out.println("KIPS: " + (ips / 10) / 1000);
	}
}

-> Méprisé de tous pour sa (soit disant) lenteur d'exécution, sa lourdeur en RAM et tous les maux de la terre qu'on pu trouver nos amis les trolls.

--

Le résultat C POSIX vs Java :

Amis linuxien je suis désolé, le résultat ci dessous risque de fendre votre petit cœur de manchot :)
Mais restez calme, je fait du POSIX sous windows avec MinGW, ceci explique donc cela ;)

Java : 63242 KIPS
C POSIX (via MinGW) : 3953 KIPS

Soit une différence de l'ordre de x15 entre java et C posix.

A qui la faute ?
A mon avis le duo windows + MinGW + surcouche POSIX a eu raison des performances de time(NULL).
En refaisant le test sous linux les choses serait sûrement bien différente.

Par contre cela pointe du doigt un fait intéressant, time(NULL) sous windows avec MinGW est (semble t-il) d'une lenteur sans limite ...

--

Nouveaux challenger, un code C WIN32 natif :

/* Simple KIPS counter */
#include <windows.h>
#include <stdio.h>

int main(void) {

	long ips = 0;
	long time_end = GetTickCount() + 10000;

	for (;;) {

		if (GetTickCount() == time_end)
			break;

		++ips;
	}

	printf("KIPS: %lu\n", (ips / 10) / 1000UL);

	return 0;
}

--

Le résultat C WIN32 vs Java :

Bam tss, java s'est pris un beau revers dans les dents ! ;)

C WIN32 : 163615 KIPS
Java : 63482 KIPS

Soit une différence de l'ordre de x2.5 entre C win32 et java.

L'écart est quand même relativement serré bien qu'on travaille avec des appels système Win32 natif avec d'un côté un code machine compilé et de l'autre un code sous machine virtuelle (jvm java).
Comme quoi java n'est pas si lent que ça, enfin dans le contexte présent.

Mais on peut faire encore mieux côté C win32 en compilant sans MinGW directement avec le compilateur C/C++ de microsoft !

Même code, mais de bien meilleurs résultats !

C WIN32 : 196034 KIPS

Soit une différence de l'ordre de x3 entre C win32 (avec VC++) et java.

Le mystère reste entier, est-ce MinGW ou GCC qui cause ce ralentissement ou le fait que le compilateur VC++ soit optimisé Windows, personnellement je n'en sais rien.

--

Test suivant, code C# avec le .net framework 4.5

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace test_mips
{
    class Program
    {
        static void Main(string[] args)
        {

            long i = 0;
            long time_end = System.Environment.TickCount + 10000;

            for (; ; )
            {
                if (System.Environment.TickCount == time_end) break;
                ++i;
            }

            System.Console.WriteLine("KIPS: " + ((i / 10) / 1000).ToString());
            System.Console.ReadKey();
        }
    }
}

-> mon langage préféré quand il s'agit de faire des applications avec GUI, simple, facile et (à peu prés) portable sous linux/mac avec Mono.

C# .net 4.5 : 118077 KIPS

Soit une différence de l'ordre de x1.8 entre C# et java.
Pas mal pour un langage semi-compilé, interprété par une machine virtuelle (même principe que java).

-> Gros avantage des applications .net : elles peuvent être optimisées lors de l'installation / déploiement pour tourner à pleine puissance sur la machine cible, chose impossible avec java (enfin il me semble).

--

Soyons fou ! Python 2.7.3 !

Juste pour le fun ;)

import time

time_end = time.time() + 10
i = 0

while 1:
    if time.time() == time_end: break
    i = i + 1

print 'KIPS:', (i / 10) / 1000

(Vous remarquerez quand même la différence en nombre de lignes)

Python : 1702 KIPS

Bon c'est pas génial comme résultat, mais il faut relativiser les choses, python est un langage de script.

--

Et parce qu'il y a toujours pire que soit, PHP

Exécuté sous PHP 5.4.3 donc avec une phase de précompilation en bytecode (Zend Engine 2).
Attention chérie, ça va troller ! :)

<?php

$time_end = round(microtime(true) * 1000) + 10000;
$i = 0;

for(;;) {
  if(round(microtime(true) * 1000) == $time_end) break;
  ++$i;
}

echo 'KIPS: ' . ($i / 10) / 1000;

?>

PHP : 504 KIPS

*sifflote*
Et dire que les 3/4 de la planète utilisent PHP comme langage de développement web ...
(On fera jamais mieux que cgi-bin avec des applications natives :))

--

Et bien voila c'était l'article à tendance troll de la semaine, demain c'est vendredi, donc allez y amis troll, le vendredi c'est permis ;)

Le but d'origine de ces morceaux de codes n'était pas vraiment de tester les performances brutes, mais plutôt de voir si j'arrivais à passer d'un langage à un autre sans me planter.
Sur ce point je suis assez content de moi, mais pour ce qui est du côté "test" à proprement parler on repassera ^^

About these ads

Discussion

5 réflexions sur “[code] Comment faire la même chose sous différents langages

  1. Ce test m’amène plusieurs remarques.

    Tout d’abord, il y a une petite erreur sur l’unité. MIPS signifie : millions d’instructions par seconde. Ici on devrait plutôt parler de kIPS (milliers d’instructions par seconde). Ces unités normalisées font normalement référence au nombre d’instructions machine exécutée au plus bas niveau. Ici, il serait donc préférable de parler simplement d’itérations par seconde. C’était d’ailleurs peut être l’idée initiale, et l’usage de MIPS n’est peut être qu’une coïncidence malheureuse…

    Deuxièmement, la façon dont est rédigé le test fait que celui-ci ne teste pas du tout ce qu’il est censé tester (la performance d’un langage) . Le fait de vérifier un timing au sein même du test représente un temps machine beaucoup plus important que le test lui même. Environ 98% du temps écoulé est passé sur l’instruction qui mesure le temps écoulé afin de décider de l’arrêt du test! Ce test mesure donc uniquement la performance de la fonction utilisée pour mesurer le temps écoulé. Les performances brutes du langage proprement dit n’interviennent qu’à hauteur d’environ 2% dans ces résultats!

    Pour que ces tests soient significatifs, il faudrait plutôt les rédiger de la façon suivante (exemple en Java):

            long i = 0;
    
            final long startTime = System.currentTimeMillis();
    
            while ( i  &lt; 10_000_000_000L ) {
                ++i;
            }
    
            final long duration = System.currentTimeMillis() - startTime;
    
            System.out.println( i / duration / 1000 + &quot;  Itérations / seconde&quot;  ); 
    

    Là, on mesure bien, et uniquement, la performance de la boucle d'instructions.

    D'autre part, en particulier pour les langages interprétés ou compilés à la volée, il est important de faire la différence en entre un test à chaud et un test à froid. Sur un test aussi simple ça ne joue pas tellement (de 1 à 2% ici en java par exemple) mais, sur des tests plus complexes, les écarts peuvent devenir très importants.

    Donc pour pouvoir comparer des pommes et des poires, il faudrait mieux sortir les résultats à froid et à chaud. Exemple en java:

            System.out.print( &quot;A froid &quot; );
             test();                                                  // On lance un premier test.
            Thread.sleep( 1000 );                           // On du temps à l'environnement pour s'optimiser. 
            System.out.print( &quot;A chaud &quot; );
            test                                                      // On lance un second test.
    

    Publié par Stratic | 14 septembre 2012, 15 h 41 min
    • Zut !
      J’avais pas fait gaffe que je divisai par 1000 et non 1000000, du coup c’est effectivement des milliers d’instructions par secondes et non des millions.
      -> Je corrige de suite le texte ;)

      >> C’était d’ailleurs peut être l’idée initiale, et l’usage de MIPS n’est peut être qu’une coïncidence malheureuse…
      C’était effectivement l’idée initiale, je n’avais pas pensez que cela irait poser probléme ;)

      Pour les tests à chaud / à froid c’est effectivement un point à prendre en compte.
      Pour la prise de mesure avant et aprés la boucle je n’y avait tout simplement pas pensé …
      Comme on dit "plus c’est simple moins on y pense" :)

      Ps:
      C’est juste une petite série de tests vite fait, je n’ai pas la prétention de vouloir faire un test "réel’.
      C’est du reste pour cela que j’ai nommé l’article "Comment faire la même chose sous différent langage"et non "Quel langage est le plus puissant" ;)

      Publié par skywodd | 14 septembre 2012, 19 h 24 min
  2. Je réagissais, surtout parce car ce qui est montré par les chiffres présentés , et malgré qu’il soit bien dit que ce ceux-ci étaient à prendre avec des pincettes, sont en fait à 100 000 lieues de la réalité et peuvent vraiment tromper ceux qui leur accorderaient le moindre crédit.

    Publié par Stratic | 14 septembre 2012, 19 h 38 min
  3. c est une note c’est léger c est frais c’est vendredi en plus :)

    Publié par g2-024d411d164b772be410d910c8779ee5 | 14 septembre 2012, 22 h 01 min
  4. Il faudrait corriger le code de référence C POSIX pour ne pas avoir plus d’instructions:

    long time_end = time(NULL) * 1000 + 10000; –> long time_end = time(NULL) 10;
    if (time(NULL) * 1000 == time_end) –> if (time(NULL) == time_end)

    Avec ces deux modifications ca pourrait devenir significatif, la boucle n’étant pas plus longue par construction.

    Publié par sebbu | 27 avril 2013, 20 h 59 min

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s

Archives

Wow. Dogecoin. Amaze.

Laissez un tip en Dogecoin

DMMNFk6WBVTpx2Wu1Z35GL61QXSd6r6WQx

Suivre

Recevez les nouvelles publications par mail.

Rejoignez 715 autres abonnés