C# .NET本机速度比使用ReadAsync调用调试生成速度慢得惊人

C# .NET本机速度比使用ReadAsync调用调试生成速度慢得惊人,c#,xaml,win-universal-app,windows-10,windows-10-universal,C#,Xaml,Win Universal App,Windows 10,Windows 10 Universal,所以我在我的应用程序中发现了一个非常奇怪的问题,结果证明它是由.NET本机编译器出于某种原因造成的 我有一个比较两个文件内容的方法,效果很好。对于两个400KBs的文件,在调试模式下在我的Lumia 930上运行大约需要0.4秒但,在释放模式下,无明显原因,最多需要17秒。代码如下: // Compares the content of the two streams private static async Task<bool> ContentEquals(ulong size,

所以我在我的应用程序中发现了一个非常奇怪的问题,结果证明它是由.NET本机编译器出于某种原因造成的

我有一个比较两个文件内容的方法,效果很好。对于两个400KBs的文件,在调试模式下在我的Lumia 930上运行大约需要0.4秒,在释放模式下,无明显原因,最多需要17秒。代码如下:

// Compares the content of the two streams
private static async Task<bool> ContentEquals(ulong size, [NotNull] Stream fileStream, [NotNull] Stream testStream)
{
    // Initialization
    const int bytes = 8;
    int iterations = (int)Math.Ceiling((double)size / bytes);
    byte[] one = new byte[bytes];
    byte[] two = new byte[bytes];

    // Read all the bytes and compare them 8 at a time
    for (int i = 0; i < iterations; i++)
    {
        await fileStream.ReadAsync(one, 0, bytes);
        await testStream.ReadAsync(two, 0, bytes);
        if (BitConverter.ToUInt64(one, 0) != BitConverter.ToUInt64(two, 0)) return false;
    }
    return true;
}

/// <summary>
/// Checks if the content of two files is the same
/// </summary>
/// <param name="file">The source file</param>
/// <param name="test">The file to test</param>
public static async Task<bool> ContentEquals([NotNull] this StorageFile file, [NotNull] StorageFile test)
{
    // If the two files have a different size, just stop here
    ulong size = await file.GetFileSizeAsync();
    if (size != await test.GetFileSizeAsync()) return false;

    // Open the two files to read them
    try
    {
        // Direct streams
        using (Stream fileStream = await file.OpenStreamForReadAsync())
        using (Stream testStream = await test.OpenStreamForReadAsync())
        {
            return await ContentEquals(size, fileStream, testStream);
        }
    }
    catch (UnauthorizedAccessException)
    {
        // Copy streams
        StorageFile fileCopy = await file.CreateCopyAsync(ApplicationData.Current.TemporaryFolder);
        StorageFile testCopy = await file.CreateCopyAsync(ApplicationData.Current.TemporaryFolder);
        using (Stream fileStream = await fileCopy.OpenStreamForReadAsync())
        using (Stream testStream = await testCopy.OpenStreamForReadAsync())
        {
            // Compare the files
            bool result = await ContentEquals(size, fileStream, testStream);

            // Delete the temp files at the end of the operation
            Task.Run(() =>
            {
                fileCopy.DeleteAsync(StorageDeleteOption.PermanentDelete).Forget();
                testCopy.DeleteAsync(StorageDeleteOption.PermanentDelete).Forget();
            }).Forget();
            return result;
        }
    }
}

从I/O设备一次读取八个字节会导致性能灾难。这就是我们首先使用缓冲读取(和写入)的原因。提交、处理、执行和最终返回I/O请求需要时间

OpenStreamForReadAsync
似乎未使用缓冲流。因此,您的8字节请求实际上是一次请求8字节。即使使用固态驱动器,速度也非常慢

不过,您不需要一次读取整个文件。通常的方法是找到一个合理的缓冲区大小进行预读;像一次读取1kib这样的东西可以解决整个问题,而不需要立即将整个文件加载到内存中。您可以在文件和读取之间使用
BufferedStream
,为您处理此问题。如果你有冒险精神,你可以在CPU处理完成之前发出下一个读取请求——尽管这很可能对你的性能没有多大帮助,因为有多少工作只是I/O


另外,对于异步I/O,.NET本机的开销似乎比托管的.NET大得多,这使得那些微小的异步调用更成问题。减少对较大数据的请求将有所帮助。

谢谢您的解释!我尝试使用AsStreamForRead方法获取缓冲流,并尝试使用不同的缓冲区大小(1KB、2KB、4KB),但运行通常需要10-15秒,我正在处理500KB的文件。这怎么可能,缓冲流不应该解决这个问题吗?@Sergio0694这可能就是.NET native在异步调用中性能不佳的原因。对于真正的I/O操作来说,这没什么大不了的,但当使用缓冲时,它会累加起来。您可能需要使用自己的
byte[]
缓冲区并对其进行迭代,仅使用
ReadAsync
获取另一批。幸运的是,这很容易做到,因为
BitConverter
接受
byte[]
参数的偏移量:)我用更新的方法编辑了我的问题,看起来已经解决了,让我知道你的想法@Sergio0694在我看来很好。虽然我希望您不需要那么大的缓冲区:PI不知道您是否有相同的问题,但请尝试代码,看看它是否有区别。
private static async Task<bool> ContentEquals(ulong size, [NotNull] Stream fileStream, [NotNull] Stream testStream)
{
    // Initialization
    const int bytes = 102400;
    int iterations = (int)Math.Ceiling((double)size / bytes);
    byte[] first = new byte[bytes], second = new byte[bytes];

    // Read all the bytes and compare them 8 at a time
    for (int i = 0; i < iterations; i++)
    {
        // Read the next data chunk
        int[] counts = await Task.WhenAll(fileStream.ReadAsync(first, 0, bytes), testStream.ReadAsync(second, 0, bytes));
        if (counts[0] != counts[1]) return false;
        int target = counts[0];

        // Compare the first bytes 8 at a time
        int j;
        for (j = 0; j < target; j += 8)
        {
            if (BitConverter.ToUInt64(first, j) != BitConverter.ToUInt64(second, j)) return false;
        }

        // Compare the bytes in the last chunk if necessary
        while (j < target)
        {
            if (first[j] != second[j]) return false;
            j++;
        }
    }
    return true;
}