Je suis tombé sur les affirmations de Xamarin selon lesquelles leur implémentation Mono sur Android et leurs applications compilées en C# sont plus rapides que le code Java. Est-ce que quelqu'un a effectué des benchmarks réels sur des codes Java et C# très similaires sur différentes plateformes Android pour vérifier ces affirmations, et a pu poster le code et les résultats ?
Ajouté le 18 juin 2013
Comme il n'y avait pas de réponse et que je n'ai pas pu trouver de tels benchmarks réalisés par d'autres, j'ai décidé de faire mes propres tests. Malheureusement, ma question reste "verrouillée" et je ne peux donc pas publier cette réponse, mais seulement modifier la question. Veuillez voter pour la réouverture de cette question. Pour C#, j'ai utilisé Xamarin.Android ver. 4.7.09001 (beta). Le code source, toutes les données que j'ai utilisées pour les tests, et les paquets APK compilés sont sur GitHub :
Java : https://github.com/gregko/TtsSetup_Java
C# : https://github.com/gregko/TtsSetup_C_sharp
Si quelqu'un souhaite répéter mes tests sur d'autres appareils ou émulateurs, je serais intéressé de connaître les résultats également.
Résultats de mes tests
J'ai porté ma classe d'extracteur de phrases en C# (à partir de mon application @Voice Aloud Reader) et j'ai effectué quelques tests sur 10 fichiers html en anglais, russe, français, polonais et tchèque. Chaque test a été effectué 5 fois sur les 10 fichiers, et le temps total pour 3 appareils différents et un émulateur est affiché ci-dessous. J'ai testé les versions "Release" uniquement, sans débogage activé.
HTC Nexus One Android 2.3.7 (API 10) - ROM CyanogenMod
Java : Temps total (5 exécutions) : 12361 ms, avec un total de lecture de fichier : 13304 ms
C# : Temps total (5 exécutions) : 17504 ms, avec un total de lecture de fichier : 17956 ms
Samsung Galaxy S2 SGH-I777 (Android 4.0.4, API 15) - ROM CyanogenMod
Java : Temps total (5 exécutions) : 8947 ms, avec un total de lecture de fichier : 9186 ms
C# : Temps total (5 exécutions) : 9884 ms, avec un total de 10247 ms pour la lecture du fichier.
Samsung GT-N7100 (Android 4.1.1 JellyBean, API 16) - ROM Samsung
Java : Temps total (5 exécutions) : 9742 ms, avec un total de lecture de fichier : 10111 ms
C# : Temps total (5 exécutions) : 10459 ms, avec un total de lecture de fichier : 10696 ms
Emulateur - Intel (Android 4.2, API 17)
Java : Temps total (5 exécutions) : 2699 ms, avec un total de 3127 ms pour la lecture du fichier.
C# : Temps total (5 exécutions) : 2049 ms, avec un total de 2182 ms pour la lecture du fichier.
Emulateur - Intel (Android 2.3.7, API 10)
Java : Temps total (5 exécutions) : 2992 ms, avec un total de lecture de fichier : 3591 ms
C# : Temps total (5 exécutions) : 2049 ms, avec lecture du fichier total : 2257 ms
Emulateur - Arm (Android 4.0.4, API 15)
Java : Temps total (5 exécutions) : 41751 ms, avec un total de 43866 ms pour la lecture du fichier.
C# : Temps total (5 exécutions) : 44136 ms, avec un total de lecture de fichier : 45109 ms
Brève discussion
Mon code de test contient principalement des analyses de texte, des remplacements et des recherches Regex, peut-être que pour d'autres codes (par exemple, plus d'opérations numériques), les résultats seraient différents. Sur tous les appareils dotés de processeurs ARM, Java a donné de meilleurs résultats que le code C# de Xamarin. La plus grande différence a été constatée sous Android 2.3, où le code C# s'est exécuté à environ 70 % de la vitesse de Java.
Sur un émulateur Intel (avec la technologie Intel HAX, l'émulateur fonctionne en mode virtuel rapide), le code C# de Xamarin exécute mon code d'exemple beaucoup plus rapidement que Java - environ 1,35 fois plus vite. Peut-être que le code et les bibliothèques de la machine virtuelle Mono sont bien mieux optimisés sur Intel que sur ARM ?
Editer le 8 juillet 2013
Je viens d'installer l'émulateur Android Genymotion, qui fonctionne dans Oracle VirtualBox, et encore une fois celui-ci utilise le processeur Intel natif, pas l'émulation du processeur ARM. Comme avec l'émulateur Intel HAX, C# s'exécute ici aussi beaucoup plus rapidement. Voici mes résultats :
Émulateur Genymotion - Intel (Android 4.1.1, API 16)
Java : Temps total (5 exécutions) : 2069 ms, avec un total de lecture de fichier : 2248 ms
C# : Temps total (5 exécutions) : 1543 ms, avec un total de lecture de fichier : 1642 ms
J'ai ensuite remarqué qu'il y avait une mise à jour de la version bêta de Xamarin.Android, la version 4.7.11, avec des notes de publication mentionnant certains changements dans le runtime Mono également. J'ai décidé de tester rapidement quelques appareils ARM, et grosse surprise : les chiffres de C# se sont améliorés :
BN Nook XD+, ARM (Android 4.0)
Java : Temps total (5 exécutions) : 8103 ms, avec un total de 8569 ms pour la lecture du fichier.
C# : Temps total (5 exécutions) : 7951 ms, avec un total de lecture de fichier : 8161 ms
Wow ! C# est maintenant meilleur que Java ? J'ai décidé de répéter le test sur mon Galaxy Note 2 :
Samsung Galaxy Note 2 - ARM (Android 4.1.1)
Java : Temps total (5 exécutions) : 9675 ms, avec un total de lecture de fichier : 10028 ms
C# : Temps total (5 exécutions) : 9911 ms, avec lecture du fichier total : 10104 ms
Ici, le C# ne semble être que légèrement plus lent, mais ces chiffres m'ont fait réfléchir : Pourquoi le temps est plus long que sur le Nook HD+, même si le Note 2 a un processeur plus rapide ? La réponse : le mode économie d'énergie. Sur le Nook, il était désactivé, sur le Note 2 - activé. J'ai décidé de tester avec le mode d'économie d'énergie désactivé (comme s'il était activé, il limite également la vitesse du processeur) :
Samsung Galaxy Note 2 - ARM (Android 4.1.1), économie d'énergie désactivée
Java : Temps total (5 exécutions) : 7153 ms, avec un total de lecture de fichier : 7459 ms
C# : Temps total (5 exécutions) : 6906 ms, avec un total de 7070 ms pour la lecture du fichier.
Maintenant, étonnamment, C# est légèrement plus rapide que Java sur un processeur ARM également. Une grande amélioration !
Editer le 12 juillet 2013
Nous savons tous que rien ne vaut le code natif pour la vitesse, et je n'étais pas satisfait des performances de mon séparateur de phrases en Java ou C#, en particulier que je dois l'améliorer (et donc le rendre encore plus lent). J'ai décidé de le réécrire en C++. Voici une petite comparaison (c'est-à-dire un ensemble de fichiers plus petit que les tests précédents, pour d'autres raisons) de la vitesse du langage natif par rapport à Java sur mon Galaxy Note 2, avec le mode économie d'énergie désactivé :
Java : Temps total (5 exécutions) : 3292 ms, avec un total de lecture de fichier : 3454 ms
Pouce natif : Temps total (5 exécutions) : 537 ms, avec lecture du fichier total : 657 ms
Bras natif : Temps total (5 exécutions) : 458 ms, avec lecture de fichier total : 587 ms
Il semble que pour mon test particulier, le code natif soit 6 à 7 fois plus rapide que Java. Mise en garde : je n'ai pas pu utiliser la classe std::regex sur Android, j'ai donc dû écrire mes propres routines spécialisées pour rechercher les sauts de paragraphes ou les balises html. Mes premiers tests du même code sur un PC en utilisant regex, étaient environ 4 à 5 fois plus rapides que Java.
Ouf ! En réveillant à nouveau la mémoire brute avec des pointeurs char* ou wchar*, je me suis instantanément senti rajeuni de 20 ans ! :)
Editer le 15 juillet 2013
(Voir ci-dessous, avec les modifications du 30/07/2013, pour de bien meilleurs résultats avec Dot42)
Avec quelques difficultés, j'ai réussi à porter mes tests C# vers Dot42 (version 1.0.1.71 beta), une autre plateforme C# pour Android. Les résultats préliminaires montrent que le code Dot42 est environ 3x (3 fois) plus lent que Xamarin C# (v. 4.7.11), sur un émulateur Intel Android. Un problème est que la classe System.Text.RegularExpressions dans Dot42 n'a pas la fonction Split() que j'ai utilisée dans les tests Xamarin, donc j'ai utilisé la classe Java.Util.Regex à la place, et Java.Util.Regex.Pattern.Split(), donc à cet endroit particulier du code il y a cette petite différence. Cela ne devrait pas être un gros problème. Dot42 compile en code Dalvik (DEX), donc il coopère avec Java sur Android de manière native, il n'a pas besoin d'une interopération coûteuse de C# à Java comme Xamarin.
À titre de comparaison, j'ai également effectué le test sur des appareils ARM - ici, le code Dot42 est "seulement" 2 fois plus lent que le C# de Xamarin. Voici mes résultats :
HTC Nexus One Android 2.3.7 (ARM)
Java : Temps total (5 exécutions) : 12187 ms, avec un total de lecture de fichier : 13200 ms
Xamarin C# : Temps total (5 exécutions) : 13935 ms, avec un total de lecture de fichier : 14465 ms
Dot42 C# : Temps total (5 exécutions) : 26000 ms, avec lecture du fichier total : 27168 ms
Samsung Galaxy Note 2, Android 4.1.1 (ARM)
Java : Temps total (5 exécutions) : 6895 ms, avec un total de 7275 ms pour la lecture du fichier.
Xamarin C# : Temps total (5 exécutions) : 6466 ms, avec un total de 6720 ms pour la lecture du fichier.
Dot42 C# : Temps total (5 exécutions) : 11185 ms, avec un total de lecture de fichier : 11843 ms
Émulateur Intel, Android 4.2 (x86)
Java : Temps total (5 exécutions) : 2389 ms, avec un total de 2770 ms pour la lecture du fichier.
Xamarin C# : Temps total (5 exécutions) : 1748 ms, avec un total de lecture de fichier : 1933 ms
Dot42 C# : Temps total (5 exécutions) : 5150 ms, avec un total de 5459 ms pour la lecture du fichier.
Il était également intéressant de noter que Xamarin C# est légèrement plus rapide que Java sur un appareil ARM récent, et légèrement plus lent sur le vieux Nexus One. Si quelqu'un souhaite également effectuer ces tests, faites-le moi savoir et je mettrai à jour les sources sur GitHub. Il serait particulièrement intéressant de voir les résultats sur un vrai appareil Android avec un processeur Intel.
Mise à jour 7/26/2013
Juste une mise à jour rapide, recompilé par des applications de référence avec la dernière Xamarin.Android 4.8, et aussi avec la mise à jour dot42 1.0.1.72 publiée aujourd'hui - pas de changements significatifs par rapport aux résultats rapportés auparavant.
Mise à jour 7/30/2013 - meilleurs résultats pour dot42
J'ai re-testé Dot42 avec le portage par Robert (de dot42 makers) de mon code Java vers C#. Dans mon portage C# réalisé initialement pour Xamarin, j'ai remplacé certaines classes Java natives, comme ListArray, par la classe List native de C#, etc. Robert n'avait pas mon code source Dot42, il l'a donc porté à nouveau à partir de Java et a utilisé les classes Java originales à ces endroits, ce qui profite à Dot42, je suppose parce qu'il fonctionne dans Dalvik VM, comme Java, et non dans Mono, comme Xamarin. Maintenant, les résultats de Dot42 sont bien meilleurs. Voici un journal de mes tests :
30/07/2013 - Les tests de Dot42 avec plus de classes Java dans Dot42 C#
Émulateur Intel, Android 4.2
Dot42, le code de Greg utilise StringBuilder.Replace() (comme dans Xamarin) :
Temps total (5 exécutions) : 3646 ms, avec un total de 3830 ms pour la lecture du fichier.Dot42, le code de Greg utilise String.Replace() (comme dans le code de Java et de Robert) :
Temps total (5 exécutions) : 3027 ms, avec un total de 3206 ms pour la lecture du fichier.Dot42, Code Robert :
Temps total (5 exécutions) : 1781 ms, avec un total de lecture de fichier : 1999 msXamarin :
Temps total (5 exécutions) : 1373 ms, avec un total de 1505 ms pour la lecture du fichier.Java :
Temps total (5 exécutions) : 1841 ms, avec un total de 2044 ms pour la lecture du fichier.ARM, Samsung Galaxy Note 2, économie d'énergie désactivée, Android 4.1.1
Dot42, le code de Greg utilise StringBuilder.Replace() (comme dans Xamarin) :
Temps total (5 exécutions) : 10875 ms, avec un total de 11280 ms pour la lecture du fichier.Dot42, le code de Greg utilise String.Replace() (comme dans le code de Java et de Robert) :
Temps total (5 exécutions) : 9710 ms, avec un total de lecture de fichier : 10097 msDot42, Code Robert :
Temps total (5 exécutions) : 6279 ms, avec un total de 6622 ms pour la lecture du fichier.Xamarin :
Temps total (5 exécutions) : 6201 ms, avec un total de 6476 ms pour la lecture du fichier.Java :
Temps total (5 exécutions) : 7141 ms, avec un total de 7479 ms pour la lecture du fichier.
Je pense toujours que Dot42 a un long chemin à parcourir. Le fait d'avoir des classes semblables à celles de Java (par exemple ArrayList) et une bonne performance avec elles, rendrait le portage de code de Java à C# légèrement plus facile. Cependant, c'est quelque chose que je ne ferais probablement pas souvent. Je préférerais utiliser du code C# existant (bibliothèques, etc.), qui utilisera des classes C# natives (par exemple List), et qui fonctionnera lentement avec le code actuel dot42, et très bien avec Xamarin.
Greg
5 votes
Mode DEBUG sur Nexus 7 4.2.2 avec quelques optimisations sur les chaînes de caractères et xamarin alpha 9 : Temps total : 3907 ms, avec un total de lecture de fichiers : 4016. Que signifie "5 runs" ?
0 votes
Mode Release sur la même plateforme : Temps total : 2192 ms, avec un total de lecture de fichiers : 2279
0 votes
Mais votre code peut être optimisé bien davantage.
0 votes
@Softlion, merci de l'avoir testé aussi ! Par 5 exécutions, je veux dire que j'appuie sur le bouton "Start Test" 5 fois, puis sur le bouton "back" du téléphone pour quitter l'application, et ensuite il sort dans la console de débogage le temps total des 5 exécutions. Veuillez m'envoyer les optimisations proposées, de préférence par courriel ou par message privé, car nous ne pouvons pas taper beaucoup ici. Mais nous devrions optimiser le code Java de manière similaire pour avoir une comparaison équitable entre C# et Java.
0 votes
Vous utilisez trop de chaînes de caractères. Vous devriez utiliser un seul StringBuilder et des index de chaînes. Les chaînes sont immuables, et allouent de la nouvelle mémoire à chaque concaténation. Vous ne devriez pas non plus utiliser "Count()" mais "Count" à la place. Il s'agit d'une erreur "classique".
0 votes
@Softlion, j'utilise les StringBuilders quand je le peux. En C#, je les utilise aussi pour rechercher et remplacer des caractères, en Java, je ne peux pas, car il n'a pas cette fonction. De toute façon, pour les besoins de ce texte, les caractères de recherche et de remplacement n'ont pas d'importance, car ils n'apparaissent pas dans mes fichiers de test - la recherche est effectuée, mais pas le remplacement, donc pas besoin de modifier les chaînes "immuables" en Java. J'ai eu de nombreux commentaires similaires, j'ai introduit des changements proposés sans aucun effet significatif sur le résultat. Veuillez indiquer les changements spécifiques et je pourrai tester à nouveau.
0 votes
@Softlion, je viens de revoir mon code C# pour .Count vs. .Count() - je les utilise correctement, j'utilise .Count pour les listes, et .Count() pour les tableaux, comme le tableau String[]. Ce n'est pas une erreur "classique", faire autrement est simplement une erreur de syntaxe qui ne permet pas au code de compiler...
0 votes
Count() est une méthode linq qui lit l'énumérable entier. Pour les tableaux, vous devez utiliser .Length et non Count(). Tout autre endroit où vous utilisez de lourdes concaténations de chaînes, y compris dans les boucles, vous ne devriez pas le faire. Cela implique de réécrire votre algorithme car vous devriez utiliser les index + longueurs des chaînes et le plus petit nombre de StringBuilders au lieu de ce que vous faites. Vous devriez également utiliser des regex statiques, ils sont thread safe.
0 votes
@Softlion, lorsque vous me parlez de méthodes comme celle-ci, précisez si vous parlez de Java ou de C#. Je suppose que Linq serait C#. Puisque vous pouvez obtenir mon code source à tout moment, veuillez le modifier comme vous le proposez et poster les résultats. J'ai essayé des modifications similaires dans le passé et je n'ai pas remarqué d'amélioration des performances. Le seul endroit où j'utilise .Count() sur un tableau est dans ma boucle de test principale, pour parcourir en boucle une liste de 10 fichiers. Je ne vois pas comment cela pourrait changer le résultat du benchmark.
0 votes
Je l'ai modifié mais je ne comprends pas assez git. Je ne connais que les bases de java donc je ne dirai rien sur le code java.
0 votes
@Softlion, merci ! Si possible, partagez vos modifications de code C# par email avec moi (gregko AT hyperionics DOT com), je ferai quelques tests et les posterai aussi. Dans vos tests, avez-vous vu une différence avec vos modifications de code ? Je suis toujours prêt à apprendre, merci encore !
0 votes
Je suis curieux de connaître la taille de l'apk et la consommation de mémoire au moment de l'exécution ?
0 votes
@numan - pour Xamarin, l'APK avec les binaires Intel et armeabi-v7a intégrés, le paquet fait environ 5.6 MB, construit en mode release avec le dernier Xamarin stable. Pour Dot42 et Java, il est inférieur à 200 kB. Je ne sais pas quelle est la meilleure façon de mesurer l'utilisation de la mémoire sur Android.
0 votes
@gregko Il n'y a pas de profileur dans xamarin ? Dans ADT (eclipse), vous pouvez profiler l'application pour l'utilisation de la mémoire.
0 votes
@numan - Le moniteur de débogage Android montre une utilisation du tas très similaire pour C# et Java (environ 5 Mo pour une seule exécution de mes fichiers de test). Je ne vois aucun moyen d'afficher l'utilisation totale de la mémoire du processus. Xamarin prendra certainement plus de place, car il doit d'abord charger le runtime Mono avant d'exécuter tout code, tandis que le code Java et Dot42 C# s'exécute directement dans Dalvik VM.
1 votes
"cette question suscitera probablement des débats, des arguments, des sondages ou des discussions prolongées" <- voir ci-dessus ;)
2 votes
@LearnCocos2D - Je ne fais que rapporter des résultats concrets et des chiffres, c'est-à-dire des faits. Les gentlemen ne contestent pas les faits :)
2 votes
Eh bien, les scientifiques le font ;) il y a une différence entre un comportement observé et un fait. Il faut beaucoup plus pour que cela devienne un fait, et même dans ce cas, l'applicabilité à d'autres utilisateurs/situations reste discutable. C'est le point crucial des benchmarks, ils ne présentent les faits qu'en surface - jusqu'à ce que vous découvriez que le fournisseur x a optimisé son pilote pour une application de benchmark particulière. Dans le même ordre d'idées, il a été prouvé une fois que l'eau avait de la mémoire (c'est-à-dire le test de l'homéopathie), ce qui a été réfuté après que le biais du testeur ait été pris en compte et écarté, puis il n'a pas montré de signification statistique.
3 votes
De plus, avec la prochaine version +0.1, ces caractéristiques de performance peuvent changer de manière significative - c'est alors que tous vos bons efforts présentés ici passent de "fait" à "discutable". Cependant, toute personne qui vient ici peut percevoir cela comme un fait et en tirer une conclusion erronée. Autre point crucial des benchmarks : ils ne sont représentatifs que pour un moment donné dans le temps et les versions du logiciel utilisé. Le lendemain, ils peuvent ne plus refléter la réalité. Vous devez sans cesse retester les résultats. C'est pourquoi les résultats présentés ici peuvent être considérés comme subjectifs et n'ayant que peu ou pas de signification.
0 votes
C'est une comparaison assez intéressante. Pourriez-vous essayer de faire également la comparaison sur Android L , puisque ART a remplacé Dalvik, et est censé être plus rapide ?
0 votes
@androiddeveloper, je n'ai plus d'abonnement à Xamarin, donc je ne peux pas faire cela du côté de Xamarin.
0 votes
Les liens sur GitHub sont cassés !
3 votes
Je vote pour fermer cette question comme étant hors sujet parce que les informations contenues sont périmées depuis 3 ans et que cela ne correspond à aucun critère actuel d'une question acceptable.