C: Pourquoi un fprintf (stdout,…) est-il si lent?

J’utilise encore souvent la sortie de la console pour avoir des idées sur ce qui se passe dans mon code. Je sais que c’est peut-être un peu à la mode, mais je l’utilise également pour “canaliser” la sortie standard dans les fichiers journaux, etc.

Cependant, il s’avère que la sortie vers la console est ralentie pour une raison quelconque. Je me demandais si quelqu’un pouvait expliquer pourquoi un fprintf () vers une fenêtre de console semble être en quelque sorte bloquant.

Ce que j’ai fait / diagnostiqué jusqu’à présent:

  1. J’ai mesuré le temps simple fprintf(stdout,"quick fprintf\n"); Il a besoin de: 0.82ms (en moyenne). Cela est considéré comme trop long, car un vsprintf_s(...) écrit la même sortie dans une chaîne en quelques microsecondes. Par conséquent, il doit y avoir un blocage spécifique à la console.

  2. Afin d’échapper au blocage, j’ai utilisé vsprintf_s(...) pour copier ma sortie dans une structure de données identique à celle de fifo. La structure de données est protégée par un object de section critique. Un thread séparé décompresse alors la structure de données en plaçant la sortie en queue sur la console.

  3. Une amélioration supplémentaire pourrait être obtenue par l’introduction de services de canalisations. La sortie de mon programme (censé se retrouver dans une fenêtre de console) se présente comme suit:

    • vsprintf_s(...) formate la sortie en chaînes simples.
    • Les chaînes sont mises en queue dans une structure de données identique à celle de fifo, une structure de liste liée par exemple. Cette structure de données est protégée par un object de section critique.
    • Un deuxième thread supprime la queue de la structure de données en envoyant les chaînes de sortie à un canal nommé.
    • Un deuxième processus lit le canal nommé et place les chaînes à nouveau dans une structure de données identique à celle de fifo. Cela est nécessaire pour que la lecture rest à l’écart de la sortie bloquante vers la console. Le processus de lecture est rapide à la lecture du tuyau nommé et surveille le niveau de remplissage du tampon de tuyaux en continu.
    • Un deuxième thread de ce second processus élimine enfin la structure de données en fprintf(stdout,...) à la console.

J’ai donc deux processus avec au moins deux threads chacun, un tuyau nommé entre eux, et quinze structures de données identiques, des deux côtés du tuyau, afin d’éviter tout blocage en cas de saturation du tampon de tuyau.

C’est beaucoup de choses pour s’assurer que la sortie de la console est “non bloquante”. Mais le résultat n’est pas trop mauvais. Mon programme principal peut écrire un fprintf complexe (stdout, …) en quelques microsecondes.

J’aurais peut-être dû demander plus tôt: existe-t-il un autre moyen (plus simple!) De générer une sortie de console non bloquante?

Je pense que le problème de synchronisation est lié au fait que la console est mise en mémoire tampon de ligne par défaut. Cela signifie que chaque fois que vous écrivez un caractère '\n' , tout le tampon de sortie est envoyé à la console, ce qui représente une opération assez coûteuse. C’est le prix que vous payez pour que la ligne apparaisse immédiatement dans la sortie.

Vous pouvez modifier ce comportement par défaut en modifiant la stratégie de mise en mémoire tampon en mise en mémoire tampon complète . La conséquence est que la sortie sera envoyée à la console sous forme de morceaux égaux à la taille de votre tampon, mais que les opérations individuelles se termineront plus rapidement.

Effectuez cet appel avant d’écrire pour la première fois sur la console:

 char buf[10000]; setvbuf(stdout, buf, _IOFBF, sizeof(buf)); 

La synchronisation des écritures individuelles devrait s’améliorer, mais la sortie n’apparaîtra pas immédiatement dans la console. Ce n’est pas très utile pour le débogage, mais le timing sera amélioré. Si vous configurez un fil qui appelle fflush(stdout) à des intervalles de temps réguliers, une fois par seconde, vous devez obtenir un équilibre raisonnable entre les performances des écritures individuelles et le délai entre le moment où votre programme écrit la sortie et le moment où vous le pouvez. effectivement le voir sur la console.