Tout en dépannant certains problèmes de performance dans nos applications, j'ai découvert que les fonctions Ce n'est pas bon pour la performance. P>
Quelle est la meilleure façon d'obtenir des fichiers d'E / S non threadsafe en C et C ++, afin que je puisse gérer moi-même et obtenir de meilleures performances? P>
Comment les autres abordent-ils cela? P>
STDIO.H CODE> STDIO.H (et, au moins pour notre fournisseur, C ++ 'S
FRStream code> classes) sont threadsafe . En conséquence, chaque fois que je fais quelque chose d'aussi simple que
fgetc code>, le RTL doit acquérir une serrure, lire un octet et libérer le verrouillage. P>
_fputc_nolock code>
, et GCC fournit déverrouillé_stdio code>
et flockfile code>
, mais je ne trouve aucune fonction similaire dans mon compilateur (CodeGear C ++ Builder ). li>
fgetc code> ne fait pas d'appels système s'il peut satisfaire à sa mémoire tampon, mais il fait toujours le verrouillage, le verrouillage finit donc de prendre un pourcentage énorme de temps (centaines de serrures à acquérir et à relâcher pour un bloc unique de données lisez du disque). Ne pas faire du caractère-AT-A-A-Time serait une solution, mais C ++ Builder's
FRStream CODE> Les classes utilisent malheureusement Fgec (donc si je souhaite utiliser
iostream code> classes , Je suis bloqué avec elle) et j'ai beaucoup de code hérité qui utilise
fgetc. / code> et amis pour lire des champs de fichiers de style record (ce qui serait raisonnable s'il n'y avait pas de verrouillage problèmes). p>
6 Réponses :
Je ne ferais tout simplement pas d'IO un caractère à la fois s'il s'agit d'une performance sensible sage. p>
Toutes les opérations aux flux aboutissent à Char Io. Vous devez travailler avec des tampons de flux. J'ai posté ci-dessous comment ...
fgetc ne lis presque certainement pas un octet chaque fois que vous l'appelez (où en "lisant", je veux dire invoquer un appel système pour effectuer des E / S). Regardez ailleurs pour votre goulot d'étranglement de la performance, car ce n'est probablement pas le problème et que vous utilisez des fonctions dangereuses n'est certainement pas la solution. Toute manipulation de verrouillage que vous faites sera probablement moins efficace que la manipulation effectuée par les routines standard. P>
Il ne lisait pas un octet à la fois, mais cela peut très bien prendre une serrure à chaque fois. BTW, prenant une serrure est obligatoire sous POSIX, il existe getc_unlocked () (et une variante _unlocked de Char par Char Io fonctions et des fonctions de verrouillage afin qu'elles puissent être protégées).
Le moyen le plus simple serait de lire l'ensemble du fichier en mémoire, puis de fournir votre propre interface de type Fget sur ce tampon. P>
Pourquoi pas seulement la carte mémoire du fichier? La cartographie de la mémoire est portable (sauf dans Windows Vista qui vous oblige à sauter dans l'espoir de l'utiliser maintenant, les dumbasses). Quoi qu'il en soit, cartographiez votre fichier en mémoire et êtes-vous propriétaire de verrouillage / pas-verrouillage sur l'emplacement de la mémoire résultant. p>
Le système d'exploitation gère tout le verrouillage requis pour lire réellement le disque - vous ne pourrez jamais éliminer cette surcharge. Mais votre traitement de traitement, de l'autre, ne sera pas affecté par un verrouillage étranger autre que celui que vous faites vous-même. p>
L'approche multi-plateformes est assez simple. Évitez les fonctions ou les opérateurs où la standard spécifie qu'elles doivent utiliser Sentry. Sentry est une classe interne dans des classes iostream qui garantit une cohérence des flux pour chaque caractère de sortie et dans un environnement multi-fileté, il verrouille le mutex relié au flux pour chaque caractère étant donné. Cela évite les conditions de race à bas niveau mais rend la sortie illisible, car les cordes de deux threads peuvent être émises simultanément comme l'exemple suivant indique:
Le fil 1 doit écrire: ABC
Le fil 2 devrait écrire: DEF P>
La sortie peut ressembler à: AdeBCF au lieu d'ABCDEF ou DEFABC. En effet, Sentry est implémenté pour verrouiller et déverrouiller par caractère. P>
La norme le définit pour toutes les fonctions et les opérateurs traitant de Istream ou d'Otstream. Le seul moyen d'éviter que ceci est d'utiliser des tampons de flux et de votre propre verrouillage (par chaîne par exemple). P>
J'ai écrit une application, qui produit des données à un fichier et mesurent la vitesse. Si vous ajoutez ici une fonction que Outputts utilisant le FRStream directement sans utiliser le tampon et la chasse, vous verrez la différence de vitesse. Il utilise Boost, mais j'espère que ce n'est pas un problème pour vous. Essayez de supprimer tous les streambuffers et de voir la différence avec et sans eux. I Mon cas L'inconvénient de la performance était le facteur 2-3 ou plus. P>
Le par N. Myers expliquera comment les locaux et Sentry dans C ++ iostreams fonctionnent. Et bien sûr, vous devriez rechercher le document standard ISO C ++, qui utilise Sentry. P>
bonne chance,
Ovanes p>
Une chose à considérer est de construire un runtime personnalisé. La plupart des compilateurs fournissent la source à la bibliothèque d'exécution (je serais surpris s'il n'y avait pas dans le package C ++ Builder). P>
Cela pourrait finir par avoir beaucoup de travail, mais peut-être qu'ils ont localisé le support de fil pour faire quelque chose comme cela facile. Par exemple, avec le compilateur système embarqué que j'utilise, il est conçu pour cela - ils ont des crochets documentés pour ajouter les routines de verrouillage. Cependant, il est possible que cela puisse être un mal de maintenance, même s'il s'avère être relativement facile d'abord. P>
Un autre itinéraire similaire serait de parler à une personne comme Dinkumware à propos de l'utilisation d'un tiers d'exécution de la partie qui fournit la Capacités dont vous avez besoin. p>
Les fonctions STDIO.H de C ne sont pas threadsafe; C'est votre fournisseur aussi.
Pas seulement mon fournisseur; POSIX, par exemple, l'exige.