Donc (juste pour le plaisir), j'essayais simplement d'écrire un code C pour copier un fichier. J'ai lu autour de vous et il semble que toutes les fonctions à lire à partir d'un appel de flux fgetc.) code> (j'espère que c'est ce vrai?), Donc j'ai utilisé cette fonction: #include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define FILEr "img1.png"
#define FILEw "img2.png"
main()
{
clock_t start,diff;
// number of bytes copied at each step
size_t st=10000;
int msec;
FILE *fr,*fw;
// placeholder for value that is read
char *x;
x=malloc(st);
fr=fopen(FILEr,"r");
fw=fopen(FILEw,"w");
start=clock();
while(!feof(fr))
{
fread(x,1,st,fr);
fwrite(x,1,st,fw);
}
diff=clock()-start;
msec=diff*1000/CLOCKS_PER_SEC;
printf("Time taken %d seconds %d milliseconds\n", msec/1000, msec%1000);
fclose(fr);
fclose(fw);
free(x);
}
4 Réponses :
Il se comporte comme si em> appelant Fread () code> n'appelle pas fgetc.) code> Pour lire chaque octet. P>
fgetc () code> à plusieurs reprises, mais il a un accès direct au tampon que fgetc () code> se lit de ainsi peut directement copier une plus grande quantité de données. p>
+1 pour "comme si". Vous pourriez améliorer la réponse en expliquant comment la "comme si la règle" s'applique à tout ce qui est dans le langage C.
@R ..: En fonction de la mise en œuvre du système de fichiers, je pense que le comportement peut très bien être absorbé. J'ai vu des points de repère de fget assez pathétiques avec Fuse FilesSemsem construits sur le dessus des interfaces de faible niveau de fusible.
La bibliothèque d'utilisateursPace Libc STDIO n'a aucune relation au fusible ou au système de fichiers / périphérique sous-jacent accédant. À moins que la mise en mémoire tampon est désactivée, elle fonctionnera toujours sous forme entièrement tamponnée. Même si la mise en mémoire tampon est désactivée, une méthode non pathologiquement-mauvaise code> n'appelera jamais à plusieurs reprises fgetc. / code> mais effectuera un seul Lire code> opération pour la partie. de la longueur demandée qui ne peut pas être obtenue à partir du tampon d'utilisateurs existant.
Vous oubliez à propos de la tampon de fichier ( inode, DENTRY ET CACHES PAGE ).
Effacez-les avant de courir: P>
echo 3 > /proc/sys/vm/drop_caches
Non liée à la question de l'OP.
Oui c'est le cas. Il est probable que l'OP teste les deux versions dans une période de temps courte. La première exécution amorcera le cache, la seconde peut s'attendre à ce que le fichier soit complètement tamponné dans des tampons OS, il n'y aura donc pas d'E / S pour l'opération.
@R .. Cela dépend beaucoup sur la façon dont vous i> interprétez-le. Je ne pense pas que la question soit trop claire, mais je pense que l'OP est en train de faire de plus en plus i> de référence en volume important, mais voyant Insertement i> Runtimes plus courts. Cela précise clairement les effets de cache pour moi. Quoi qu'il en soit, depuis que FS Benchmarking est dur i> et que l'OP ne se présente pas avec les mesures prises pour éviter les mauvais résultats, je suis enclin à supposons i> le PO ne sait pas ce.
Bien compte tenu de l'utilisation stupide feof () et de sa préoccupation avec la performance, il est assez clair pour moi que l'OP ne fait pas que ce qu'il fait, et le fait que R. est upé pour le commentaire ci-dessus indique que l'OP n'est pas seul.
@wildplasser: Pour être juste, l'autre réponse n'est pas sans mérite. Il y a beaucoup de choses en jeu. Benchmarking du système de fichiers est difficile i> :) Alors les deux réponses sont très liées à la question de l'OP, imo
Sur tout système moderne, le fichier que vous testez sera entièrement mis en cache en mémoire (par le noyau), quelle que soit la méthode de lecture. La question de OP concerne STDIO et FREAD CODE> VERSUS FGETC CODE>, pas des problèmes de cache du système de fichiers.
Comme Sehe dit son partiellement parce que la tampon, mais il y en a plus et je vais expliquer pourquoi est-ce que c'est et en même temps pourquoi Donc pour un fichier 10MIB: P>
tandis que Permet de dire pour la simplicité que chaque appel de fonction prend 1 ms:
En plus de cela, le système d'exploitation lit et écrit généralement le même appareil, je suppose que votre exemple le fait sur le même disque dur. Le système d'exploitation Lors de la lecture de votre fichier source, déplacez les têtes de disque dur sur les plateaux de disques de filage à la recherche du fichier, puis lisez 1 octet, mettez-le sur mémoire, puis déplacez la tête de lecture / écriture sur les plateaux de filature du disque dur à la recherche sur la place. que le système d'exploitation et le contrôleur de disque dur acceptaient de localiser le fichier de destination, puis écrit 1 octet de la mémoire. Pour l'exemple ci-dessus, cela se produit plus de 10 millions de fois pour chaque fichier: totalisant plus de 20 millions de fois, à l'aide de la version tamponnée, cela se produit juste un total de plus de 20 000 fois. P>
En outre que le système d'exploitation lors de la lecture du disque met en mémoire quelques données de disque dur des données de disque dur à des fins de performance, cela peut accélérer le programme même lorsque vous utilisez le Selon votre machine Configuration / Charge / OS / etc. Vos résultats de la lecture et de l'écriture peuvent varier beaucoup, d'où sa recommandation de vider les caches de disque permettant de mieux comprendre des résultats plus significatifs. P>
Lorsque les fichiers de source et de destination sont sur différents, des choses du disque dur sont beaucoup plus rapides. Avec SDDS, je ne suis pas vraiment sûr si la lecture / écriture est absolument exclusive les unes des autres. P>
Résumé: Chaque appel à une fonction comporte certains frais généraux, la lecture d'un disque dur contient d'autres frais généraux et caches / tampons aident à obtenir des choses plus rapides. P>
Autres informations p>
http://fr.wikipedia.org/wiki/disk_read-and-wiki_head_head p>
http://fr.wikipedia.org/wiki/hard_disk#Components p>
blockQuote> fgetc.) code> donnera plus de latence. P>
fgetc () code> est appelé pour chaque octet lu à partir du fichier. p>
Fread () code> est appelé tous les n octets du tampon local pour les données de fichier. P>
fgetc.) code> est appelé: 10 485 760 fois p>
Fread code> avec un tampon 1KIB la fonction appelée 10 240 fois. P>
fgetc code> prendrait 10 485 760 ms = 10485,76 secondes ~ 2 9127 heures
Fread code> prendrait 10 240 ms = 10,24 secondes p>
fget code> moins efficace, car le programme Lisez à partir de la mémoire du système d'exploitation au lieu de lire directement à partir du disque dur. C'est à quelle réponse de Sehe se réfère. P>
Les fonctions STDIO rempliront un tampon de lecture, de la taille "bufsiz" telle que définie dans stdio.h, et ne fera que faire un appel à un système de lecture (2) à chaque fois que la mémoire tampon est drainée. Ils ne feront pas un appel individuel (2) appel à tous les octets consommés - ils ont lu de gros morceaux. Bufsiz est typiquement quelque chose comme 1024 ou 4096. P>
Vous pouvez également ajuster la taille de la mémoire tampon, si vous le souhaitez, pour l'augmenter - voir les pages de l'homme pour SETBUF / SETVBUF / SETBUFFER sur la plupart des systèmes - bien que cela soit peu susceptible de faire une énorme différence de performance. P >
D'autre part, comme vous le souhaitez, vous pouvez faire un appel de la taille arbitraire en lecture (2) en définissant cette taille dans l'appel, bien que vous obteniez des retours diminuant avec cela à un moment donné. P>
BTW, vous pouvez aussi bien utiliser ouvert (2) et non fopen (3) si vous faites des choses de cette façon. Il y a peu de point dans Fopen'ing un fichier que vous allez utiliser uniquement pour son descripteur de fichier. P>
Merci, cela fait beaucoup de sens, et je suppose que c'était un peu stupide de ma part d'utiliser Fopen Haha ... J'ai eu une question de suivi à votre réponse: vous avez dit que vous pouvez faire savoir (2) appel de système d'arbitraire (2) Taille en réglant cette taille dans l'appel, mais vous obtiendrez des rendements décroissants. Qu'est-ce que tu veux dire exactement par là? Comment les choses vont-elles mal ou moins efficaces?
@Matthewfitzpatrick: Il y a toujours un point où le coût de l'IO et de la copie de la mémoire est tellement plus cher que l'appel du système que cela ne fait aucune différence dans le nombre d'octets que vous lisez.
La réduction du nombre d'appels système améliore les performances en faisant des choses comme abaissement du nombre de commutateurs contextuels du système, mais une fois que vous avez décimé qu'en un facteur de mille ou quatre mille, la quantité d'amélioration de la performance diminue. Votre programme n'est probablement pas dominé par des appels de système de lecture à ce stade. Les choses ne vont pas mal, c'est simplement que les performances ne seront pas améliorées de manière significative au-delà d'un point - les E / S réels commenceront à dominer le temps pris par Lecture (2) plutôt que le dépôt du système une fois que vous avez passé un certaine taille.
Je pense qu'il veut dire que Perry signifiait la différence de performance entre Fread code> et lire code> doit approcher zéro comme la taille du bloc d'être lue des approches à l'infini. Sur une bonne implémentation de stdio, cela est définitivement vrai, mais un mauvais peut toujours être plus lent dans Fread code> s'il lit d'abord les données via le tampon dans de nombreux petits sous-jacents lire code> s et les copie au tampon de l'appelant ...
@R .., j'étais plus de répondre à la "Pourquoi augmenterait la taille de la lecture (2) des appels rencontrant des rendements décroissants". Votre commentaire est également correct, bien sûr - en général, une mise en œuvre de STDIO utilisera un tampon de taille fixe, de sorte qu'une faille d'un énorme morceau beaucoup plus grand que la taille de la mémoire tampon ne sera pas aussi efficace que l'appel de lecture comparable (2).
Eh bien, je voudrais sérieusement remettre en question la viabilité d'une implémentation de STDIO qui force toutes les données à parcourir le tampon, même lorsqu'il pouvait être lu directement à (ou écrit directement à partir de) le tampon de l'appelant, tout comme je voudrais remettre en question la viabilité d'une à plusieurs reprises. appelé fgetc code> plutôt que de se comporter "comme si" en l'appelant à plusieurs reprises - mais apparemment certaines de ces mauvaises implémentations existent! :-)
Il est difficile pour une implémentation d'éviter le tampon dans le cas général, car une partie des données nécessaires pour satisfaire la lecture peut déjà être dans le tampon. Je n'ai pas regardé ces derniers temps sur les implémentations de Fread qui sont là-bas dans BSD et Linux - vous pouvez être correct qu'après avoir copié le tampon qu'ils satisfont à la partie suivante de la lecture directe dans le tampon de l'utilisateur. Bien sûr, la norme n'exige pas que, mais je ne sais pas quelle est la vraie pratique sans regarder. Mais nous sommes maintenant loin de la question originale de @ Matthewfitzpatrick ...
Vous dites que votre code copies i> un fichier, mais le fichier résultant ne sera pas égal à votre fichier d'origine. Votre code utilise un mauvais modèle pour la copie.
Pouvez-vous s'il vous plaît élaborer? J'ai copié une image et j'ai pu ouvrir la copie .... même avec un fichier HDF5
Avez-vous vérifié que les deux fichiers ont exactement la même taille? Si vous souhaitez copier des fichiers arbitraires, il peut toujours être ouvert i> n'est pas un argument valide.
Oui, est-ce que cela aussi, et ils sont les mêmes ... pour les petits et grands fichiers
@pratikm: ce que @Roland Illig fait référence au fait que
Feof () code> ne renvoie que vrais après i> une lecture a échoué, alors vos boucles écrivent le dernier caractère / bloc à le fichier de sortie deux fois.Si il convient également de noter que
horloge code> n'est pas une méthode valide de temps de mesure. Ses résultats sont définis dans une certaine mesure, et sur les systèmes POSIX, il renvoie le montant de CPU Heure i> utilisé par le processus, pas la quantité de temps réel i> - cela peut Faites une grande différence lorsque IO est impliqué. Utilisezclock_GutiTime code> (ouGetTimeOdday code>) à la place.@pratikm: Utilisez
md5sum code> pour vérifier l'intégrité du fichier copié (ousha1sum code> etc ...)CMP code> serait un meilleur moyen de vérifier l'intégrité avec certitude b>.Merci tout le monde ... je vais essayer cela juste dès que je peux ...
Regardez des fichiers mappés - si vos données sont en lecture seule, cela peut être le moyen le plus rapide d'y accéder (l'accès en lecture-écriture est également possible, mais vos modifications sont permanentes et en place, ce qui n'est pas toujours ce que vous voulez) .
@ Michałkosmulski: Les fichiers mappés de mémoire sont peu susceptibles d'être plus rapides au moins dans la configuration de mappage par défaut, car chaque accès de page (c'est-à-dire que chaque 4k) va entraîner une faute de page (et ainsi aller aller à Kernelspace), alors que
Lire code> peut faire le tout en un seul voyage à Kernelspace.mapp_populer code> peut probablement fonctionner autour de la question, mais toujours, je voudrais toujours exhorter la retenue à l'utilisation demmap code> purement une optimisation de performance pour les lectures et ne l'utilise que lorsque l'accès souhaité Modèle et idiome correspondent à une sémantique de mémoire à accessoires aléatoires.@R .. Il est vrai que le MMAP est le meilleur pour les modèles d'accès aléatoires ou complexes. Cependant, il n'est pas vrai que chaque accès de page entraîne un voyage à l'espace du noyau. Chaque page touchée provoque un certain nombre de pages voisines à précharger dans la même opération (sous Linux son numéro est synodible via
/ proc code>. Utilisation deMADVise () CODE> Vous pouvez également Définissez un indice d'accès séquentiel sur Strenghtne cet effet. Il serait préférable d'effectuer un test de pic pour le modèle d'accès particulier dont les besoins d'affiche originaux et comparent.@R .. Pour ce cas, je pense que l'horloge
code> fonctionne bien car ce n'est pas un programme fileté. Quand il s'agit de Pthreads en utilisant, il ne vaut rien.