【问题标题】:Is there an optimal byte size for sending data over a network?是否有通过网络发送数据的最佳字节大小?
【发布时间】:2014-04-09 01:23:57
【问题描述】:

我认为 100 字节太小了,并且可能会减慢所有写入的较大文件传输速度,但是像 1MB 这样的大小似乎太多了。有人对通过网络发送数据的每次写入的最佳字节块有任何建议吗?

为了详细说明,我正在实现通过网络连接发送数据并显示发送数据的进度。我注意到如果我以每次写入大约 100 字节的速度发送大文件,它会非常慢,但进度条效果很好。但是,如果我以每次写入 1M 的速度发送,它会快得多,但由于发送的块更大,进度条就不能很好地工作。

【问题讨论】:

  • 会不会取决于发送协议?
  • 字节大小为 0 可以优化每个数据包的速度,但对于整体传输时间来说不是最佳的。
  • 我同意 Mitch Wheat 的观点 - 如果您使用 TCP,那么除非您启用了 NODELAY 或刷新,否则堆栈发送的数据包无论如何都与您的发送调用不对应。检查与输出缓冲区相关的 ioctl - 您可以改进进度条或写入缓冲区大小的块。

标签: c network-programming


【解决方案1】:

不,没有通用的最佳字节大小。

TCP 数据包会产生碎片,尽管假设从这里到目的地的所有东西都是具有巨大数据包大小的真正以太网会很好,但现实情况是,即使您可以获得所有单个网络的数据包大小您发送的数据包中,您发送的每个数据包都可能通过互联网采用不同的路径。

这不是你可以“解决”的问题,也没有通用的理想尺寸。

尽快将数据提供给操作系统和 TCP/IP 堆栈,它会根据网络连接动态调整数据包大小(您应该看到他们用于此优化的代码 - 这真的非常有趣. 至少在更好的堆栈上。)

如果您控制所有正在使用的网络和堆栈以及客户端/服务器之间的所有网络和堆栈,那么您可以进行一些手动调整。但一般情况下,即使那样,您也必须非常了解网络和您发送的数据,然后我才会建议您接近它。

-亚当

