Hier nous vous parlions d’une nouvelle bibliothèque pour parser du JSON en Swift : Freddy. Nous l’avons comparez à SwiftyJSON et la bibliothèque native NSJSONSerialization
d’un point de vue fonctionnel, mais il nous restait à comparer les performances entre ces 3 librairies. En effet, Freddy se définit comme plus rapide pour parser de gros fichier. C’est ce que nous allons vérifier maintenant.
Condition du test
Nous allons utiliser Xcode et faire tourner les tests de performances dans des XCTestCase
en utilisant le méthode fournit measureBlock
. Le projet qui sert de référence est sur github. Pour l’utiliser il vous faut cloner le repo, et l’initialiser avec Cocoapod :
$ git clone git@github.com:swifttuto/JSONComparator.git
$ cd JSONComparator
$ pod install
$ open JSONComparator.xcworkspace
Pour les tests il nous faut tout d’abords les donnée d’entrées. On va générer un JSON plus ou moins gros en utilisant cette méthode dans notre test :
class JSONComparatorTests: XCTestCase {
static let iterationCount = 100000
static var data: NSData? = nil
override class func setUp() {
var persons: [[String: AnyObject]] = []
for i in 0 ..< iterationCount {
let person: [String: AnyObject] = [
"firstname": "Firstname \(i)",
"lastname": "Lastname \(i)",
"age": i
]
persons.append(person)
}
do {
data = try NSJSONSerialization.dataWithJSONObject(persons, options: .PrettyPrinted)
}
catch {}
}
}
La méthode de classe setUp
est appelé une seule fois lors des tests et elle permet de générer un JSON
sous forme de NSData
prêt à être parser par les différentes bibliothèques. Le JSON
générer est simplement une liste de personne contenant 3 attributs chacun (un nom, un prénom et un age). Nous pouvons faire varier le nombre d’entré dans le JSON
en changeant la variable iterationCount
.
Les tests
Voici les méthodes qui vont nous servir de comparaison :
func testNativePerformance() {
guard let data = JSONComparatorTests.data else {
return XCTFail("Data should not be empty")
}
self.measureBlock {
do {
let _ = try NSJSONSerialization.JSONObjectWithData(data, options: [])
}
catch let error {
XCTFail("\(error)")
}
}
}
func testSwiftyJSONPerformance() {
guard let data = JSONComparatorTests.data else {
return XCTFail("Data should not be empty")
}
self.measureBlock {
let _ = SwiftyJSON.JSON(data: data)
}
}
func testFreddyPerformance() {
guard let data = JSONComparatorTests.data else {
return XCTFail("Data should not be empty")
}
self.measureBlock {
do {
let _ = try Freddy.JSON(data: data)
} catch {
XCTFail("\(error)")
}
}
}
Pour chacun des librairies on ne fait que parser le JSON
générer. Il ne devrait pas y avoir beaucoup de différence entre SwiftyJSON
et NSJSONSerialization
car le premier utilise le deuxième comme parser.
Veuillez aussi noter que les tests sont exécutés sur le simulateur en mode Release.
Les résultats
Le parsing
Voici les résultats d’un parsing avec un JSON de 100 entrées :
- Freddy : 0.271 secondes
- NSJSONSerialization : 0.259 secondes
- SwiftyJSON : 0.259 secondes
Comme on pouvait s’y attendre SwiftyJSON
et NSJSONSerialization
sont équivalent en performance, et Freddy
accuse un petit coup de retard (1,05 fois plus lent).
Essayons maintenant avec 10000 entrées :
- Freddy : 0.764 secondes
- NSJSONSerialization : 0.556 secondes
- SwiftyJSON : 0.554 secondes
Les différences se creusent un peu, maintenant Freddy
est 1,37 fois plus lent que NSJSONSerialization
.
Et avec 1 000 000 d’entrées ?
- Freddy : 49.539 secondes
- NSJSONSerialization : 27.506 secondes
- SwiftyJSON : 27.991 secondes
Encore pire ! Freddy
est plus de 1,8 fois plus lent que NSJSONSerialization
.
Le downcasting
Ce que nous avions oublié de comparer la première fois c’est le downcasting. En effet les JSON
retournés sont des AnyObject
, c-à-d que l’on ne connait pas leur type. Afin de récupérer un String
, un Int
, un Dictionary
ou n’importe quel autre type il faut downcaster le AnyObject
vers le type correspondant. Cette opération à un coût non négligeable et sur des ensemble de données importants cela se ressent beaucoup.
Je vous renvoie vers le benchmark fournit par Freddy
qui montre qu’il devance et largement les NSJSONSerialization
et SwiftyJSON
.
Conclusion
Pour conclure Freddy apporte bien de gros gain de performance sur la partie downcasting qui est la partie la plus chronophage lorsque l’on manipule du JSON
. De plus Freddy est une bibliothèque très propre, très bien conçu qui peut être utiliser sans hésitation. Sur des petits ensembles de données vous pouvez utiliser n’importe quel librairie car il y aura peu d’impact sur vos performance, en revanche sur de gros ensemble utilisez Freddy !
Si vous avez des remarques, amélioration ou autre, n’hésitez pas à laisser des commentaires.
À bientôt
Edit :
Sur les précisions de John Gallagher, ce post a été mis à jour en faisant les tests en mode Release plutôt qu’en mode Debug. De plus comme il l’explique dans son commentaire le parsing du JSON
n’est qu’une petite partie de son utilisation. Accéder aux éléments de celui ci est une autre grosse partie et il est très gourmand en ressource.
Disclaimers: I’m one of the authors of Freddy, and I don’t speak French so I’m hoping Google translate did an OK job with your post. 🙂
There are a couple of issues with the benchmarks here. The biggest is that it appears your timing results were run with the project compiled in Debug mode. This severely slows down Freddy (since it’s pure Swift) but doesn’t slow down native/SwiftyJSON at all since they’re using the shipped, release-mode-compiled version of NSJSONSerialization. I ran your benchmarks in release mode, and on my machine, Freddy was a little worse than 2x slower than native. This is very close to our own measurements (https://github.com/bignerdranch/Freddy/wiki/JSONParser); it’s obviously not as fast as we’d like, but it’s much better than being 10x slower.
The second issue is that pure parsing is only part of the story. One of the slowest parts of using NSJSONSerialization from Swift (as SwiftyJSON does) is the runtime downcast from AnyObject to determine which JSON type a particular value is. This benchmark does not include that step, but it’s a step that will be required in any real-world use of SwiftyJSON. If you have a JSON document where you only need to extract a small portion of it, SwiftJSON may very well still be faster, but the difference will close as you need to get more and more of the data out of the JSON.
No prob, Google is smart enough to translate from French to English into a intelligible text.
Indeed I forgot the Debug vs Release mode. I didn’t know that
NSJSONSerialiation
was a release-mode-compiled version, so yes I understand the difference between both.Of course, I forgot this part in this post, I’m going to update it to point on your benchmark and explain this part. You have convinced me!
Thank you for taking time to bring these clarifications! That’s great.