【问题标题】:What can I do to improve socket performance in Python 3?我可以做些什么来提高 Python 3 中的套接字性能?
【发布时间】:2014-06-16 03:26:13
【问题描述】:

首发

我有一个运行时间很长的程序,其中大约 97% 的性能依赖于 ftp.retrlines 和 ftp.retrbinary 调用创建的套接字对象。我已经使用进程和线程来并行化程序。我还能做些什么来加快速度吗?

示例代码:

# Get file list
ftpfilelist = []
ftp.retrlines('NLST %s' % ftp_directory, ftpfilelist.append)
... filter file list, this part takes almost no time ...
# Download a file
with open(path, 'wb') as fout:
    ftp.retrbinary('RETR %s' % ftp_path, fout.write)

cProfiler 的输出:

5890792 function calls (5888775 primitive calls) in 548.883 seconds

Ordered by: internal time
List reduced from 843 to 50 due to restriction <50>

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
  9166  249.154    0.027  249.154    0.027 {method 'recv_into' of '_socket.socket' objects}
 99573  230.489    0.002  230.489    0.002 {method 'recv' of '_socket.socket' objects}
  1767   53.113    0.030   53.129    0.030 {method 'connect' of '_socket.socket' objects}
 98808    2.839    0.000    2.839    0.000 {method 'write' of '_io.BufferedWriter' objects}

跟进

支持 python 3.4.1 的 gevent fork (https://github.com/fantix/gevent) 的结果:

7645675 function calls (7153156 primitive calls) in 301.813 seconds

Ordered by: internal time
List reduced from 948 to 50 due to restriction <50>

ncalls       tottime  percall  cumtime  percall filename:lineno(function)
107541/4418  281.228    0.003  296.499    0.067 gevent/hub.py:354(wait)
99885/59883    4.466    0.000  405.922    0.007 gevent/_socket3.py:248(recv)
99097          2.244    0.000    2.244    0.000 {method 'write' of '_io.BufferedWriter' objects}
111125/2796    1.036    0.000    0.017    0.000 gevent/hub.py:345(switch)
107543/2788    1.000    0.000    0.039    0.000 gevent/hub.py:575(get)

concurrent.futures.ThreadPool 的结果:

5319963 function calls (5318875 primitive calls) in 359.541 seconds

Ordered by: internal time
List reduced from 872 to 50 due to restriction <50>

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
    31  349.876   11.286  349.876   11.286 {method 'acquire' of '_thread.lock' objects}
  2652    3.293    0.001    3.293    0.001 {method 'recv' of '_socket.socket' objects}
310270    0.790    0.000    0.790    0.000 {method 'timetuple' of 'datetime.date' objects}
    25    0.661    0.026    0.661    0.026 {method 'recv_into' of '_socket.socket' objects}

结论: 对于我的用例,gevent 将性能提高了大约 20%!

【问题讨论】:

    标签: python performance ftp ftplib


    【解决方案1】:

    查看gevent。它可以修补您正在使用的任何库(例如您的 FTP 库),以通过使用协作线程来提高套接字性能。

    一般的前提是线程程序在处理繁重的 I/O 程序时效率不高,因为调度程序不知道线程是否正在等待网络操作,因此当前线程可能被调度但也浪费时间等待 I/O,而其他线程实际上可能正在工作。

    使用 gevent,只要您的线程(称为 greenlet)遇到阻塞网络调用,它就会自动切换到另一个 greenlet。通过这种机制,您的线程/greenlets 可以充分发挥其潜力。

    以下是对该库的精彩介绍: http://www.gevent.org/intro.html#example

    【讨论】:

      【解决方案2】:

      在我看来,cProfile 正在计算函数中花费的总时间,例如用户空间中的时间以及系统时间,它在内核中等待。这意味着像 retrbinaryretrlines 这样的函数将包括从网络获取数据所需的时间,并且您的 ftp 服务器提供数据的速度越慢,花费在这些函数上的时间就越多。

      我建议您根据调用 time(1) 或使用 os.times() 对分析器结果进行完整性检查。您可能会看到,该进程大部分时间都在等待数据(系统时间),因此您没有太多可以优化的地方。

      【讨论】:

        猜你喜欢
        • 2010-09-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-05-20
        • 2012-03-13
        • 2014-09-06
        • 1970-01-01
        相关资源
        最近更新 更多