在Python中发送文件的最佳块大小?

在Python中发送文件的最佳块大小?,python,sockets,Python,Sockets,实际上,我正在使用python的(2.7)ftplib将文件发送到FTP服务器,但实际上它使用了socket.sendall。利益的作用如下: def storbinary(self, cmd, fp, blocksize=8192, callback=None, rest=None): """Store a file in binary mode. A new port is created for you. Args: cmd: A STOR command.

实际上,我正在使用python的(2.7)ftplib将文件发送到FTP服务器,但实际上它使用了socket.sendall。利益的作用如下:

def storbinary(self, cmd, fp, blocksize=8192, callback=None, rest=None):
    """Store a file in binary mode.  A new port is created for you.

    Args:
      cmd: A STOR command.
      fp: A file-like object with a read(num_bytes) method.
      blocksize: The maximum data size to read from fp and send over
                 the connection at once.  [default: 8192]
      callback: An optional single parameter callable that is called on
                on each block of data after it is sent.  [default: None]
      rest: Passed to transfercmd().  [default: None]

    Returns:
      The response code.
    """
    self.voidcmd('TYPE I')
    conn = self.transfercmd(cmd, rest)
    while 1:
        buf = fp.read(blocksize)
        if not buf: break
        conn.sendall(buf)
        if callback: callback(buf)

    conn.close()
    return self.voidresp()
我试图选择最佳块大小,或者至少了解影响块大小的因素。该代码目前运行在本地千兆网络上,在Ubuntu内核3.2上对FTP服务器的ping时间为0.2ms(是的,0.2ms,而不是0.2s)。我对TCP窗口缩放和发送/接收/拥塞窗口有相当的了解。我正在通过这个网络发送2GB文件,并且在实践中发现,传输速度随着块大小的增加而增加,使用256KB的块大小可以达到533Mb/s。作为参考,64KB的块大小大约为330Mb/s

我不是在抱怨这些速度,而是想了解为什么256KB的块大小是最佳的。到目前为止,我所发现的一切都表明~64KB是所需的最大数据块大小。我对storebinary函数的子组件进行了计时,以确保发送文件的总时间实际上随着块大小增加到256KB而减少(与读取文件的时间相反)

我传输这些2GB文件的代码最终将在许多网络上运行(尽管是相同的操作系统、内核和python版本)。我担心256KB在其他网络上是次优的,我很好奇为什么256KB的块大小提供了最快的传输速度。如有任何见解,将不胜感激

编辑:对于那些关心我如何独立计时实际socket.sendall调用的人,这里是我用来计时的函数的修改版本。从64KB的数据块到256KB的数据块,读取时间从19秒到14秒,发送时间从18秒到10秒

def storbinary(self, cmd, fp, blocksize=8192, callback=None, rest=None):
    """Store a file in binary mode.  A new port is created for you.

    Args:
      cmd: A STOR command.
      fp: A file-like object with a read(num_bytes) method.
      blocksize: The maximum data size to read from fp and send over
                 the connection at once.  [default: 8192]
      callback: An optional single parameter callable that is called on
                on each block of data after it is sent.  [default: None]
      rest: Passed to transfercmd().  [default: None]

    Returns:
      The response code.
    """
    self.voidcmd('TYPE I')
    conn = self.transfercmd(cmd, rest)
    totalTime = 0
    totalSendTime = 0
    totalCallbackTime = 0
    while 1:
        startTime = time.time()
        buf = fp.read(blocksize)
        endTime = time.time()
        if not buf: break
        totalTime += (endTime - startTime)
        startTime = time.time()
        conn.sendall(buf)
        endTime = time.time()
        totalSendTime += (endTime - startTime)
        startTime = time.time()
        if callback: callback(buf)
        endTime = time.time()
        totalCallbackTime += (endTime - startTime)

    print 'Total read time was %s'%str(totalTime)
    print 'Total send time was %s'%str(totalSendTime)
    print 'Total callback time was %s'%str(totalCallbackTime)
    conn.close()
    return self.voidresp()

ftp中的位基于数据报,因此它们通过固定路径以特定大小的数据包发送。要发送所有数据,您需要确定完整文件的大小,然后在ftp端预期相同的大小。更好的方法是在文件末尾添加一个结束分隔符。因此,当您在ftp端循环文件内容并找到结束分隔符时,您必须停止期望文件中来自同一客户端的更多数据。保持在一个发送中传输的比特的标称大小大约为1024,这是由于各种原因的首选大小(请在谷歌上找到原因),您将很容易找到它。

您可能需要考虑实现这一点,以便块大小能够动态地改变,然后你就可以使用启发式方法来选择一个好的大小,而不必硬编码。你是从硬盘而不是SSD读取数据吗?您的限制因素可能是存储文件的硬盘的读取速度。作为实验,请先尝试将整个文件读取到内存中。这将消除它成为磁盘瓶颈的可能性。@JamieCockburn如果您注意到在我的OP中,我提到我对该函数的实际发送部分进行了计时。当我使用256KB而不是64KB时,在socket.sendall上花费的总时间实际上减少了(对于所有2GB,从~18s减少到~10s)。块大小越大,读取速度也越快,但我目前关注的是发送。我现在考虑的是,当我发送较大的块大小时,socket.send调用返回,而要发送的一些数据仍由操作系统缓冲。因此,当我的程序读取下一个缓冲区时,操作系统正在发送。@maxywb我已经考虑过动态调整块大小,但我们正处于生产阶段,上级不想添加这样的更改。它计划在下一版本的代码中发布,但目前我们正试图根据网络速度和延迟确定一个合适的、恒定的块大小。