6
votes

Fichier non threadsafe E / S en C / C ++

Tout en dépannant certains problèmes de performance dans nos applications, j'ai découvert que les fonctions STDIO.H STDIO.H (et, au moins pour notre fournisseur, C ++ 'S FRStream classes) sont threadsafe . En conséquence, chaque fois que je fais quelque chose d'aussi simple que fgetc , le RTL doit acquérir une serrure, lire un octet et libérer le verrouillage.

Ce n'est pas bon pour la performance.

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?

  • MSVC fournit _fputc_nolock , et GCC fournit déverrouillé_stdio et flockfile , mais je ne trouve aucune fonction similaire dans mon compilateur (CodeGear C ++ Builder ).
  • Je pourrais utiliser l'API Windows brut, mais ce n'est pas portable et que je suppose que serait plus lent qu'un Fgec déverrouillé pour le caractère d'I / O.
  • Je pourrais passer à quelque chose comme le Apache Portable Runtime , mais cela pourrait potentiellement être beaucoup de travail.

    Comment les autres abordent-ils cela?

    Modifier : Étant donné que quelques personnes se demandaient, j'avais testé cela avant de poster. fgetc 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 Les classes utilisent malheureusement Fgec (donc si je souhaite utiliser iostream 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).


2 commentaires

Les fonctions STDIO.H de C ne sont pas threadsafe; C'est votre fournisseur aussi.


Pas seulement mon fournisseur; POSIX, par exemple, l'exige.


6 Réponses :


4
votes

Je ne ferais tout simplement pas d'IO un caractère à la fois s'il s'agit d'une performance sensible sage.


1 commentaires

Toutes les opérations aux flux aboutissent à Char Io. Vous devez travailler avec des tampons de flux. J'ai posté ci-dessous comment ...



1
votes

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.


1 commentaires

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).



1
votes

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.


0 commentaires

1
votes

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.

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.


0 commentaires

1
votes

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

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.

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).

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.

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.

bonne chance,
Ovanes xxx


0 commentaires

1
votes

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).

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.

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.


0 commentaires