C#File.ReadAllBytes vs std::ifstream(Windows) 在网络文件的所有字节中,C和C++读取产生不同的结果。C++的长度是C<的三倍。

C#File.ReadAllBytes vs std::ifstream(Windows) 在网络文件的所有字节中,C和C++读取产生不同的结果。C++的长度是C<的三倍。,c#,c++,windows,C#,C++,Windows,C# C++ 使用C结构文件给出与C++ +/p>钻孔的相同结果,我们可以看到它是从KNAL32.DLL调用Read文件,C++中复制相同的代码给出了相同的性能。 HANDLE f_handle = CreateFile(fp, GENERIC_READ, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); DWORD read=0; ReadFile(f_handle, buffer, fs_len, &a

C#

C++

<>使用C结构文件给出与C++ +/p>钻孔的相同结果,我们可以看到它是从KNAL32.DLL调用Read文件,C++中复制相同的代码给出了相同的性能。
HANDLE f_handle = CreateFile(fp, GENERIC_READ, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

DWORD read=0;
ReadFile(f_handle, buffer, fs_len, &read, NULL);

CloseHandle(f_handle);

< P>如果你得到不同的时间,可能是因为.NET默认使用文件IO的4KB缓冲区,这是你C++代码使用的1KB缓冲区的4倍。 编辑:请记住,因为这是一个网络文件,所以每次读取缓冲区时,网络延迟也会增加。因此,缓冲区越小,读取越多,增加的延迟和其他开销就越多


编辑:我没有看到整行代码,也没有看到缓冲区大小上额外的*1024块。因此,缓冲区的大小实际上是1MB。为了公平地比较这两种情况,使用相同的缓冲区大小来实现C++和C++的实现。

< P>我做了一些进一步的测试,并给出了完整的代码/结果。 测试由以下人员设置:

  • 为网络上读取的每个文件创建相同的文件。文件大小~37MB
  • 使用C#main循环调用SLT(std::ifstream),然后交替调用Windows ReadFile方法,调用50个文件(每个文件25个)
  • 对许多不同的读取大小{KB*1、KB*4、KB*32、MB*1、MB*5}重复步骤2
  • 等待20分钟以确保缓存不会影响任何内容,然后重新执行步骤2和步骤3
  • 所有代码都是在visual studio 2015社区版的发布模式下编译的


    C#呼叫代码

    [DllImport(DLL_NAME, CharSet = CharSet.Unicode)]
    public static extern int test(string data_in, int option, int block_size);
    
    ...
    
    string fp_template = @"N:/foo/{0}/bar ({1}).csv";
    
    int KB = 1024;
    int MB = KB * 1024;
    
    int block_id = 0;
    foreach (var block_size in new int[] {KB*1,KB*4,KB*32,MB*1,MB*5 })
    {
        Console.WriteLine("BlockSize (KB): " +(block_size/1024));
        for (int i = 0; i < 50; i++)
        {
            Stopwatch sw_loop = Stopwatch.StartNew();
            ParseBlockNative.test(String.Format(fp_template,block_id, i), i % 2,block_size);
            Console.WriteLine("Option: {0} Taken: {1}", i % 2 == 0 ? "0-SLT" : "1-WIN", sw_loop.ElapsedMilliseconds);
        }
        Console.WriteLine();
        block_id++;
    }
    

    这些测试结果已被取代,请跳到下一节

    从网络读取37MB文件的时间,平均每个数据点超过25个文件

    拿1

    +------------+------+----------+
    | Block_Size | SLT  | Kernel32 |
    +------------+------+----------+
    | 1KB        |  957 |     1065 |
    | 4KB        |  965 |      953 |
    | 32KB       |  952 |      729 |
    | 1MB        | 1015 |      230 |
    | 5MB        |  993 |      231 |
    +------------+------+----------+
    
    进行2次(20分钟后重新进行相同测试)


    评论: 更改读取大小不会改善STL结果。 内核32倍从4KB略微提高到32KB,大部分从32KB提高到1MB

    目前我的直觉是,作为一个网络驱动器,延迟非常高,但带宽很大。许多小请求(~4KB)比较少的大请求(~1MB)慢得多。延迟比我们在常规SSD或HDD上看到的要大得多,因此对更多请求的惩罚更严厉。 即使我告诉STL以1MB块读取,结果表明它忽略了这一点,并请求了大量4KB的读取


    更新-唯一数据

    在上面一节中看到的Kernel32从32KB提高到1MB的速度对我来说有点难以置信。我对每个读取操作都使用一个唯一的文件,但内容是相同的。我担心基于数据内容会有某种程度的缓存。我重新进行了上述测试,但这次我为每个读取操作随机生成了唯一的文件

    下面的每个数据点表示25个不同文件的平均值,其中包含随机生成的内容

    +------------+---------+----------+
    | Block_Size |   SLT   | Kernel32 |
    +------------+---------+----------+
    | 1KB        |  976    |  1,101   |
    | 4KB        |  1,027  |  1,011   |
    | 32KB       |  969    |  768     |
    | 1MB        |  981    |  530     |
    | 5MB        |  1,008  |  541     |
    +------------+---------+----------+
    
    为了完整性,C#的File.ReadAllBytes完成所需的时间约为585ms,接近内核32 1MB的时间

    我将上述测试的一个子集redid为Kernel32,将文件\u FLAG\u NO\u缓冲传递到Kernel32,1MB运行时增加到大约700ms,但仍然明显优于STL

    评论: 在我看来,服务器端网络缓存的影响已经消除。
    我看到的是C#\Kernel32的性能超过了SLT。在SLT中,通过增加读取大小而不增加性能。

    C++缓冲区为1 MIB,不是1。KiB@CodesInChaos该死完整的代码和测试结果添加到另一个POSII中,希望C++代码只是一个简单的例子,它看起来像是缓冲区溢出到了ME。完整的代码和测试结果添加到另一个POST中,需要找到另一个理论,文件。您还需要反向运行测试,以便消除/检测文件系统缓存的影响。最有可能的原因是速度差异。我们的网络确实进行缓存,我可以在初始测试中看到它的效果。在上面的测试中,我通过为每次读取创建一个唯一的文件来纠正这个问题。ie我对SLT和Kerel32的5种不同块大小平均超过25个文件,因此网络上有25*5*2个不同(但相等)的文件。没有文件被读取两次,因此颠倒顺序不会起任何作用。我同意网络缓存是不同的,在我看来,SLT和Kerel32以不同的方式请求数据,后者导致网络缓存。缓存已在数据内容上找到,测试结果已更新,并显示与C#的File.ReadAllBytes一致
    extern "C" int DllExport test(wchar_t* fp,int option,int block_size)
    {
        int fs_len = 38441760;
    
        char* buffer = (char*)malloc(fs_len);
    
        int data_rem = fs_len;
        int data_read = 0;
    
        if (option==0)
        {
            std::ifstream str_in(fp, std::ifstream::binary);
    
            while (data_rem > 0)
            {
                str_in.read(buffer + data_read, min(block_size,data_rem));
                data_rem -= str_in.gcount();
                data_read += str_in.gcount();
            }
        }
        else
        {
            HANDLE f_handle = CreateFile(fp, GENERIC_READ, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    
            while (data_rem > 0)
            {
                DWORD read = 0;
                ReadFile(f_handle, buffer + data_read, min(block_size, data_rem), &read, NULL);
    
                data_rem -= read;
                data_read += read;
            }
    
            CloseHandle(f_handle);
        }
    
        free(buffer);
    
        return data_read;
    }
    
    +------------+------+----------+
    | Block_Size | SLT  | Kernel32 |
    +------------+------+----------+
    | 1KB        |  957 |     1065 |
    | 4KB        |  965 |      953 |
    | 32KB       |  952 |      729 |
    | 1MB        | 1015 |      230 |
    | 5MB        |  993 |      231 |
    +------------+------+----------+
    
    +------------+------+----------+
    | Block_Size | SLT  | Kernel32 |
    +------------+------+----------+
    | 1KB        | 1040 |      999 |
    | 4KB        | 1077 |     1102 |
    | 32KB       | 1079 |      784 |
    | 1MB        | 1028 |      231 |
    | 5MB        | 1035 |      201 |
    +------------+------+----------+
    
    +------------+---------+----------+
    | Block_Size |   SLT   | Kernel32 |
    +------------+---------+----------+
    | 1KB        |  976    |  1,101   |
    | 4KB        |  1,027  |  1,011   |
    | 32KB       |  969    |  768     |
    | 1MB        |  981    |  530     |
    | 5MB        |  1,008  |  541     |
    +------------+---------+----------+