des informations sur la garantie des données est sur le disque ( http://winntfs.com/2012/11/29/windows-write-caching-par-2-an-overview-for-Application-Developers/ ), même dans le cas d'ex. Une panne de courant, il semble que sur les plates-formes Windows, vous devez compter sur sa version étant donné le Le fait que je travaillerai avec des fichiers assez volumineux, il faut mettre à jour "transactionnellement", cela signifie effectuer une "FSYNC" à la fin d'une transaction commit. J'ai donc créé une infime application pour tester la performance en le faisant. Il effectue essentiellement des écrivies séquentielles d'un lot de 8 octets aléatoires de la taille de la page de mémoire utilisant 8 écrit puis des flushes. Le lot est répété dans une boucle et après toutes nombreuses pages écrites, elle enregistre les performances. En outre, il dispose de deux options configurables: à FSYNC sur une affleurance et d'écrire un octet à la dernière position du fichier, avant de commencer la page écrit. P> Les résultats de la performance J'obtenue (64 bits Win 7, disque à broche lente) ne sont pas très encourageants. Il semble que "FSYNC" performance dépend beaucoup de la taille du fichier rinçage, de sorte que cela domine le temps, et non la quantité de données "sales" à rougir. Le graphique ci-dessous montre les résultats des 4 options de réglages différents de la petite application de référence. P> p> Comme vous pouvez le constater, les performances de" FSYNC "diminuent de manière exponentielle, car le fichier se développe (jusqu'à quelques Go, il est vraiment à une halte). En outre, le disque lui-même ne semble pas faire un lot entier (c'est-à-dire le moniteur de ressources indique son temps actif, à seulement quelques cent et sa file d'attente de disque vidamment vide pendant la plupart du temps). P> i Évidemment attendait que la performance "FSYNC" soit très pire que de faire des flushes tamponnées normales, mais je m'attendais à ce qu'il soit plus ou moins constant et indépendant de la taille du fichier. Comme ceci, il semblerait suggérer qu'il n'est pas utilisable en combinaison avec un seul gros fichier. P> Est-ce que quelqu'un a une explication, des expériences différentes ou une solution différente permettant de garantir que des données sont sur le disque et qui a un Performances plus ou moins constantes, prévisibles? P> flushfilebuffers code> pour avoir la meilleure garantie que les tampons sont effectivement rincé des caches de périphérique de disque sur le support de stockage lui-même. La combinaison de
file_flag_no_buffering code> avec
file_flag_write_through code> ne garantit pas la rinçage du cache de périphérique, mais simplement avoir un effet sur le cache du système de fichiers, si cette information est correcte.
4 Réponses :
J'ai expérimenté et j'ai raccordé un peu de plus et j'ai trouvé une solution qui peut être acceptable pour moi (bien que je n'ai actuellement que des écrivies séquentielles testées). Dans le processus, j'ai découvert des comportements inattendus qui posent un certain nombre de nouvelles questions. Je posterai une nouvelle question à la question ( Explication / information recherché: Windows Ecrire des performances d'E / S avec "FSYNC" (flushfilebuffers) ) pour ceux-ci. P>
J'ai ajouté les deux autres options supplémentaires à mon indice de référence: P>
file_flag_no_buffering code> et file_flag_write_through code> drapeaux) li>
- Écrivez indirectement au fichier, via un fichier mappé de mémoire. LI>
ul>
Cela m'a fourni des résultats inattendus, dont l'un me donne une solution plus ou moins acceptable à mon problème. Lorsque "FSGNCCING" en combinaison avec des E / S ignorés / WritethRough, je n'observe pas une décroissance exponentielle de la vitesse d'écriture. Ainsi (bien que ce ne soit pas très rapide), cela me fournit une solution permettant de garantir que les données sont sur le disque, ce qui a une performance prévisible constante qui n'est pas affectée par la taille du fichier. P>
Quelques autres résultats inattendus étaient les suivants: P>
- Si un octet est écrit à la dernière position dans le fichier, avant d'effectuer la page écrit avec les options "FSYNC" et "non coupées / writethRough", le débit d'écriture est presque double. Li>
- La performance de nonuffer / écrit avec ou sans FSYNC est presque identique, sauf lorsqu'un octet a été écrit à la dernière position du fichier. Le débit d'écriture du scénario «nonuffered / écrit» sans «FSYNC» sur un fichier vide est d'environ 12,5 Mo / s, alors que dans le même scénario sur un fichier qui comporte un octet écrit dans la dernière position dans le fichier que le débit est trois fois plus haut à 37 Mo / s. Li>
- Écrire à un fichier indirectement via un fichier mail de mémoire en combinaison avec "FSYNC" indique la même diminution de débit exponentielle que celle observée dans les écrivies tamponnées directement à un fichier, même si "nonuffer / écraser" est défini sur le fichier. < / li>
ul>
J'ai ajouté le code mis à jour que j'ai utilisé pour la référence à ma question initiale. p>
Le graphique ci-dessous montre certains des nouveaux résultats supplémentaires. P>
p>
[mal; Voir les commentaires.] p>
Je pense que l'article que vous référence est incorrect pour indiquer que les flushfilebuffers ont un effet utile sur les E / S non avancé. Il fait référence à un document Microsoft, mais le papier en question ne fait aucune revendication de ce type. P>
Selon la documentation, l'utilisation d'E / S non défigé a le même effet que, mais est plus efficace que, appelant le flushfilebuffer après chaque écriture. Donc, la solution pratique consiste à utiliser des E / S non volés plutôt que de l'utilisation de FlushfileBuffer. P>
Notez cependant que l'utilisation d'un fichier mappé de mémoire défaite les paramètres de mémoire tampon. Je ne recommanderais pas d'utiliser un fichier mail de mémoire si vous essayez de repousser les données sur le disque dès que possible. P>
De ce que je comprends que l'article est correct. Également confirmé par ex. Ici: support.microsoft.com/kb/332023 (voir la section "Plus d'informations"). Flushfilebuffers Code> Traduit sur
Synchroniser le cache Code> Commande pour les périphériques SCSI et
FLUSH CACHE CODE> Commande des appareils IDE / ATAPI.
Ecrire via CODE> est émis sous la forme
ForceUnittaccess CODE>, ce qui n'est apparemment pas manifesté par des périphériques IDE / ITAPI. Je crois que c'est aussi ce qui est mentionné dans l'article référencé FTP. recherche.microsoft.com/pub/tr/tr-2008-36.pdf .
@alex Ces articles parlent de cache à l'intérieur du disque / contrôleur, et non du niveau du système d'exploitation. Lorsque vous utilisez WritThRough, cela aurait dû le faire à cette "dans le cache de disque", mais ce n'est pas réellement "sur le disque". Pour les tests tels que votre Q, ce cache peut expliquer la baisse d'EXP lorsque votre volume IO augmente. Ce cache est une grosse affaire pour les bases de données lorsque ces caches "dans le disque" échouent sous condition PowerFail avant d'être écrites dans un secteur, voir support.microsoft.com/kb/234656
Je ne trouve rien dans ce document qui dit quoi que ce soit sur l'API Win32. Cependant, l'article de KB fait certainement!
Votre test indique une diminution exponentielle de la vitesse sur la synchronisation des exécutions car vous recréez le fichier à chaque fois. Dans ce cas, ce n'est plus une écriture purement séquentielle - chaque écriture augmente également le fichier, ce qui nécessite plusieurs cherchent à mettre à jour les métadonnées de fichier dans le système de fichiers. Si vous rencontrez tous ces travaux à l'aide d'un fichier préexistant entièrement attribué, vous verriez un résultat beaucoup plus rapide car aucune de ces mises à jour de métadonnées ne serait interférée.
J'ai rencontré un test similaire sur ma boîte Linux. Les résultats tout en recréant le fichier à chaque fois: p> Les résultats à l'aide du fichier préexistant (évidemment, l'affaire Last_Byte n'est pas pertinente ici. En outre, le tout premier résultat a également dû créer le fichier): p> (Notez que je n'ai utilisé que 10 000 morceaux pas de 25 000 morceaux, il ne s'agit donc que de 320 Mo, à l'aide d'un système de fichiers ext2. Je n'ai pas eu de plus grand ext2fs pratique, mon plus grand FS est XFS et il a refusé d'autoriser MMAP + I / O direct.) P> Voici le code, si vous êtes intéressé: P> #define _GNU_SOURCE 1
#include <malloc.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#define USE_MMAP 8
#define USE_DIRECT 4
#define USE_LAST 2
#define USE_SYNC 1
#define PAGE 4096
#define CHUNK (8*PAGE)
#define NCHUNKS 10000
#define STATI 1000
#define FSIZE (NCHUNKS*CHUNK)
main()
{
int i, j, fd, rc, stc;
char *data = valloc(CHUNK);
char *map, *dst;
char sfname[8];
struct timeval start, end, stats[NCHUNKS/STATI+1];
FILE *sfile;
printf("mmap\tdirect\tlast\tsync\ttime\n");
for (i=0; i<16; i++) {
int oflag = O_CREAT|O_RDWR|O_TRUNC;
if (i & USE_DIRECT)
oflag |= O_DIRECT;
fd = open("dummy", oflag, 0666);
ftruncate(fd, FSIZE);
if (i & USE_LAST) {
lseek(fd, 0, SEEK_END);
write(fd, data, 1);
lseek(fd, 0, SEEK_SET);
}
if (i & USE_MMAP) {
map = mmap(NULL, FSIZE, PROT_WRITE, MAP_SHARED, fd, 0);
if (map == (char *)-1L) {
perror("mmap");
exit(1);
}
dst = map;
}
sprintf(sfname, "%x.csv", i);
sfile = fopen(sfname, "w");
stc = 1;
printf("%d\t%d\t%d\t%d\t",
(i&USE_MMAP)!=0, (i&USE_DIRECT)!=0, (i&USE_LAST)!=0, i&USE_SYNC);
fflush(stdout);
gettimeofday(&start, NULL);
stats[0] = start;
for (j = 1; j<=NCHUNKS; j++) {
if (i & USE_MMAP) {
memcpy(dst, data, CHUNK);
if (i & USE_SYNC)
msync(dst, CHUNK, MS_SYNC);
dst += CHUNK;
} else {
write(fd, data, CHUNK);
if (i & USE_SYNC)
fdatasync(fd);
}
if (!(j % STATI)) {
gettimeofday(&end, NULL);
stats[stc++] = end;
}
}
end.tv_usec -= start.tv_usec;
if (end.tv_usec < 0) {
end.tv_sec--;
end.tv_usec += 1000000;
}
end.tv_sec -= start.tv_sec;
printf(" %d.%06ds\n", (int)end.tv_sec, (int)end.tv_usec);
if (i & USE_MMAP)
munmap(map, FSIZE);
close(fd);
for (j=NCHUNKS/STATI; j>0; j--) {
stats[j].tv_usec -= stats[j-1].tv_usec;
if (stats[j].tv_usec < 0) {
stats[j].tv_sec--;
stats[j].tv_usec+= 1000000;
}
stats[j].tv_sec -= stats[j-1].tv_sec;
}
for (j=1; j<=NCHUNKS/STATI; j++)
fprintf(sfile, "%d\t%d.%06d\n", j*STATI*CHUNK,
(int)stats[j].tv_sec, (int)stats[j].tv_usec);
fclose(sfile);
}
}
Merci d'avoir regardé dans ce Howard. Je vais étudier ces résultats et remédier à mes tests à l'aide du scénario de fichier préexistant que vous mentionnez. Les résultats sur un boîtier de Linux et une boîte de fenêtres peuvent être différents, car je soupçonne que le cache de fichier de système d'exploitation et / ou la couche de pagination du noyau peut jouer un rôle important dans les performances dégradantes. Mais peut-être, comme vous le mentionnez, cela est lié aux mises à jour des métadonnées de fichiers.
Voici une version Windows de mon code synchtonisé. Je ne le faisais que dans une Virtualbox VM, donc je ne pense pas avoir de numéros utiles à titre de comparaison, mais vous pourriez lui donner un tir à comparer à vos numéros C # sur votre machine. Je passe Open_alway à Createfile, il va donc réutiliser le fichier existant. Changer ce drapeau pour créer_always si vous souhaitez tester à nouveau avec un fichier vide à chaque fois.
Une chose que j'ai remarquée, c'est que les résultats étaient beaucoup plus rapides la première fois que je rencontrais ce programme. Peut-être que NTFS n'est pas très efficace pour écraser les données existantes et les effets de fragmentation de fichiers apparaissent sur les exécutions suivantes. P>
#include <windows.h> #include <stdio.h> #define USE_MMAP 8 #define USE_DIRECT 4 #define USE_LAST 2 #define USE_SYNC 1 #define PAGE 4096 #define CHUNK (8*PAGE) #define NCHUNKS 10000 #define STATI 1000 #define FSIZE (NCHUNKS*CHUNK) static LARGE_INTEGER cFreq; int gettimeofday(struct timeval *tv, void *unused) { LARGE_INTEGER count; if (!cFreq.QuadPart) { QueryPerformanceFrequency(&cFreq); } QueryPerformanceCounter(&count); tv->tv_sec = count.QuadPart / cFreq.QuadPart; count.QuadPart %= cFreq.QuadPart; count.QuadPart *= 1000000; tv->tv_usec = count.QuadPart / cFreq.QuadPart; return 0; } main() { int i, j, rc, stc; HANDLE fd; char *data = _aligned_malloc(CHUNK, PAGE); char *map, *dst; char sfname[8]; struct timeval start, end, stats[NCHUNKS/STATI+1]; FILE *sfile; DWORD len; printf("mmap\tdirect\tlast\tsync\ttime\n"); for (i=0; i<16; i++) { int oflag = FILE_ATTRIBUTE_NORMAL; if (i & USE_DIRECT) oflag |= FILE_FLAG_NO_BUFFERING|FILE_FLAG_WRITE_THROUGH; fd = CreateFile("dummy", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, oflag, NULL); SetFilePointer(fd, FSIZE, NULL, FILE_BEGIN); SetEndOfFile(fd); if (i & USE_LAST) WriteFile(fd, data, 1, &len, NULL); SetFilePointer(fd, 0, NULL, FILE_BEGIN); if (i & USE_MMAP) { HANDLE mh; mh = CreateFileMapping(fd, NULL, PAGE_READWRITE, 0, FSIZE, NULL); map = MapViewOfFile(mh, FILE_MAP_WRITE, 0, 0, FSIZE); CloseHandle(mh); dst = map; } sprintf(sfname, "%x.csv", i); sfile = fopen(sfname, "w"); stc = 1; printf("%d\t%d\t%d\t%d\t", (i&USE_MMAP)!=0, (i&USE_DIRECT)!=0, (i&USE_LAST)!=0, i&USE_SYNC); fflush(stdout); gettimeofday(&start, NULL); stats[0] = start; for (j = 1; j<=NCHUNKS; j++) { if (i & USE_MMAP) { memcpy(dst, data, CHUNK); FlushViewOfFile(dst, CHUNK); dst += CHUNK; } else { WriteFile(fd, data, CHUNK, &len, NULL); } if (i & USE_SYNC) FlushFileBuffers(fd); if (!(j % STATI)) { gettimeofday(&end, NULL); stats[stc++] = end; } } end.tv_usec -= start.tv_usec; if (end.tv_usec < 0) { end.tv_sec--; end.tv_usec += 1000000; } end.tv_sec -= start.tv_sec; printf(" %d.%06ds\n", (int)end.tv_sec, (int)end.tv_usec); if (i & USE_MMAP) UnmapViewOfFile(map); CloseHandle(fd); for (j=NCHUNKS/STATI; j>0; j--) { stats[j].tv_usec -= stats[j-1].tv_usec; if (stats[j].tv_usec < 0) { stats[j].tv_sec--; stats[j].tv_usec+= 1000000; } stats[j].tv_sec -= stats[j-1].tv_sec; } for (j=1; j<=NCHUNKS/STATI; j++) fprintf(sfile, "%d\t%d.%06d\n", j*STATI*CHUNK, (int)stats[j].tv_sec, (int)stats[j].tv_usec); fclose(sfile); } }
Merci Howard, je vais également courir celui-là. Il peut éclairer des conclusions de mes réducteurs à l'aide d'un fichier entièrement écrit. Je suis juste en train de les ajouter à mes questions. Vous verrez que des comportements inexpliqués et inattendus demeurent, alors que l'explication que vous avez fournie vous convient bien à certains des changements de performance observés dans ce test.