. net原生版本比使用ReadAsync调用的调试版本慢得多



所以我在我的应用程序中发现了一个非常奇怪的问题,事实证明这是由。net原生编译器由于某种原因引起的。

我有一个比较两个文件内容的方法,它工作得很好。对于两个400kb的文件,在调试模式下在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;
        }
    }
}

现在,我完全不知道为什么同样的方法在使用。net Native工具链编译时从0.4秒一直到超过15秒。

我使用单个ReadAsync调用来读取整个文件来修复此问题,然后我从结果中生成两个MD5哈希并比较两者。在我的Lumia 930上,即使在发布模式下,这种方法也只需要0.4秒左右。

我仍然对这个问题很好奇,我想知道为什么会发生这种情况。

提前感谢您的帮助!

编辑:所以我已经调整了我的方法,以减少实际的IO操作的数量,这是结果,它看起来像它的工作很好到目前为止。

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;
}

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

OpenStreamForReadAsync似乎没有使用缓冲流。所以你的8字节请求实际上是每次请求8字节。即使使用固态硬盘,也非常慢。

但是,您不需要一次读取整个文件。通常的方法是找到一个合理的缓冲区大小来预读;像一次读取1kib这样的操作应该可以解决整个问题,而不需要您立即将整个文件加载到内存中。您可以在文件和读取之间使用BufferedStream来为您处理此问题。如果您愿意冒险,您可以在CPU处理完成之前发出下一个读请求—尽管考虑到I/o的工作量,这很可能不会对您的性能有多大帮助。 在异步I/O方面,. net原生的开销似乎比托管的。net要大得多,这将使那些微小的异步调用成为一个更大的问题。

最新更新