2013-08-16 22 views
6

Dalle informazioni sull'assicurazione dei dati sul disco (http://winntfs.com/2012/11/29/windows-write-caching-part-2-an-overview-for-application-developers/), anche nel caso ad es. un'interruzione dell'alimentazione, sembra che su piattaforme Windows è necessario fare affidamento sulla sua versione "fsync" FlushFileBuffers per avere la migliore garanzia che i buffer siano effettivamente scaricati dalle cache del dispositivo disco sul supporto di memorizzazione stesso. La combinazione di FILE_FLAG_NO_BUFFERING con FILE_FLAG_WRITE_THROUGH non garantisce lo svuotamento della cache del dispositivo, ma ha semplicemente un effetto sulla cache del file system, se queste informazioni sono corrette.Prestazioni di Windows fsync (FlushFileBuffers) con file di grandi dimensioni

Dato che lavorerò con file piuttosto grandi, che devono essere aggiornati "a livello transazionale", ciò significa eseguire un "fsync" alla fine di un commit di transazione. Così ho creato una piccola app per testare le prestazioni nel farlo. Esegue fondamentalmente scritture sequenziali di un lotto di 8 byte casuali di dimensioni di pagina di memoria utilizzando 8 scritture e quindi esegue il flushing. Il batch viene ripetuto in un ciclo e, dopo ogni numero di pagine scritte, registra le prestazioni. Inoltre ha due opzioni configurabili: fsync su un flush e se scrivere un byte nell'ultima posizione del file, prima di iniziare la scrittura della pagina.

// Code updated to reflect new results as discussed in answer below. 
// 26/Aug/2013: Code updated again to reflect results as discussed in follow up question. 
// 28/Aug/2012: Increased file stream buffer to ensure 8 page flushes. 
class Program 
{ 
    static void Main(string[] args) 
    { 
     BenchSequentialWrites(reuseExistingFile:false); 
    } 
    public static void BenchSequentialWrites(bool reuseExistingFile = false) 
    { 
     Tuple<string, bool, bool, bool, bool>[] scenarios = new Tuple<string, bool, bool, bool, bool>[] 
     { // output csv, fsync?, fill end?, write through?, mem map? 
      Tuple.Create("timing FS-E-B-F.csv", true, false, false, false), 
      Tuple.Create("timing NS-E-B-F.csv", false, false, false, false), 
      Tuple.Create("timing FS-LB-B-F.csv", true, true, false, false), 
      Tuple.Create("timing NS-LB-B-F.csv", false, true, false, false), 
      Tuple.Create("timing FS-E-WT-F.csv", true, false, true, false), 
      Tuple.Create("timing NS-E-WT-F.csv", false, false, true, false), 
      Tuple.Create("timing FS-LB-WT-F.csv", true, true, true, false), 
      Tuple.Create("timing NS-LB-WT-F.csv", false, true, true, false), 
      Tuple.Create("timing FS-E-B-MM.csv", true, false, false, true), 
      Tuple.Create("timing NS-E-B-MM.csv", false, false, false, true), 
      Tuple.Create("timing FS-LB-B-MM.csv", true, true, false, true), 
      Tuple.Create("timing NS-LB-B-MM.csv", false, true, false, true), 
      Tuple.Create("timing FS-E-WT-MM.csv", true, false, true, true), 
      Tuple.Create("timing NS-E-WT-MM.csv", false, false, true, true), 
      Tuple.Create("timing FS-LB-WT-MM.csv", true, true, true, true), 
      Tuple.Create("timing NS-LB-WT-MM.csv", false, true, true, true), 
     }; 
     foreach (var scenario in scenarios) 
     { 
      Console.WriteLine("{0,-12} {1,-16} {2,-16} {3,-16} {4:F2}", "Total pages", "Interval pages", "Total time", "Interval time", "MB/s"); 
      CollectGarbage(); 
      var timingResults = SequentialWriteTest("test.data", !reuseExistingFile, fillEnd: scenario.Item3, nPages: 200 * 1000, fSync: scenario.Item2, writeThrough: scenario.Item4, writeToMemMap: scenario.Item5); 
      using (var report = File.CreateText(scenario.Item1)) 
      { 
       report.WriteLine("Total pages,Interval pages,Total bytes,Interval bytes,Total time,Interval time,MB/s"); 
       foreach (var entry in timingResults) 
       { 
        Console.WriteLine("{0,-12} {1,-16} {2,-16} {3,-16} {4:F2}", entry.Item1, entry.Item2, entry.Item5, entry.Item6, entry.Item7); 
        report.WriteLine("{0},{1},{2},{3},{4},{5},{6}", entry.Item1, entry.Item2, entry.Item3, entry.Item4, entry.Item5.TotalSeconds, entry.Item6.TotalSeconds, entry.Item7); 
       } 
      } 
     } 
    } 

