Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/266.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

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/asp.net-core/3.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# 提高大型(1-10 gb)文件流的速度.Net Core_C#_Asp.net Core_Stream_Multipartform Data_Large Files - Fatal编程技术网

C# 提高大型(1-10 gb)文件流的速度.Net Core

C# 提高大型(1-10 gb)文件流的速度.Net Core,c#,asp.net-core,stream,multipartform-data,large-files,C#,Asp.net Core,Stream,Multipartform Data,Large Files,我正在尝试使用多部分数据通过API上传*.iso文件,并将其流式传输到本地文件夹。 我使用了Stream.copyanc(destinationStream),它运行得很慢,但还不错。但现在我需要报告进展情况。因此,我使用定制的CopyTOAsync并向其中添加了一个进度报告。但是,即使与Stream::CopyToASync相比,该方法也非常慢(根本不可接受) public async Task CopyToAsync(Stream source, Stream destination, l

我正在尝试使用多部分数据通过API上传*.iso文件,并将其流式传输到本地文件夹。 我使用了Stream.copyanc(destinationStream),它运行得很慢,但还不错。但现在我需要报告进展情况。因此,我使用定制的CopyTOAsync并向其中添加了一个进度报告。但是,即使与Stream::CopyToASync相比,该方法也非常慢(根本不可接受)

 public async Task CopyToAsync(Stream source, Stream destination, long? contentLength, ICommandContext context, int bufferSize = 81920 )
    {
        var buffer = new byte[bufferSize];
        int bytesRead;
        long totalRead = 0;
        while ((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length)) > 0)
        {
            await destination.WriteAsync(buffer, 0, bytesRead);
            totalRead += bytesRead;
            context.Report(CommandResources.RID_IMAGE_LOADIND, Math.Clamp((uint)((totalRead * 100) / contentLength), 3, 99));
        }
        _logger.Info($"Total read during upload : {totalRead}");
    }
我尝试的是: Stream::CopyToAsync的默认缓冲区大小为81920字节,我首先使用了相同的值,然后尝试将缓冲区大小增加到104857600字节-没有区别

关于如何提高自定义CopyToAsync的性能,您还有其他想法吗?

  • 始终使用
    ConfigureAwait
    await
    为异步继续指定线程同步。
    • 根据平台的不同,省略
      ConfigureAwait
      可能默认与UI线程(WPF、WinForms)或任何线程(ASP.NET核心)同步。如果它与流复制操作中的UI线程同步,那么性能大幅下降也就不足为奇了
    • 如果在线程同步的上下文中运行代码,则
      wait
      语句将被不必要地延迟,因为程序将继续调度到可能正忙的线程
  • 使用一个大小至少为几百KiB的缓冲区,甚至是用于异步操作的兆字节大小的缓冲区,而不是典型的4KiB或80KiB大小的阵列。
  • 如果使用的是
    FileStream
    请确保使用了
    FileOptions.Asynchronous
    useAsync:true
    ,否则
    FileStream
    将通过使用线程池线程而不是Windows的本机异步IO执行阻塞IO来伪造其异步操作
关于您的实际代码-只需使用
Stream::CopyToAsync
,而不是自己重新实现它。如果您想要进度报告,那么考虑子类<代码>流<代码>(作为代理包装)。 下面是我将如何编写您的代码:

  • 你的项目
  • 然后子类
    ProxyStream
    添加对
    IProgress
    的支持:
  • 确保使用
    FileOptions.Asynchronous | FileOptions.SequentialScan
    创建任何
    FileStream
    实例
  • 使用
    CopyToAsync
  • 公共类ProxyStream:ProxyStream
    {
    私有只读IProgress进程;
    私有只读Int64?总计;
    public ProgressProxyStream(Stream Stream、IProgress ProxyStream、Open)
    :基本(流,打开)
    {
    this.progress=progress??抛出新的ArgumentNullException(nameof(progress));
    this.total=stream.CanSeek?stream.Length:(Int64?)null;
    }
    公共重写任务ReadAsync(字节[]缓冲区、Int32偏移量、Int32计数、CancellationToken CancellationToken)
    {
    此.progress.Report((抵销,此.total));
    返回this.Stream.ReadAsync(缓冲区、偏移量、计数、取消令牌);
    }
    }
    

    如果上述
    ProgressProxyStream
    仍会影响性能,那么我敢打赌瓶颈在
    IProgress.Report
    回调目标(我假设它与UI线程同步)ProgressProxyStream(甚至是
    IProgress
    的实现)内将进度报告转储到,而不阻止任何其他IO活动。

    这些文件是什么?您可以使用压缩流传输未压缩的文件。传输一个1GB的文件需要多长时间?您正在将这些文件上载到远程服务器吗?还是所有东西都留在你的本地网络上?我不想告诉你,但你的方法一开始就有缺陷。想要更快的性能?不要发送MULTIPARTFORM-DATA-那里的编码使您的数据从一开始就大30%,因为它必须是ASCII编码的。以searate请求(无格式)将其作为二进制内容进行流式处理。发送必要的元数据aeitehr之前(获取上传url)或作为标题。看看Youtube在他们的SDK中是如何做到的。@ChristophLütjen我怀疑
    ArrayPool
    是否有助于考虑所讨论的代码没有在紧循环中分配数组。这是一个很好的建议,但考虑到它似乎没有考虑到这一点,它并不能真正解释“慢但不太坏”与“非常慢”的区别添加进度报告部分后,OP观察到的差异。他们还尝试了合理增加缓冲区大小。@CeeMcSharpface我敢打赌
    上下文中发生了阻塞线程同步。Report
    call。@Dai,非常感谢。瓶颈实际上在报告方法内部。我感谢你的帮助。
    public class ProgressProxyStream : ProxyStream
    {
        private readonly IProgress<(Int64 soFar, Int64? total)> progress;
        private readonly Int64? total;
    
        public ProgressProxyStream( Stream stream, IProgress<Int64> progress, Boolean leaveOpen )
            : base( stream, leaveOpen ) 
        {
            this.progress = progress ?? throw new ArgumentNullException(nameof(progress));
            this.total = stream.CanSeek ? stream.Length : (Int64?)null;
        }
    
        public override Task<Int32> ReadAsync( Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken )
        {
            this.progress.Report( ( offset, this.total ) );
            return this.Stream.ReadAsync( buffer, offset, count, cancellationToken );
        }
    }