Natknąłem się na twierdzenia Xamarin, że ich implementacja Mono na Androidzie i aplikacje kompilowane w języku C # są szybsze niż kod Java. Czy ktoś przeprowadził rzeczywiste testy porównawcze na bardzo podobnym kodzie Java i C # na różnych platformach Android, aby zweryfikować takie twierdzenia, mógł opublikować kod i wyniki?
Dodano 18 czerwca 2013 r
Ponieważ nie było odpowiedzi i nie mogłem znaleźć takich testów wykonanych przez innych, postanowiłem zrobić własne testy. Niestety moje pytanie pozostaje „zablokowane”, więc nie mogę opublikować tego jako odpowiedzi, tylko edytować pytanie. Głosuj, aby ponownie otworzyć to pytanie. Do C # użyłem Xamarin.Android Ver. 4.7.09001 (beta). Kod źródłowy, wszystkie dane, których użyłem do testowania, oraz skompilowane pakiety APK znajdują się w GitHub:
Java: https://github.com/gregko/TtsSetup_Java
C #: https://github.com/gregko/TtsSetup_C_sharp
Jeśli ktoś chciałby powtórzyć moje testy na innych urządzeniach lub emulatorach, chciałbym również poznać wyniki.
Wyniki z moich testów
Przeniesiłem moją klasę wyodrębniania zdań do C # (z mojej aplikacji @Voice Aloud Reader) i przeprowadziłem testy na 10 plikach HTML w języku angielskim, rosyjskim, francuskim, polskim i czeskim. Każde uruchomienie przeprowadzono 5 razy na wszystkich 10 plikach, a całkowity czas dla 3 różnych urządzeń i jednego emulatora podano poniżej. Testowałem tylko kompilacje „Release”, bez włączonego debugowania.
HTC Nexus One Android 2.3.7 (API 10) - CyanogenMod ROM
Java: Całkowity czas całkowity (5 uruchomień): 12361 ms, całkowity odczyt pliku: 13304 ms
C #: Całkowity czas całkowity (5 uruchomień): 17504 ms, z całkowitym odczytem pliku: 17956 ms
Samsung Galaxy S2 SGH-I777 (Android 4.0.4, API 15) - CyanogenMod ROM
Java: Grand całkowity czas (5 uruchomień): 8947 ms, z całkowitym odczytem pliku: 9186 ms
C #: Całkowity czas całkowity (5 uruchomień): 9884 ms, z całkowitym odczytem pliku: 10247 ms
Samsung GT-N7100 (Android 4.1.1 JellyBean, API 16) - Samsung ROM
Java: Całkowity czas całkowity (5 uruchomień): 9742 ms, z całkowitym odczytem pliku: 10111 ms
C #: Całkowity czas całkowity (5 uruchomień): 10459 ms, z całkowitym odczytem pliku: 10696 ms
Emulator - Intel (Android 4.2, API 17)
Java: Całkowity czas całkowity (5 uruchomień): 2699 ms, całkowity odczyt pliku: 3127 ms
C #: Całkowity czas całkowity (5 uruchomień): 2049 ms, z całkowitym odczytem pliku: 2182 ms
Emulator - Intel (Android 2.3.7, API 10)
Java: Całkowity czas całkowity (5 uruchomień): 2992 ms, całkowity odczyt pliku: 3591 ms
C #: Całkowity czas całkowity (5 uruchomień): 2049 ms, z całkowitym odczytem pliku: 2257 ms
Emulator - Uzbrojenie (Android 4.0.4, API 15)
Java: Całkowity czas całkowity (5 uruchomień): 41751 ms, całkowity odczyt pliku: 43866 ms
C #: Całkowity czas całkowity (5 uruchomień): 44136 ms, całkowity odczyt pliku: 45109 ms
Krótka dyskusja
Mój kod testowy zawiera głównie parsowanie tekstu, zastępowanie i wyszukiwanie Regex, być może dla innego kodu (np. Więcej operacji numerycznych) wyniki byłyby inne. Na wszystkich urządzeniach z procesorami ARM Java działała lepiej niż kod Xamarin C #. Największa różnica występowała w systemie Android 2.3, w którym kod C # działa w przybliżeniu. 70% prędkości Java.
Na emulatorze Intel (z technologią Intel HAX, emulator działa w trybie szybkiego podglądu), kod Xamarin C # uruchamia mój przykładowy kod znacznie szybciej niż Java - około 1,35 razy szybciej. Może kod i biblioteki maszyn wirtualnych Mono są znacznie lepiej zoptymalizowane na Intelie niż na ARM?
Edytuj 8 lipca 2013 r
Właśnie zainstalowałem emulator Genymotion Android, który działa w Oracle VirtualBox, i ponownie ten używa natywnego procesora Intel, nie emulując procesora ARM. Podobnie jak w przypadku emulatora Intel HAX, ponownie C # działa tutaj znacznie szybciej. Oto moje wyniki:
Emulator Genymotion - Intel (Android 4.1.1, API 16)
Java: Całkowity czas całkowity (5 uruchomień): 2069 ms, całkowity odczyt pliku: 2248 ms
C #: Całkowity czas całkowity (5 uruchomień): 1543 ms, całkowity odczyt pliku: 1642 ms
Następnie zauważyłem, że nastąpiła aktualizacja Xamarin.Android beta, wersja 4.7.11, z uwagami do wydania, w których wspomniano również o niektórych zmianach w środowisku wykonawczym Mono. Postanowiłem szybko przetestować niektóre urządzenia ARM i wielka niespodzianka - poprawiono liczby C #:
BN Nook XD +, ARM (Android 4.0)
Java: Całkowity czas całkowity (5 uruchomień): 8103 ms, całkowity odczyt pliku: 8569 ms
C #: Całkowity czas całkowity (5 uruchomień): 7951 ms, z całkowitym odczytem pliku: 8161 ms
Łał! C # jest teraz lepszy niż Java? Postanowiłem powtórzyć test na mojej Galaxy Note 2:
Samsung Galaxy Note 2 - ARM (Android 4.1.1)
Java: Całkowity czas całkowity (5 uruchomień): 9675 ms, całkowity odczyt pliku: 10028 ms
C #: Całkowity czas całkowity (5 uruchomień): 9911 ms, z całkowitym odczytem pliku: 10104 ms
Tutaj C # wydaje się być tylko nieco wolniejszy, ale te liczby zatrzymały mnie: Dlaczego czas jest dłuższy niż w Nook HD +, mimo że Note 2 ma szybszy procesor? Odpowiedź: tryb oszczędzania energii. W Nook został wyłączony, w Note 2 - włączony. Podjęto decyzję o przetestowaniu z wyłączonym trybem oszczędzania energii (podobnie jak przy włączonym, ogranicza także prędkość procesora):
Samsung Galaxy Note 2 - ARM (Android 4.1.1), wyłączono oszczędzanie energii
Java: Całkowity czas całkowity (5 uruchomień): 7153 ms, całkowity odczyt pliku: 7459 ms
C #: Całkowity czas całkowity (5 uruchomień): 6906 ms, z całkowitym odczytem pliku: 7070 ms
O dziwo, C # jest również nieco szybszy niż Java na procesorze ARM. Duża poprawa!
Edytuj 12 lipca 2013 r
Wszyscy wiemy, że nic nie przebije natywnego kodu szybkości i nie byłem zadowolony z wydajności mojego rozdzielacza zdań w Javie lub C #, szczególnie, że muszę go poprawić (a tym samym spowolnić). Postanowiłem przepisać go w C ++. Oto małe (tj. Mniejszy zestaw plików niż poprzednie testy, z innych powodów) porównanie prędkości natywnej i Java na moim Galaxy Note 2, z wyłączonym trybem oszczędzania energii:
Java: Całkowity czas całkowity (5 uruchomień): 3292 ms, całkowity odczyt pliku: 3454 ms
Natywny kciuk: Całkowity czas całkowity (5 uruchomień): 537 ms, całkowity odczyt pliku: 657 ms
Uzbrojenie rodzime: Całkowity czas całkowity (5 uruchomień): 458 ms, całkowity odczyt pliku: 587 ms
Wygląda na to, że w moim konkretnym teście natywny kod jest 6-7 razy szybszy niż Java. Zastrzeżenie: nie mogłem używać klasy std :: regex na Androidzie, więc musiałem napisać własne specjalistyczne procedury szukające podziałów akapitów lub tagów HTML. Moje pierwsze testy tego samego kodu na komputerze PC przy użyciu wyrażenia regularnego były około 4 do 5 razy szybsze niż Java.
Uff! Ponownie budząc surową pamięć wskaźnikami char * lub wchar *, od razu poczułem się 20 lat młodszy! :)
Edytuj 15 lipca 2013 r
(Zobacz poniżej, z edycjami 30.07.2013, aby uzyskać znacznie lepsze wyniki z Dot42)
Z pewnym trudem udało mi się przenieść moje testy C # na Dot42 (wersja 1.0.1.71 beta), kolejną platformę C # dla Androida. Wstępne wyniki pokazują, że kod Dot42 jest około 3 razy (3 razy) wolniejszy niż Xamarin C # (wer. 4.7.11) na emulatorze Intel Android. Jednym z problemów jest to, że klasa System.Text.RegularExpressions w Dot42 nie ma funkcji Split (), której użyłem w testach Xamarin, więc zamiast tego użyłem klasy Java.Util.Regex i Java.Util.Regex.Pattern.Split () , więc w tym konkretnym miejscu w kodzie istnieje niewielka różnica. Nie powinno to stanowić dużego problemu. Dot42 kompiluje się do kodu Dalvik (DEX), więc natywnie współpracuje z Javą na Androidzie, nie potrzebuje kosztownego współdziałania z C # do Javy jak Xamarin.
Dla porównania uruchamiam również test na urządzeniach ARM - tutaj kod Dot42 jest „tylko” 2x wolniejszy niż Xamarin C #. Oto moje wyniki:
HTC Nexus One Android 2.3.7 (ARM)
Java: Całkowity czas całkowity (5 uruchomień): 12187 ms, z całkowitym odczytem pliku: 13200 ms
Xamarin C #: całkowity czas całkowity (5 uruchomień): 13935 ms, całkowity odczyt pliku: 14465 ms
Dot42 C #: Całkowity czas całkowity (5 uruchomień): 26000 ms, z całkowitym odczytem pliku: 27168 ms
Samsung Galaxy Note 2, Android 4.1.1 (ARM)
Java: Całkowity czas całkowity (5 uruchomień): 6895 ms, całkowity odczyt pliku: 7275 ms
Xamarin C #: całkowity czas całkowity (5 uruchomień): 6466 ms, całkowity odczyt pliku: 6720 ms
Dot42 C #: Całkowity czas całkowity (5 uruchomień): 11185 ms, z całkowitym odczytem pliku: 11843 ms
Emulator Intel, Android 4.2 (x86)
Java: Całkowity czas całkowity (5 uruchomień): 2389 ms, całkowity odczyt pliku: 2770 ms
Xamarin C #: Całkowity czas całkowity (5 uruchomień): 1748 ms, całkowity odczyt pliku: 1933 ms
Dot42 C #: Całkowity czas całkowity (5 uruchomień): 5150 ms, całkowity odczyt pliku: 5459 ms
Dla mnie interesujące było również zauważyć, że Xamarin C # jest nieco szybszy niż Java na nowszym urządzeniu ARM i nieco wolniejszy na starym Nexus One. Jeśli ktoś chciałby również uruchomić te testy, daj mi znać, a zaktualizuję źródła na GitHub. Szczególnie interesujące byłoby zobaczyć wyniki z prawdziwego urządzenia z Androidem z procesorem Intel.
Aktualizacja 26.07.2013
Tylko szybka aktualizacja, ponownie skompilowana przez aplikacje testowe z najnowszą wersją Xamarin.Android 4.8, a także z wydaną dzisiaj aktualizacją dot42 1.0.1.72 - bez istotnych zmian w porównaniu z poprzednimi wynikami.
Aktualizacja 30.07.2013 - lepsze wyniki dla dot42
Ponownie przetestowałem Dot42 z portem Roberta (od twórców dot42) mojego kodu Java do C #. W moim porcie C # wykonanym początkowo dla Xamarin zastąpiłem niektóre rodzime klasy Java, takie jak ListArray, klasą List rodzimą w C # itp. Robert nie miał mojego kodu źródłowego Dot42, więc przeniósł go ponownie z Javy i użył oryginalnych klas Java w takie miejsca, z których korzysta Dot42, tak sądzę, ponieważ działa w Dalvik VM, jak Java, a nie w Mono, jak Xamarin. Teraz wyniki Dot42 są znacznie lepsze. Oto dziennik z moich testów:
30.07.2013 - Testy Dot42 z większą liczbą klas Java w Dot42 C #
Emulator Intel, Android 4.2
Dot42, kod Grega używający StringBuilder.Replace () (jak w Xamarin):
Całkowity czas całkowity (5 uruchomień): 3646 ms, całkowity odczyt pliku: 3830 msDot42, kod Grega za pomocą String.Replace () (jak w Javie i kodzie Roberta):
Całkowity czas całkowity (5 uruchomień): 3027 ms, całkowity odczyt pliku: 3206 msDot42, Kod Roberta:
Całkowity czas całkowity (5 uruchomień): 1781 ms, z całkowitym odczytem pliku: 1999 msXamarin:
całkowity czas całkowity (5 uruchomień): 1373 ms, całkowity odczyt pliku: 1505 msJava:
Całkowity czas całkowity (5 uruchomień): 1841 ms, całkowity odczyt pliku: 2044 msARM, Samsung Galaxy Note 2, wyłączanie oszczędzania energii, Android 4.1.1
Dot42, kod Grega używający StringBuilder.Replace () (jak w Xamarin):
Całkowity czas całkowity (5 uruchomień): 10875 ms, przy całkowitym odczycie pliku: 11280 msDot42, kod Grega za pomocą String.Replace () (jak w Javie i kodzie Roberta):
Całkowity czas całkowity (5 uruchomień): 9710 ms, z całkowitym odczytem pliku: 10097 msDot42, kod Roberta:
Całkowity czas całkowity (5 przebiegów): 6279 ms, całkowity odczyt pliku: 6622 msXamarin:
całkowity czas całkowity (5 uruchomień): 6201 ms, całkowity odczyt pliku: 6476 msJava:
Całkowity czas całkowity (5 uruchomień): 7141 ms, całkowity odczyt pliku: 7479 ms
Nadal uważam, że Dot42 ma przed sobą długą drogę. Posiadanie klas podobnych do Java (np. ArrayList) i dobra wydajność z nimi ułatwiłoby przenoszenie kodu z Java na C #. Jest to jednak coś, czego nie zrobiłbym wiele. Wolałbym użyć istniejącego kodu C # (bibliotek itp.), Który będzie używał natywnych klas C # (np. List), i który działałby wolno z bieżącym kodem dot42 i bardzo dobrze z Xamarin.
Greg