    public unsafe static IEnumerable<Tuple<long, long, long, long, TimeSpan, TimeSpan, double>> SequentialWriteTest(
     string fileName, 
     bool createNewFile, 
     bool fillEnd, 
     long nPages, 
     bool fSync = true, 
     bool writeThrough = false, 
     bool writeToMemMap = false, 
     long pageSize = 4096) 
    { 
     // create or open file and if requested fill in its last byte. 
     var fileMode = createNewFile ? FileMode.Create : FileMode.OpenOrCreate; 
     using (var tmpFile = new FileStream(fileName, fileMode, FileAccess.ReadWrite, FileShare.ReadWrite, (int)pageSize)) 
     { 
      Console.WriteLine("Opening temp file with mode {0}{1}", fileMode, fillEnd ? " and writing last byte." : "."); 
      tmpFile.SetLength(nPages * pageSize); 
      if (fillEnd) 
      { 
       tmpFile.Position = tmpFile.Length - 1; 
       tmpFile.WriteByte(1); 
       tmpFile.Position = 0; 
       tmpFile.Flush(true); 
      } 
     } 
     // Make sure any flushing/activity has completed 
     System.Threading.Thread.Sleep(TimeSpan.FromMinutes(1)); 
     System.Threading.Thread.SpinWait(50); // warm up. 

     var buf = new byte[pageSize]; 
     new Random().NextBytes(buf); 
     var ms = new System.IO.MemoryStream(buf); 

     var stopwatch = new System.Diagnostics.Stopwatch(); 
     var timings = new List<Tuple<long, long, long, long, TimeSpan, TimeSpan, double>>(); 
     var pageTimingInterval = 8 * 2000; 
     var prevPages = 0L; 
     var prevElapsed = TimeSpan.FromMilliseconds(0); 

     // Open file 
     const FileOptions NoBuffering = ((FileOptions)0x20000000); 
     var options = writeThrough ? (FileOptions.WriteThrough | NoBuffering) : FileOptions.None; 
     using (var file = new FileStream(fileName, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite, (int)(16 *pageSize), options)) 
     { 
      stopwatch.Start(); 
      if (writeToMemMap) 
      { 
       // write pages through memory map. 
       using (var mmf = MemoryMappedFile.CreateFromFile(file, Guid.NewGuid().ToString(), file.Length, MemoryMappedFileAccess.ReadWrite, null, HandleInheritability.None, true)) 
       using (var accessor = mmf.CreateViewAccessor(0, file.Length, MemoryMappedFileAccess.ReadWrite)) 
       { 
        byte* base_ptr = null; 
        accessor.SafeMemoryMappedViewHandle.AcquirePointer(ref base_ptr); 
        var offset = 0L; 
        for (long i = 0; i < nPages/8; i++) 
        { 
         using (var memStream = new UnmanagedMemoryStream(base_ptr + offset, 8 * pageSize, 8 * pageSize, FileAccess.ReadWrite)) 
         { 
          for (int j = 0; j < 8; j++) 
          { 
           ms.CopyTo(memStream); 
           ms.Position = 0; 
          } 
         } 
         FlushViewOfFile((IntPtr)(base_ptr + offset), (int)(8 * pageSize)); 
         offset += 8 * pageSize; 
         if (fSync) 
          FlushFileBuffers(file.SafeFileHandle); 

         if (((i + 1) * 8) % pageTimingInterval == 0) 
          timings.Add(Report(stopwatch.Elapsed, ref prevElapsed, (i + 1) * 8, ref prevPages, pageSize)); 
        } 
        accessor.SafeMemoryMappedViewHandle.ReleasePointer(); 
       } 
      } 
      else 
      { 
       for (long i = 0; i < nPages/8; i++) 
       { 
        for (int j = 0; j < 8; j++) 
        { 
         ms.CopyTo(file); 
         ms.Position = 0; 
        } 
        file.Flush(fSync); 
        if (((i + 1) * 8) % pageTimingInterval == 0) 
         timings.Add(Report(stopwatch.Elapsed, ref prevElapsed, (i + 1) * 8, ref prevPages, pageSize)); 
       } 
      } 
     } 
     timings.Add(Report(stopwatch.Elapsed, ref prevElapsed, nPages, ref prevPages, pageSize)); 
     return timings; 
    } 

