Warum ist Swift in diesem Bildverarbeitungstest 100 Mal langsamer als C? [duplicate]

Lesezeit: 4 Minuten

Benutzeravatar von Penghe Geng
Penghe Geng

Wie viele andere Entwickler war ich sehr gespannt auf die neue Swift-Sprache von Apple. Apple hat behauptet, dass seine Geschwindigkeit schneller als Objective C ist und zum Schreiben von Betriebssystemen verwendet werden kann. Und nach dem, was ich bisher gelernt habe, ist es eine statisch typisierte Sprache und in der Lage, den genauen Datentyp (wie die Ganzzahllänge) genau zu kontrollieren. Es sieht also so aus, als hätten Sie ein gutes Potenzial für leistungskritische Aufgaben wie die Bildverarbeitung, oder?

Das dachte ich mir, bevor ich einen Schnelltest durchführte. Das Ergebnis hat mich wirklich überrascht.

Hier ist ein einfaches Code-Snippet in C:

test.c:

#include <stdio.h>
#include <stdint.h>
#include <string.h>

uint8_t pixels[640*480];
uint8_t alpha[640*480];
uint8_t blended[640*480];

void blend(uint8_t* px, uint8_t* al, uint8_t* result, int size)
{
    for(int i=0; i<size; i++) {
        result[i] = (uint8_t)(((uint16_t)px[i]) *al[i] /255);
    }
}

int main(void)
{
    memset(pixels, 128, 640*480);
    memset(alpha, 128, 640*480);
    memset(blended, 255, 640*480);

    // Test 10 frames
    for(int i=0; i<10; i++) {
        blend(pixels, alpha, blended, 640*480);
    }

    return 0;
}

Ich habe es auf meinem Macbook Air 2011 mit dem folgenden Befehl kompiliert:

clang -O3 test.c -o test

Die 10-Frame-Verarbeitungszeit beträgt etwa 0,01 s. Mit anderen Worten, der C-Code benötigt 1 ms, um einen Frame zu verarbeiten:

$ time ./test
real    0m0.010s
user    0m0.006s
sys     0m0.003s

Dann habe ich eine Swift-Version des gleichen Codes:

test.swift:

let pixels = UInt8[](count: 640*480, repeatedValue: 128)
let alpha = UInt8[](count: 640*480, repeatedValue: 128)
let blended = UInt8[](count: 640*480, repeatedValue: 255)

func blend(px: UInt8[], al: UInt8[], result: UInt8[], size: Int)
{
    for(var i=0; i<size; i++) {
        var b = (UInt16)(px[i]) * (UInt16)(al[i])
        result[i] = (UInt8)(b/255)
    }
}

for i in 0..10 {
    blend(pixels, alpha, blended, 640*480)
}

Die Build-Befehlszeile lautet:

xcrun swift -O3 test.swift -o test

Hier benutze ich das gleiche O3 Level-Optimierungs-Flag, um den Vergleich hoffentlich fair zu gestalten. Die resultierende Geschwindigkeit ist jedoch 100-mal langsamer:

$ time ./test

real    0m1.172s
user    0m1.146s
sys     0m0.006s

Mit anderen Worten, Swift benötigt ~120 ms, um einen Frame zu verarbeiten, was C nur 1 ms dauert.

Was ist passiert?

Update: Ich verwende Clang:

$ gcc -v
Configured with: --prefix=/Applications/Xcode6-Beta.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 6.0 (clang-600.0.34.4) (based on LLVM 3.5svn)
Target: x86_64-apple-darwin13.2.0
Thread model: posix

Update: mehr Ergebnisse mit verschiedenen laufenden Iterationen:

Hier sind die Ergebnisse für unterschiedliche Anzahl von “Frames”, dh die wichtigsten ändern for Loop-Nummer von 10 zu anderen Nummern. Beachten Sie, dass ich jetzt eine noch schnellere C-Code-Zeit bekomme (Cache heiß?), Während sich die Swift-Zeit nicht zu sehr ändert:

             C Time (s)      Swift Time (s)
  1 frame:     0.005            0.130
 10 frames(*): 0.006            1.196
 20 frames:    0.008            2.397
