Comme beaucoup d'autres développeurs, j'ai été très enthousiasmé par le nouveau langage Swift d'Apple. Apple a affirmé que sa vitesse est plus rapide que l'objectif C et peut être utilisée pour écrire le système d'exploitation. Et d'après ce que j'ai appris jusqu'à présent, c'est un langage de type statique et capable d'avoir un contrôle précis sur le type de données exact (comme la longueur entière). Il semble donc avoir un bon potentiel de gestion des tâches critiques de performance, comme le traitement d'image , droite?
C'est ce que je pensais avant de faire un test rapide. Le résultat m'a vraiment surpris.
Voici un simple extrait de code en 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;
}
Je l'ai compilé sur mon Macbook Air 2011 avec la commande suivante:
clang -O3 test.c -o test
Le temps de traitement de 10 images est d'environ 0,01 s. En d'autres termes, il faut le code C 1 ms pour traiter une trame:
$ time ./test
real 0m0.010s
user 0m0.006s
sys 0m0.003s
Ensuite, j'ai une version Swift du même code:
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)
}
La ligne de commande de génération est:
xcrun Swift -O3 test.Swift -o test
Ici, j'utilise le même O3
indicateur d'optimisation de niveau pour rendre la comparaison, espérons-le, juste. Cependant, la vitesse résultante est 100 fois plus lente:
$ time ./test
real 0m1.172s
user 0m1.146s
sys 0m0.006s
En d'autres termes, cela prend Swift ~ 120ms pour traiter une trame qui prend C seulement 1 ms.
Qu'est-il arrivé?
$ 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
Voici le résultat pour un nombre différent de "trames", c'est-à-dire changer le numéro de boucle principal for
de 10 à d'autres nombres. Notez maintenant que je reçois un temps de code C encore plus rapide (cache chaud?), Tandis que le temps Swift ne change pas trop:
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
Avec -Ofast
suggérée par @mweathers, la vitesse Swift monte jusqu'à une plage raisonnable.
Sur mon ordinateur portable, la version Swift avec -Ofast
obtient 0,013 s pour 10 images et 0,048 s pour 100 images, soit près de la moitié des performances C.
Construire avec:
xcrun Swift -Ofast test.Swift -o test
Je reçois des moments de:
real 0m0.052s
user 0m0.009s
sys 0m0.005s
Concentrons-nous simplement sur la réponse à la question, qui a commencé par un "Pourquoi": Parce que vous n'avez pas activé les optimisations, et Swift s'appuie fortement sur l'optimisation du compilateur.
Cela dit, faire du traitement d'image en C est vraiment idiot. C'est pour cela que vous avez CGImage et vos amis.