    private static Tuple<long, long, long, long, TimeSpan, TimeSpan, double> Report(TimeSpan elapsed, ref TimeSpan prevElapsed, long curPages, ref long prevPages, long pageSize) 
    { 
     var intervalPages = curPages - prevPages; 
     var intervalElapsed = elapsed - prevElapsed; 
     var intervalPageSize = intervalPages * pageSize; 
     var mbps = (intervalPageSize/(1024.0 * 1024.0))/intervalElapsed.TotalSeconds; 
     prevElapsed = elapsed; 
     prevPages = curPages; 
     return Tuple.Create(curPages, intervalPages, curPages * pageSize, intervalPageSize, elapsed, intervalElapsed, mbps); 
    } 

    private static void CollectGarbage() 
    { 
     GC.Collect(); 
     GC.WaitForPendingFinalizers(); 
     System.Threading.Thread.Sleep(200); 
     GC.Collect(); 
     GC.WaitForPendingFinalizers(); 
     System.Threading.Thread.SpinWait(10); 
    } 

    [DllImport("kernel32.dll", SetLastError = true)] 
    static extern bool FlushViewOfFile(
     IntPtr lpBaseAddress, int dwNumBytesToFlush); 

    [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] 
    static extern bool FlushFileBuffers(SafeFileHandle hFile); 
} 

I risultati prestazionali sto ottenendo (64 bit Windows 7, disco a rotazione lenta) non sono molto incoraggianti. Sembra che le prestazioni di "fsync" dipendano molto dalle dimensioni del file che viene svuotato, in modo tale da dominare il tempo e non dalla quantità di dati "sporchi" da svuotare. Il grafico sottostante mostra i risultati per le 4 diverse opzioni di impostazione della piccola applicazione benchmark.

Benchmark timing for 4 the scenarios