100 frames:    0.024           11.668

Update: `-Ofast` hilft

Mit -Ofast vorgeschlagen von @mweathers, die Swift-Geschwindigkeit steigt in einen vernünftigen Bereich.

Auf meinem Laptop die Swift-Version mit -Ofast erhält 0,013 s für 10 Frames und 0,048 s für 100 Frames, fast die Hälfte der C-Leistung.

  • Hilft es aus Neugier, die Mischungsberechnung durch zu ersetzen var b = (UInt16)(px[i]) &* (UInt16)(al[i])was, wenn ich die Dokumente richtig lese, dazu führt, dass Swift die Überlaufprüfung vermeidet?

    – Rici

    8. Juni 2014 um 3:36 Uhr

  • Was passiert, wenn Sie den Code so anpassen, dass er denselben Prozess zweimal durchführt (dh Iterationen von 10 auf 20 erweitert)? Ich könnte mir vorstellen, dass das Starten der Swift-Laufzeit etwas mehr kostet als das Starten der C-Laufzeit.

    – Tommi

    8. Juni 2014 um 3:52 Uhr


  • können Sie den Assembler-Code ausgeben? Meine Vermutung ist, dass die Clang-Version möglicherweise die Division durch Konstante 255 optimiert

    – blutig

    8. Juni 2014 um 3:53 Uhr

  • Versuchen Sie, nur das Profil zu erstellen blend Funktion. Das Füllen des Arrays und Unterschiede in der Umgebungskonfiguration spielen wahrscheinlich eine Rolle.

    – Rechnung

    8. Juni 2014 um 3:58 Uhr

  • Wenn man den gesamten Link liest, kann man sehen, dass „Ändern des Swift-Compilers – Optimierungsstufe in Xcode auf ‚Schnellste, nicht überprüft‘ dies beschleunigt hat, um mit Ihrem C++ vergleichbar zu sein.“

    – Jim Balter

    8. Juni 2014 um 4:21 Uhr

Bauen mit:

xcrun swift -Ofast test.swift -o test

Ich erhalte Zeiten von:

real    0m0.052s
user    0m0.009s
sys 0m0.005s

  • @JeremyBanks: -Ofast ändert die Semantik der Sprache. Es ist nicht sicher. Sie verwandeln Swift in eine C++-ähnliche Sprache. Ganzzahlüberläufe, Arrayüberläufe usw. werden stillschweigend ignoriert.

    – Jukka Suomela

    8. Juni 2014 um 8:43 Uhr

  • Weitere Beispiele für die Auswirkungen von -Ofast hier: stackoverflow.com/questions/24101718/…

    – Jukka Suomela

    8. Juni 2014 um 9:17 Uhr

  • @grasGendarme: Die Überprüfung der Array-Grenze funktioniert nicht bedeutet eine Verlangsamung um den Faktor 100 (es sollte eher ein Faktor << 2 sein). Vgl. Java vs. C.

    – Jukka Suomela

    8. Juni 2014 um 11:33 Uhr


  • @JukkaSuomela Die Array-Bound-Prüfung kann mit Sicherheit eine 100-fache Verlangsamung in einer solchen inneren Schleife mit über einer Million Iterationen erklären

    – blutig

    8. Juni 2014 um 17:50 Uhr

  • @gordy: Siehe dies für einige Vergleiche von Swift vs. Java vs. Python vs. C++. Und denken Sie daran, dass Java auch Grenzen überprüft (und mehr – es muss auch nach einem Nullzeiger suchen).

    – Jukka Suomela

    8. Juni 2014 um 19:07 Uhr


Konzentrieren wir uns nur auf die Antwort auf die Frage, die mit einem „Warum“ begann: Weil Sie keine Optimierungen eingeschaltet haben und Swift stark auf Compiler-Optimierung setzt.

Allerdings ist die Bildverarbeitung in C wirklich dumm. Dafür gibt es CGImage und seine Freunde.

1412800cookie-checkWarum ist Swift in diesem Bildverarbeitungstest 100 Mal langsamer als C? [duplicate]

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy