Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/72.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 如何在Windows中测试复制文件的最快方法(内存映射与文件I/O)_C_Winapi - Fatal编程技术网

C 如何在Windows中测试复制文件的最快方法(内存映射与文件I/O)

C 如何在Windows中测试复制文件的最快方法(内存映射与文件I/O),c,winapi,C,Winapi,(就本问题而言,我不考虑文件复制API,如CopyFile等) 我试图回答这个问题:如果我需要复制很多大文件,哪种方法最快 我可以想到复制文件的四种基本方法: ReadFile+WriteFile MapViewOfFile将源文件写入内存,将缓冲区写入目标文件 MapViewOfFile将目标文件放入内存,并ReadFile将源文件放入缓冲区 MapViewOfFile两个文件,以及从一个文件到另一个文件的memcpy 此外,在每种情况下,我还可以设置一些选项,例如文件\u标志\u无\u缓冲和

(就本问题而言,我不考虑文件复制API,如
CopyFile
等)

我试图回答这个问题:如果我需要复制很多大文件,哪种方法最快

我可以想到复制文件的四种基本方法:

  • ReadFile
    +
    WriteFile
  • MapViewOfFile
    将源文件写入内存,将缓冲区写入目标文件
  • MapViewOfFile
    将目标文件放入内存,并
    ReadFile
    将源文件放入缓冲区
  • MapViewOfFile
    两个文件,以及从一个文件到另一个文件的
    memcpy
  • 此外,在每种情况下,我还可以设置一些选项,例如
    文件\u标志\u无\u缓冲
    秒大\u页面

    然而,我不知道如何正确地对其进行基准测试。我编写了以下代码:

    #include <stdio.h>
    #include <time.h>
    #include <tchar.h>
    #include <Windows.h>
    
    void MyCopyFile(HANDLE source, HANDLE sink, bool mapsource, bool mapsink)
    {
        LARGE_INTEGER size = { 0 };
        GetFileSizeEx(source, &size);
        HANDLE msource = mapsource ? CreateFileMapping(source, NULL, PAGE_READONLY, 0, 0, NULL) : NULL;
        HANDLE msink = mapsink ? CreateFileMapping(sink, NULL, PAGE_READWRITE, size.HighPart, size.LowPart, NULL) : NULL;
        void const *const psource = mapsource ? MapViewOfFile(msource, FILE_MAP_READ, 0, 0, size.QuadPart) : NULL;
        void *const psink = mapsink ? MapViewOfFile(msink, FILE_MAP_WRITE, 0, 0, size.QuadPart) : NULL;
        clock_t const start = clock();
        unsigned long nw = 0;
        if (mapsource)
        {
            if (mapsink)
            {
                memcpy(psink, psource, size.QuadPart);
                nw = size.QuadPart;
            }
            else
            { WriteFile(sink, psource, size.QuadPart, &nw, NULL); }
        }
        else
        {
            if (mapsink)
            { ReadFile(source, psink, size.QuadPart, &nw, NULL); }
            else
            {
                void *const buf = malloc(size.QuadPart);
                if (!ReadFile(source, buf, size.QuadPart, &nw, NULL)) { fprintf(stderr, "Error reading from file: %u\n", GetLastError()); }
                if (!WriteFile(sink, buf, size.QuadPart, &nw, NULL)) { fprintf(stderr, "Error writing to file: %u\n", GetLastError()); }
                free(buf);
            }
        }
        FlushViewOfFile(psink, size.QuadPart);
        clock_t const end = clock();
        if (mapsource) { UnmapViewOfFile(psource); }
        if (mapsink) { UnmapViewOfFile(psink); }
        if (mapsource) { CloseHandle(msource); }
        if (mapsink) { CloseHandle(msink); }
        if (nw) { fprintf(stderr, "(%d, %d): %u MiB/s\n", mapsource, mapsink, (unsigned int)(size.QuadPart * CLOCKS_PER_SEC / (((long long)(end - start) << 20) + 1))); }
    }
    int main()
    {
        // Request permission to extend file without zeroing, for faster performance
        {
            enum TokenPrivilege { SeManageVolumePrivilege = 28 };
            typedef NTSTATUS NTAPI PRtlAdjustPrivilege(IN TokenPrivilege Privilege, IN BOOLEAN Enable, IN BOOLEAN Client, OUT PBOOLEAN WasEnabled);
            static PRtlAdjustPrivilege &RtlAdjustPrivilege = *(PRtlAdjustPrivilege *)(GetProcAddress(GetModuleHandle(_T("ntdll.dll")), _CRT_STRINGIZE(RtlAdjustPrivilege)));
            BOOLEAN old; RtlAdjustPrivilege(SeManageVolumePrivilege, TRUE, FALSE, &old);
        }
        for (int i = 0;; i++)
        {
            HANDLE source = CreateFile(_T("TempSource.bin"), FILE_READ_DATA | FILE_WRITE_DATA | SYNCHRONIZE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE | FILE_FLAG_NO_BUFFERING | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
            HANDLE sink = CreateFile(_T("TempSink.bin"), FILE_READ_DATA | FILE_WRITE_DATA | SYNCHRONIZE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE | FILE_FLAG_NO_BUFFERING | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
            LARGE_INTEGER size; size.QuadPart = 1 << 26;
            LARGE_INTEGER zero = { 0 };
            SetFilePointerEx(source, size, &size, FILE_BEGIN);
            SetEndOfFile(source);
            SetFileValidData(source, size.QuadPart);
            SetFilePointerEx(source, zero, &zero, FILE_BEGIN);
            SetFilePointerEx(sink, zero, &zero, FILE_BEGIN);
            MyCopyFile(source, sink, i % 2 != 0, i / 2 % 2 != 0);
            FlushFileBuffers(source);
            FlushFileBuffers(sink);
            if ((i % 4) + 1 == 4) { fprintf(stderr, "\n"); }
            CloseHandle(source);
            CloseHandle(sink);
        }
    }
    
    #包括
    #包括
    #包括
    #包括
    void MyCopyFile(句柄源、句柄接收器、bool映射源、bool映射接收器)
    {
    大_整数大小={0};
    GetFileSizeEx(源代码和大小);
    HANDLE msource=mapsource?CreateFileMapping(source,NULL,PAGE_READONLY,0,0,NULL):NULL;
    HANDLE msink=mapsink?CreateFileMapping(sink,NULL,PAGE_READWRITE,size.HighPart,size.LowPart,NULL):NULL;
    void const*const psource=mapsource?MapViewOfFile(msource,FILE\u MAP\u READ,0,0,size.QuadPart):NULL;
    void*const psink=mapsink?MapViewOfFile(msink,FILE\u MAP\u WRITE,0,0,size.QuadPart):NULL;
    时钟常数开始=时钟();
    无符号长nw=0;
    如果(映射源)
    {
    if(地图接收器)
    {
    memcpy(psink、psource、size.QuadPart);
    nw=尺寸。四分之一;
    }
    其他的
    {WriteFile(sink,psource,size.QuadPart,&nw,NULL);}
    }
    其他的
    {
    if(地图接收器)
    {ReadFile(source,psink,size.QuadPart,&nw,NULL);}
    其他的
    {
    void*const buf=malloc(大小为四分之一);
    如果(!ReadFile(source,buf,size.QuadPart,&nw,NULL)){fprintf(stderr,“读取文件时出错:%u\n”,GetLastError());}
    如果(!WriteFile(sink,buf,size.QuadPart,&nw,NULL)){fprintf(stderr,“写入文件时出错:%u\n”,GetLastError());}
    免费(buf);
    }
    }
    平齐视图文件(psink,大小为四分之一);
    时钟常数结束=时钟();
    if(mapsource){UnmapViewOfFile(psource);}
    if(mapsink){UnmapViewOfFile(psink);}
    if(mapsource){CloseHandle(msource);}
    if(mapsink){CloseHandle(msink);}
    
    如果(nw){fprintf(stderr,(%d,%d):%u MiB/s\n',映射源,映射接收器,(unsigned int)(size.QuadPart*CLOCKS_PER_SEC/((long-long)(end-start)我认为这取决于性能跟踪的严重程度。您可以使用计时器来测量自己的时间,也可以考虑使用计时器。ETW是在Windows中跟踪所有性能的方式。此外,ETW是Windows执行高性能事件和跟踪的方式。如果您执行系统性能跟踪,则您正在使用ETW。此外,一旦您的组件与ETW正确连接,您就可以在整个系统中从用户模式组件一直跟踪您的性能,一直跟踪到内核模式。我不是真正的VS用户,但我认为有一些工具可以自动将分析添加到您的组件中。我使用了F1和TDD等工具h可能在某个时候被整合到VS中

    此外,您还可以使用一些疯狂的工具深入研究性能跟踪的ETL文件。例如,您是否对堆碎片或给定堆栈的CPU时间感兴趣?通过适当的性能跟踪,您基本上可以了解(或系统)性能的任何方面

    其中一个基本概念是活动ID。它是在线程本地存储上设置的GUID。它将事件/场景缝合在一起

    首先尝试捕获性能跟踪。找出如何解码ETL文件。开始向代码中添加活动ID。解码ETL文件并开始测量性能或场景

    无论如何,这就是系统性能跟踪的重要性。希望这是一个有用的起点

    如果你不需要那么认真,那就在代码中使用定时器

    void GetSetActivityId(
        _Out_ GUID *pActivityId) 
    {
    GUID activityId = {0};
    GUID guidNull = {0};
    
    TRACE_FUNCTION_ENTRY(LEVEL_INFO);
    
    // check to see if there is already an activity id on the thread
    (void)EventActivityIdControl(EVENT_ACTIVITY_CTRL_GET_ID, &activityId);
    
    if (RtlCompareMemory(&guidNull, &activityId, sizeof(GUID)) == sizeof(GUID)) {
        // we will create and set an activity id because we didn't get one from the thread
        if (EventActivityIdControl(EVENT_ACTIVITY_CTRL_CREATE_ID, &activityId) == ERROR_SUCCESS) {
            (void)EventActivityIdControl(EVENT_ACTIVITY_CTRL_SET_ID, &activityId);
        }
    }
    
    TRACE_FUNCTION_EXIT(LEVEL_COND);
    
    *pActivityId = activityId;
    }  // GetSetActivityId
    

    文件复制速度是一门艺术。它似乎主要取决于您使用的缓冲区大小。理论上,最快的方法应该是使用重叠I/O,这排除了内存映射。内存映射还意味着在32位系统上,您无法复制大于3GB的文件。别忘了文件系统将缓存(部分)您读取的数据,因此如果不以某种方式刷新缓存(即通过重新启动),就无法获得可靠的结果在测试之间。@JonathanPotter:是的,我现在只是在64位系统上测试。不确定为什么重叠I/O会排除内存映射…我不能以重叠的方式读取或写入内存映射文件吗?至于刷新缓存,我确实设置了
    文件标志\u无缓冲
    ,所以这应该足够了,对吗?或者会吗这给了我不切实际的速度?对于重叠的I/O,你可以开始写操作,然后读取更多的数据。有了内存映射,你的memcpy调用直到实际完成才会返回。也许你可以得到一些多线程的东西?我想值得一试。@JonathanPotter:哦,是的,我意识到memcpy可能是低,我只是为了完整性才把它放在那里。但其他4种方法中有2种也是基于内存映射的,而那些方法不使用memcpy。