Come si può vedere, le prestazioni di "fsync" in modo esponenziale diminuisce il file cresce (fino a qualche GB macina davvero una battuta d'arresto). Inoltre, il disco stesso sembra non occuparsi molto (ad esempio, il monitor delle risorse mostra il suo tempo attivo solo attorno a qualche percento, e la sua coda di dischi è quasi sempre vuota per la maggior parte del tempo).

Mi aspettavo ovviamente che le prestazioni di "fsync" fossero un po 'peggiori rispetto ai normali flush buffer, ma mi aspettavo che fosse più o meno costante e indipendente dalla dimensione del file. In questo modo sembrerebbe suggerire che non è utilizzabile in combinazione con un singolo file di grandi dimensioni.

Qualcuno ha una spiegazione, esperienze diverse o una soluzione diversa che consente di garantire che i dati siano su disco e che abbiano una prestazione più o meno costante e prevedibile?

AGGIORNATO Vedere le nuove informazioni nella risposta di seguito.

risposta

1

Ho sperimentato un po 'di più e ho trovato una soluzione che potrebbe essere accettabile per me (anche se attualmente ho solo provato le scritture sequenziali). Nel processo ho scoperto alcuni comportamenti imprevisti che sollevano una serie di nuove domande. Inserirò una nuova domanda SO (Explanation/information sought: Windows write I/O performance with "fsync" (FlushFileBuffers)) per quelli.

ho aggiunto le seguenti due opzioni aggiuntive per il mio punto di riferimento:

  • Usa buffer/Writethrough scrive (cioè specificando le FILE_FLAG_NO_BUFFERING e FILE_FLAG_WRITE_THROUGH bandiere)
  • scrittura al file indirettamente, attraverso un file di memoria mappata.

Questo mi ha fornito alcuni risultati inaspettati, uno dei quali mi dà una soluzione più o meno accettabile per il mio problema.Quando "fsyncing" in combinazione con I/O unbuffered/writethrough, non osservo un decadimento esponenziale della velocità di scrittura. Quindi (anche se non è molto veloce) questo mi fornisce una soluzione che consente di assicurare che i dati siano su disco e che abbia una prestazione prevedibile costante che non sia influenzata dalle dimensioni del file.

Pochi altri risultati imprevisti sono i seguenti:

  • Se un byte viene scritto l'ultima posizione nel file, prima di eseguire la pagina scrive con le e le opzioni "Fsync" "senza buffer/WriteThrough", il throughput di scrittura quasi raddoppia.
  • Le prestazioni di unbuffered/writethrough con o senza fsync sono quasi identiche, tranne quando un byte è stato scritto nell'ultima posizione nel file. Il throughput di scrittura dello scenario "unbuffered/writethrough" senza "fsync" su un file vuoto è di circa 12,5 MB/s, mentre nello stesso scenario su un file che ha un byte scritto nell'ultima posizione nel file il throughput è tre volte superiore a 37 MB/s.
  • La scrittura su un file indirettamente tramite un file mappato in memoria in combinazione con "fsync" mostra la stessa diminuzione del throughput esponenziale osservata nelle scritture bufferizzate direttamente su un file, anche se "unbuffered/writethrough" è impostato sul file.

Ho aggiunto il codice aggiornato che ho usato per il punto di riferimento alla mia domanda originale.

Il grafico seguente mostra alcuni nuovi risultati aggiuntivi.

Throughput for different combinations of options

0

[sbagliato; vedere i commenti.]

Credo che l'articolo di riferimento non sia corretto affermando che FlushFileBuffers ha effetti utili su I/O senza buffer. Si riferisce a un documento Microsoft, ma la carta in questione non fa affermazioni del genere.

Secondo la documentazione, l'utilizzo di I/O senza buffer ha lo stesso effetto di, ma è più efficiente di, chiamando FlushFileBuffer dopo ogni scrittura. Quindi la soluzione pratica è usare l'I/O senza buffer anziché usare FlushFileBuffer.

Si noti tuttavia che l'utilizzo di un file mappato in memoria annulla le impostazioni di buffering. Non suggerirei di utilizzare un file mappato in memoria se stai provando a trasferire i dati su disco il prima possibile.

+0

Da quello che ho capito l'articolo è corretto. Anche confermato ad es. qui: http://support.microsoft.com/kb/332023 (vedere la sezione "Ulteriori informazioni"). 'FlushFileBuffers' si traduce nel comando' SYNCHRONIZE CACHE' per i dispositivi SCSI e nel comando 'FLUSH CACHE' per i dispositivi IDE/ATAPI. 'Write Through' è rilasciato come' ForceUnitAccess', che apparentemente non è onorato dai dispositivi IDE/ITAPI. Credo che questo sia anche ciò che viene menzionato nell'articolo di riferimento ftp://ftp.research.microsoft.com/pub/tr/TR-2008-36.pdf. – Alex

+0

@alex questi articoli parlano di una cache all'interno del disco/controller, non del livello del SO. Quando si usa writethrough, dovrebbe averlo fatto su "nella cache del disco", ma in realtà non è "sul disco". Per test come la tua Q, questa cache potrebbe spiegare il declino delle aspettative al crescere del tuo volume di IO. Questa cache è un grosso problema per i database quando queste cache "nel disco" falliscono in condizioni powerfail prima di essere scritte in un settore, vedere http://support.microsoft.com/kb/234656 – rlb

+0

Non riesco a trovare nulla in quel carta che dice qualcosa sull'API Win32. Tuttavia l'articolo KB certamente lo fa! –

4

Il test mostra una diminuzione esponenziale della velocità nell'esecuzione della sincronizzazione perché si sta ricreando il file ogni volta. In questo caso non è più una scrittura puramente sequenziale: ogni scrittura aumenta anche il file, che richiede più ricerche per aggiornare i metadati del file nel filesystem. Se hai eseguito tutti questi lavori utilizzando un file già completo pre-esistente, vedresti un risultato molto più veloce perché nessuno di questi aggiornamenti dei metadati potrebbe interferire.

Ho eseguito un test simile sulla mia macchina Linux. I risultati, mentre ricreare il file ogni volta:

mmap direct last sync time 
0 0 0 0 0.882293s 
0 0 0 1 27.050636s 
0 0 1 0 0.832495s 
0 0 1 1 26.966625s 
0 1 0 0 5.775266s 
0 1 0 1 22.063392s 
0 1 1 0 5.265739s 
0 1 1 1 24.203251s 
1 0 0 0 1.031684s 
1 0 0 1 28.244678s 
1 0 1 0 1.031888s 
1 0 1 1 29.540660s 
1 1 0 0 1.032883s 
1 1 0 1 29.408005s 
1 1 1 0 1.035110s 
1 1 1 1 28.948555s 

i risultati utilizzando il file pre-esistente (ovviamente il caso last_byte è irrilevante qui Inoltre, il primo risultato ha avuto anche per creare il file.):

mmap direct last sync time 
0 0 0 0 1.199310s 
0 0 0 1 7.858803s 
0 0 1 0 0.184925s 
0 0 1 1 8.320572s 
0 1 0 0 4.047780s 
0 1 0 1 4.066993s 
0 1 1 0 4.042564s 
0 1 1 1 4.307159s 
1 0 0 0 3.596712s 
1 0 0 1 8.284428s 
1 0 1 0 0.242584s 
1 0 1 1 8.070947s 
1 1 0 0 0.240500s 
1 1 0 1 8.213450s 
1 1 1 0 0.240922s 
1 1 1 1 8.265024s 

(Si noti che ho usato solo 10.000 blocchi non 25.000 blocchi, quindi si tratta solo di 320MB di scrittura, utilizzando un filesystem ext2.Non ho avuto un ext2fs più grande a portata di mano, il mio fs più grande è XFS e si è rifiutato di consentire mmap + diretto I/O.)

Ecco il codice, se siete interessati:

#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); 
    } 
} 
+0

