• [^] # Re: Performance

    Posté par . En réponse au journal Koffi, un paquet simple, complet et rapide de FFI C pour Node.js. Évalué à 3.

    Il y a une page dédiée aux benchmarks sur le site, avec 3 tests implémentés en plusieurs versions :

    1. rand(): ce benchmark est basé sur des millions d'appels à rand(), compare un binding maison (qui utilise N-API) et Koffi (et node-ffi-napi, tellement catastrophique que je ne le mets que dans tes tableaux et pas les graphiques sinon on ne voit plus rien...). On est sur du +70% à +80% de temps d'exécution, donc même pas un doublement, et il s'agit d'une fonction C très simple donc l'overhead est majoré.
    2. atoi(): ce benchmark est similaire au précédent, mais basé sur atoi() pour mesurer les performances de passage de chaine.
    3. Raylib: ce benchmark est assez différent, il est basé sur Raylib (librairie C pour faire de la programmation de jeux vidéos). Ici 3 implémentations (sans compter node-ffi-napi) sont comparées : une version entièrement en C++, une version basée sur le paquet node-raylib (qui est un binding maison) et une version basée sur Koffi. Les fonctions Raylib font plus de boulot que les tests précédents, et là on est sur +20% de temps d'exécution par rapport au binding N-API.

    Performance Linux

    Les deux premiers permettent de révèler l'overhead lié à Koffi puisqu'il s'agit de fonctions très simples, notamment rand(), mais par contre ce n'est pas un cas très réaliste d'utilisation. Le troisième est plus réaliste par rapport à l'utilisation réelle d'un module FFI, avec un overhead qui est relativement tolérable.

    Pour résumer, l'overhead sur les deux premiers benchmarks (appels d'une fonction C très rapide) est de +70 à +80% par rapport au binding N-API, sur le troisième c'est +20% par rapport au binding N-API node-raylib. Pour comparaison, node-ffi-napi c'est +7000% sur atoi(), sans parler d'une explosion de la consommation mémoire (plusieurs Go) liée au GC qui ne réagit pas.

    Les appels synchrones ont lieu sur le thread principal, les appels asynchrones sont exécutés par un pool de threads (worker threads).