【讨论】:

    【解决方案2】:

    如果可以的话,就让IP栈来处理吧;大多数操作系统已经内置了很多优化。例如,Vista 将动态改变各种参数以最大化吞吐量;事后猜测算法不太可能是有益的。

    在高阶语言中尤其如此,远离实际的线路,如 C#;你和实际的 TCP/IP 数据包之间有足够的层,我希望你的代码对吞吐量的影响相对较小。

    在最坏的情况下,为自己测试各种情况下的各种消息大小;很少有解决方案是一刀切的。

    【讨论】:

      【解决方案3】:

      如果您在以太网上使用 TCP/IP,则最大数据包大小约为 1500 字节。如果您尝试一次发送多个数据,则数据将被拆分为多个数据包,然后再通过网络发送出去。如果您的应用程序中的数据已经打包,那么您可能希望选择小于 1500 的数据包大小,以便在发送完整数据包时,底层堆栈不必将其分解。例如,如果您进行的每次发送都是 1600 字节,则 TCP 堆栈将不得不为每次发送发送两个数据包,而第二个数据包大部分是空的。这是相当低效的。

      话虽如此,我不知道这会对性能产生多大的明显影响。

      【讨论】:

      • 最大 ETHERNET 数据包大小约为 1500 字节。最大 TCP/IP 数据包大小约为 65k 字节,但除特殊情况外,始终会被分割成更小的数据包。
      • 许多以太网端口(尤其是 1Gb)的 MTU 大于 1500。
      【解决方案4】:

      创建一个名为 CalcChunkSize 的函数 向您的类添加一些私有变量:

      Private PreferredTransferDuration As Integer = 1800 ' milliseconds, the timespan the class will attempt to achieve for each chunk, to give responsive feedback on the progress bar.
      Private ChunkSizeSampleInterval As Integer = 15    ' interval to update the chunk size, used in conjunction with AutoSetChunkSize. 
      Private ChunkSize As Integer = 16 * 1024           ' 16k by default  
      Private StartTime As DateTime
      Private MaxRequestLength As Long = 4096            ' default, this is updated so that the transfer class knows how much the server will accept      
      

      在每次下载块之前,检查是否使用 ChunkSizeSampleInterval 计算新块大小的时间

                  Dim currentIntervalMod As Integer = numIterations Mod Me.ChunkSizeSampleInterval
                  If currentIntervalMod = 0 Then
                      Me.StartTime = DateTime.Now
                  ElseIf currentIntervalMod = 1 Then
                      Me.CalcChunkSize()
                  End If
      

      numIterations 在下载循环之外设置为 0,并且在每个下载的块设置为 numIterations += 1

      让 CalcChunkSize 这样做:

      Protected Sub CalcAndSetChunkSize()
          ' chunk size calculation is defined as follows 
          ' * in the examples below, the preferred transfer time is 1500ms, taking one sample. 
          ' * 
          ' * Example 1 Example 2 
          ' * Initial size = 16384 bytes (16k) 16384 
          ' * Transfer time for 1 chunk = 800ms 2000 ms 
          ' * Average throughput / ms = 16384b / 800ms = 20.48 b/ms 16384 / 2000 = 8.192 b/ms 
          ' * How many bytes in 1500ms? = 20.48 * 1500 = 30720 bytes 8.192 * 1500 = 12228 bytes 
          ' * New chunksize = 30720 bytes (speed up) 12228 bytes (slow down from original chunk size) 
          ' 
      
          Dim transferTime As Double = DateTime.Now.Subtract(Me.StartTime).TotalMilliseconds
          Dim averageBytesPerMilliSec As Double = Me.ChunkSize / transferTime
          Dim preferredChunkSize As Double = averageBytesPerMilliSec * Me.PreferredTransferDuration
          Me.ChunkSize = CInt(Math.Min(Me.MaxRequestLength, Math.Max(4 * 1024, preferredChunkSize)))
          ' set the chunk size so that it takes 1500ms per chunk (estimate), not less than 4Kb and not greater than 4mb // (note 4096Kb sometimes causes problems, probably due to the IIS max request size limit, choosing a slightly smaller max size of 4 million bytes seems to work nicely) 
      End Sub
      

      然后在请求下一个块时只需使用 ChunkSize。

      我在 Tim_mackey 的“使用 MTOM Web 服务和 .Net 2.0 以块发送文件”中发现了这一点,并且我发现动态计算最有效的块大小非常有用。

      完整的源代码在这里:http://www.codeproject.com/KB/XML/MTOMWebServices.aspx

      这里是作者:http://www.codeproject.com/script/Membership/Profiles.aspx?mid=321767

      【讨论】:

        【解决方案5】:

        我相信你的问题是你使用阻塞套接字而不是非阻塞套接字。

        当您使用阻塞套接字并发送 1M 数据时,网络堆栈可以等待所有数据放入缓冲区中,如果缓冲区已满,您将被阻塞并且您的进度条将等待整个1M 被接受到缓冲区,这可能需要一段时间,你的进度条会跳动。

        但是,如果您使用非阻塞套接字,则无论您使用的缓冲区大小都不会阻塞,并且您需要自己使用 select/poll/epoll/whatever-works-on-your-platform(select 是虽然最便携)。这样,您的进度条将快速更新并反映最准确的信息。

        请注意,在发送方,进度条已部分损坏,因为内核会缓冲一些数据,并且您将在对方真正收到数据之前达到 100%。解决此问题的唯一方法是,如果您的协议包含对接收方接收到的数据量的回复。

        正如其他人所说,再次猜测操作系统和网络大多是徒劳的,如果您继续使用阻塞套接字,请选择一个足够大的大小以包含比单个数据包更多的数据,这样您就不会发送太少的数据一个数据包,因为这会不必要地降低您的吞吐量。我会选择像 4K 这样的东西,一次至少包含两个数据包。

        【讨论】:

          【解决方案6】:

          我要补充的一件事是,对于给定的以太网连接,发送一个小数据包的时间与发送一个大数据包的时间差不多。正如其他人所说:如果您只是发送数据流,请让系统处理它。但是,如果您担心来回发送单个短消息,典型以太网数据包大约是 1500 字节 - 只要您保持它在下面就可以了。

          【讨论】:

          • 在 90 年代末的网络计算中有一篇文章,他们调查了应用程序中的性能问题,发现应用程序每个数据包发送 1 个字节。他们绘制了开销惩罚的图表。我在他们的网站上找不到这篇文章,可能太旧了。经典的故事!
          【解决方案7】:

          您需要使用Path MTU Discovery,或使用良好的默认值(即小于 1500 字节)。

          【讨论】:

            【解决方案8】:

            如果您还没有,您可以进行的一项实证测试当然是使用嗅探器(tcpdump、Wireshark 等)并查看使用其他软件进行上传/下载时达到的数据包大小。这可能会给你一个提示。

            【讨论】:

              【解决方案9】:

              这是您需要的公式:

              int optimalChunkSize = totalDataSize / progressBar1.Width;
              

              使用它,您发送的每个块都会将进度条增加 1 个像素。就用户反馈而言,比这更小的块大小是没有意义的。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2013-11-24
                • 2017-03-17
                相关资源
                最近更新 更多