Grazie per aver esaminato questo Howard. Studierò questi risultati e rieseguirò i miei test usando lo scenario di file preesistente che hai citato. I risultati su una macchina Linux e una Windows possono essere diversi anche se sospetto che la cache dei file OS e/o il livello di paging del kernel possano giocare un ruolo significativo nelle prestazioni degradanti. Ma forse, come dici tu, questo è legato agli aggiornamenti dei metadati del file. – Alex

2

Ecco una versione di Windows del mio codice synctest. L'ho eseguito solo all'interno di un VirtualBox VM, quindi non penso di avere alcun numero utile per il confronto, ma potresti dare una possibilità di confronto con i tuoi numeri C# sulla tua macchina. Sto passando OPEN_ALWAYS a CreateFile, quindi riutilizzerà il file esistente. Cambia questo flag in CREATE_ALWAYS se vuoi testare di nuovo ogni volta con un file vuoto.

Una cosa che ho notato è che i risultati erano molto più veloci la prima volta che eseguivo questo programma. Forse NTFS non è molto efficiente nel sovrascrivere i dati esistenti e gli effetti di frammentazione dei file si sono presentati nelle esecuzioni successive.

#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); 
    } 
} 
+0

Grazie Howard, lo gestirò anch'io. Potrebbe far luce sui risultati delle mie repliche usando un file completamente scritto. Sto solo aggiungendo quelli alle mie domande. Vedrai che rimane un comportamento inspiegabile e inaspettato, mentre la spiegazione che hai fornito si adatta bene ad alcuni dei cambiamenti delle prestazioni osservati in quel test. – Alex

Problemi correlati