Test sur manipulation de pixels
Mala
Membre, Modérateur
Le post "Swift et types de base" a un peu largement glissé sur le questionnement des perfs qu'on peut attendre avec Swift. Dans le domaine qui m'intéresse par rapport à mes logiciels de traitement d'image, la manipulation de buffers de pixel est un sujet sensible. Je me suis donc fait la main sur un cas d'école: ajustement des seuils min et max d'une image.
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
Histoire de suivre un peu l'évolution de Swift, je viens de mettre au goût du jour mon projet de test pour Xcode 6...
UInt8 vers float et float vers UInt8, (et Float vers int lors du test <255).
Pour éviter les conversions superflues, je préciserais le type de newValue.
Et, comme type, j'essaierais les types UInt8 et Int.
La conversion float vers Int est sans doute moins violente que Float vers UInt8.
EDIT: en fait j'éviterais carrément le passage par le type Float.
Pas mal d'optimisations/stabilisation dans la nouvelle version de Swift 1.2, peut être qu'il vaut mieux refaire les tests avec la dernière version.
Télécharger Xcode 6.3 Beta et recommencer le test !
EDIT : Grilled par Samir ..
Oui, je viens de voir ça pour Swift 1.2. On va voir ce que ça donne.
Je viens de tenter vite fait (pas encore sous Xcode 6.3). C'est un peu mieux mais c'est encore loin d'être ça avec 14 Fps.
Le soucis c'est que les float sont très utiles en traitement d'image. C'est typiquement le cas de figure où le passage en entier peu générer des effets de crénelage sur des dégradés subtiles.
On peux utiliser de vrais types C/Obj-C en Swift maintenant? Si c'est le cas tu m'intéresses car c'est justement là le fond du problème pour accéder aux pixels de manière efficace.
Moi j'en suis resté à ça...
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithCAPIs.html
Téléchargement d'Xcode 6.3 en cours...
C'est pas conseillé si tu veux faire du pur Swift bien sûr, mais si tu as besoin d'inter-opérabilité entre Swift et ObjC c'est tout à fait possible (depuis la toute première version de Swift, pas besoin d'attendre la fin du download de Xcode 6.3 ^^)
Je pense qu'on ne se comprend pas. Pourquoi me parles-tu de NSString et de string? Je te parles d'accéder à des types C de base et non à des objets.
CGFloat en C, c'est juste un type C redéfinissant un nombre réel en double précision sur architecture 64bits et restant en simple précision sur architecture 32bits. En Swift ça devient un objet à part entière avec toute la lourdeur que cela entraine.
Hors de ce que je vois en l'état, tous les accès à des données brutes d'octets (getBytes de NSData aussi par exemple) passent par des conversions avec Swift et c'est extrêmement pénalisant.
On peut utiliser quasi tous les types de base qu'on a en ObjC " que ce soit des types C comme float/double/uint8_t/... ou des types ObjC comme NSString " depuis Swift. On peut utiliser CGFloat aussi depuis Swift, et ce sera alors un CGFloat au même sens qu'en C/ObjC, c'est à dire un float en 32 bits et double en 64 bits, tout pareil.
Non justement pas tout pareil. En Swift CGFloat est un objet et en C c'est juste un espace mémoire.
En Swift...
En C/Obj-C
S'ils ont fonctionnellement le même rôle, c'est juste le jour et la nuit en terme d'accès.
@Mala: Ne te fie pas aux pseudo header que tu as avec cmd+click dans Xcode. Ils sont générés automatiquement pour des raisons pratiques et pour dire au compilo comment appréhender le code. Les implémentations qui sont derrière restent écrites en C++. Cocoa et Fondation n'ont pas bougé d'un iota vis-à -vis de Swift (source: lu de Chris Lattner sur le dev-forum, mais pour retrouver ça...)
En changeant tous les Double en CGFloat et en modifiant un peu la fonction redraw j'ai ~35 FPS sur un MBP 13" mid 2010 (une machine de guerre quoi...). Le tout en 1.2 -Ofast .
Niveau utilisation ça donne ça :
Arggg, les salopards!!!
C'est l'occasion de passer à Yosemite, non ?
J'évite d'aller trop vite sur ma config de prod. Je viens de m'installer une partition pour les tests.
Ca j'entends bien que c'est du simili bridging automatisé pour les classes. Cela ne me choque pas et c'est même logique. Mais dès qu'on touche aux types de bases ce n'est plus la même.
Bon sinon je viens de tester sous Yosemite et dernière Beta 6.3 d'Xcode:
Float: 14,34 Fps
CGFloat: 12,3 Fps (logique on perd en perfs en double)
Donc c'est un peu mieux. On revient à peu près aux perfs de l'idée de FKDEV de passer en Int (voir un chouilla mieux) mais pour l'instant c'est toujours une belle branlée d'un facteur 20x en faveur du code C...
Quelques observations en testant ton code:
- Le projet n'est pas en double mais bien en float. On utilise d'ailleurs très rarement des double en traitement d'image car on perd en perfs pour un gain en précision qui ne se justifie plus à ce stade.
- Ta modification de mon clamping en factorisant en opérateur ternaire fait perdre presque 1Fps chez moi.
- uint_fast8_t() semble équivalent en performance à CUnsignedChar() chez moi.
Oui effectivement, maintenant est-ce que ça a une influence particulière sur un os et un cpu 64bit ? (je me pose réellement la question, hein)
Le clamping fait comme ça me fait gagner 1 fps chez moi, ça doit dépendre du proc (j'ai un Core2Duo @ 2.4Ghz). Et instruments me donnait moins de % d'utilisation pour cette ligne en particulier que la somme de l'utilisation des boucles if.
uint_fast8_t est strictement identique à CUnsignedChar et UInt8 (typealias)
D'expérience c'est de l'ordre de 6 à 10% de pertes sur mes algos de traitement de signal. Ce qu'Apple a bien oublié de dire en nous vendant le passage au 64bits c'est que le gain n'était réel que pour des algos déjà entièrement en double précision. Hors d'une part, peu de calculs justifient une telle précision et d'autre part les mêmes algos en simple précision sont toujours plus performant...
L'usage de CGFloat est vraiment un non sens selon moi de par son ambiguà¯té 32/64bits.
Vous n'avez pas reparlé de la grosse différence entre le premier test swift et les tests suivants avec Xcode 6, qui sont beaucoup plus lents. N'est-ce pas étrange ? Cela voudrait-il dire que le nouveau swift est moins abouti que le précédent ?
Apple nous a vendu swift comme beaucoup plus rapide. D'après les tests, il semble que ce soit faux. Si on considère que swift est une langage plus haut niveau que objective-c (tout est objet, si j'ai bien compris), c'est logique que ce soit moins performant.
Finalement, ce qu'on doit attendre de swift ce ne sont pas les perfs mais le confort dans le code, non ?
Plus que le confort, je dirais la sécurité. Mais elle a un prix. En même temps, est-ce illogique que dans les faits Swift perde en performances à mesure qu'il se sécurise? Après nul doute, qu'ils font les benchs qu'ils veulent comme toujours. Moi je me contente de cas concrets qui me concernent. Je ne suis sans doute pas représentatif de la masse.
Il ne faut pas non plus oublier qu'Apple compare Swift à l'Obj-C et non au C... Vous avez dit hypocrisie de commercial? Ah non c'est moi qui pense trop fort pardon. ;D
Maintenant la question à se poser c'est si il faut utiliser un langage haut niveau pour des opérations bas niveau. Pas sûr.
J'ai commencé à m'intéresser aux parser, vu les premiers tests swift est out.
Je coderai le lexer/parser en C. Le type-safety coûte trop cher quand on a un texte de 100000 caractères unicode.
- Une fois que tu as compilé ton code Swift, tous les symboles sont déjà résolus.
- Alors qu'en Objective-C, c'est hautement dynamique. Ce ne sont pas des appels de fonctions, mais des envois de message. Avec une indirection de dispatch-table supplémentaire, une résolution des messages dynamique. Ca offre des possibilités en + (on peut imaginer construire dynamiquement une NSString contenant le nom d'une méthode, la convertir en @selector, et appeler ce dernier, par exemple), mais de la sécurité ET de la performance en moins.
Là où Swift ajoute de la lenteur, c'est quand il n'est pas tout seul, mais qu'il est utilisé en corrélation avec Objective-C. Dans ce cas, non seulement il y a perte de performances à cause du bridging et du passage d'un monde à l'autre, mais en plus aussi une perte à cause du passage par le monde dynamique en injectant de l'Objective-C.
Le jour où Objective-C disparaà®tra et qu'on fera des applications Swift qui n'ont plus du tout de code dynamique (type implémentation ObjC) sous le capot, mais que des appels de fonctions directs et plus d'envoi de messages, la différence sera alors bien + flagrante.
Sauf que pour l'instant Swift ne sait toujours pas gérer tout seul un simple tableau d'octets de manière efficace. C'est une sacrée limitation dans bien des domaines. Espérons qu'Apple ait une vraie solution dans les cartons mais dans l'immédiat on doit faire avec ce qu'on a.
Du coup, je continue un peu mes investigations pour découvrir les possibilités du language. Swift permettant de faire du bridging de manière relativement transparente (ça c'est un sacré confort) entre Swift et Obj-C, j'ai décidé de tenter le coup pour voir.
D'abord avec une catégorie Obj-C appelée depuis le code Swift...