芹菜在子流程上的重要减速
Celery important slowdown on subprocess
在 Web 应用程序的上下文中,我最近将一些耗时的处理(乳胶渲染)从我的 "main program" 切换到 celery。
虽然在开发期间它在我的笔记本电脑上运行得非常好,但在我的生产服务器上执行此处理所需的时间增加了两倍多。
我将问题缩小到核心处理本身,并对其进行了概要分析,但我完全不知道是什么导致了这种差异。
核心处理
def process(content, passes=2):
tmp_dir = tempfile.mkdtemp()
with open(os.path.join(tmp_dir, 'content.tex'), 'wb') as f:
f.write(content.encode('utf-8'))
pr = cProfile.Profile()
pr.enable()
for _ in range(passes):
process = subprocess32.Popen(
[
'/path/to/lualatex',
'--interaction',
'nonstopmode',
'--output-directory',
tmp_dir,
'--jobname',
'output',
'content.tex',
],
cwd=tmp_dir,
stdout=FNULL,
stderr=FNULL,
bufsize=0,
)
process.wait()
if process.returncode != 0:
with open(os.path.join(tmp_dir, 'output.log'), 'rb') as f:
raise LatexError(f.read())
pr.disable()
pr.dump_stats('/path/to/profile')
with open(os.path.join(tmp_dir, 'output.pdf'), 'rb') as pdf:
pdf_bytes = pdf.read()
shutil.rmtree(tmp_dir)
return pdf_bytes
简介"outside"芹菜
80 function calls in 1.973 seconds
Ordered by: internal time
ncalls tottime percall cumtime percall filename:lineno(function)
2 1.969 0.985 1.969 0.985 {posix.waitpid}
2 0.002 0.001 0.002 0.001 {_posixsubprocess.fork_exec}
2 0.001 0.001 0.001 0.001 {posix.read}
2 0.000 0.000 0.003 0.002 /home/admin/.envs/sydpnl/lib/python2.7/site-packages/subprocess32.py:1323(_execute_child)
2 0.000 0.000 1.969 0.985 /home/admin/.envs/sydpnl/lib/python2.7/site-packages/subprocess32.py:1644(wait)
2 0.000 0.000 0.003 0.002 /home/admin/.envs/sydpnl/lib/python2.7/site-packages/subprocess32.py:713(__init__)
4 0.000 0.000 1.970 0.493 /home/admin/.envs/sydpnl/lib/python2.7/site-packages/subprocess32.py:540(_eintr_retry_call)
2 0.000 0.000 0.000 0.000 /home/admin/.envs/sydpnl/lib/python2.7/site-packages/subprocess32.py:1578(_handle_exitstatus)
2 0.000 0.000 0.000 0.000 /home/admin/.envs/sydpnl/lib/python2.7/posixpath.py:127(dirname)
2 0.000 0.000 1.969 0.985 /home/admin/.envs/sydpnl/lib/python2.7/site-packages/subprocess32.py:1629(_try_wait)
4 0.000 0.000 0.000 0.000 {posix.close}
2 0.000 0.000 0.000 0.000 {_posixsubprocess.cloexec_pipe}
1 0.000 0.000 0.000 0.000 /home/admin/.envs/sydpnl/lib/python2.7/site-packages/subprocess32.py:872(__del__)
2 0.000 0.000 0.000 0.000 /home/admin/.envs/sydpnl/lib/python2.7/site-packages/subprocess32.py:1245(_get_handles)
10 0.000 0.000 0.000 0.000 {isinstance}
2 0.000 0.000 0.000 0.000 {sorted}
2 0.000 0.000 0.000 0.000 /home/admin/.envs/sydpnl/lib/python2.7/site-packages/subprocess32.py:525(_cleanup)
2 0.000 0.000 0.000 0.000 {posix.WIFSIGNALED}
3 0.000 0.000 0.000 0.000 {method 'get' of 'dict' objects}
2 0.000 0.000 0.000 0.000 {method 'release' of 'thread.lock' objects}
2 0.000 0.000 0.000 0.000 {method 'rfind' of 'str' objects}
1 0.000 0.000 0.000 0.000 {getattr}
2 0.000 0.000 0.000 0.000 {thread.allocate_lock}
4 0.000 0.000 0.000 0.000 {method 'fileno' of 'file' objects}
2 0.000 0.000 0.000 0.000 /home/admin/.envs/sydpnl/lib/python2.7/site-packages/subprocess32.py:1355(fs_encode)
2 0.000 0.000 0.000 0.000 {method 'rstrip' of 'str' objects}
2 0.000 0.000 0.000 0.000 {method 'acquire' of 'thread.lock' objects}
1 0.000 0.000 0.000 0.000 {range}
1 0.000 0.000 0.000 0.000 /home/admin/.envs/sydpnl/lib/python2.7/site-packages/subprocess32.py:1593(_internal_poll)
2 0.000 0.000 0.000 0.000 {posix.WEXITSTATUS}
2 0.000 0.000 0.000 0.000 {sys.getfilesystemencoding}
2 0.000 0.000 0.000 0.000 {len}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
2 0.000 0.000 0.000 0.000 {posix.WIFEXITED}
2 0.000 0.000 0.000 0.000 {method 'add' of 'set' objects}
配置芹菜
80 function calls in 6.260 seconds
Ordered by: internal time
ncalls tottime percall cumtime percall filename:lineno(function)
2 6.256 3.128 6.256 3.128 {posix.waitpid}
2 0.002 0.001 0.002 0.001 {_posixsubprocess.fork_exec}
2 0.001 0.001 0.001 0.001 {posix.read}
2 0.000 0.000 0.003 0.002 /home/admin/.envs/sydpnl/lib/python2.7/site-packages/subprocess32.py:1323(_execute_child)
2 0.000 0.000 0.003 0.002 /home/admin/.envs/sydpnl/lib/python2.7/site-packages/subprocess32.py:713(__init__)
2 0.000 0.000 6.256 3.128 /home/admin/.envs/sydpnl/lib/python2.7/site-packages/subprocess32.py:1644(wait)
4 0.000 0.000 6.258 1.564 /home/admin/.envs/sydpnl/lib/python2.7/site-packages/subprocess32.py:540(_eintr_retry_call)
4 0.000 0.000 0.000 0.000 {posix.close}
2 0.000 0.000 6.256 3.128 /home/admin/.envs/sydpnl/lib/python2.7/site-packages/subprocess32.py:1629(_try_wait)
2 0.000 0.000 0.000 0.000 /home/admin/.envs/sydpnl/lib/python2.7/site-packages/subprocess32.py:1578(_handle_exitstatus)
2 0.000 0.000 0.000 0.000 /home/admin/.envs/sydpnl/lib/python2.7/posixpath.py:127(dirname)
2 0.000 0.000 0.000 0.000 {_posixsubprocess.cloexec_pipe}
2 0.000 0.000 0.000 0.000 /home/admin/.envs/sydpnl/lib/python2.7/site-packages/subprocess32.py:1245(_get_handles)
1 0.000 0.000 0.000 0.000 /home/admin/.envs/sydpnl/lib/python2.7/site-packages/subprocess32.py:872(__del__)
2 0.000 0.000 0.000 0.000 {sorted}
10 0.000 0.000 0.000 0.000 {isinstance}
2 0.000 0.000 0.000 0.000 {posix.WIFSIGNALED}
2 0.000 0.000 0.000 0.000 /home/admin/.envs/sydpnl/lib/python2.7/site-packages/subprocess32.py:525(_cleanup)
3 0.000 0.000 0.000 0.000 {method 'get' of 'dict' objects}
2 0.000 0.000 0.000 0.000 /home/admin/.envs/sydpnl/lib/python2.7/site-packages/subprocess32.py:1355(fs_encode)
2 0.000 0.000 0.000 0.000 {method 'release' of 'thread.lock' objects}
2 0.000 0.000 0.000 0.000 {method 'rfind' of 'str' objects}
4 0.000 0.000 0.000 0.000 {method 'fileno' of 'file' objects}
1 0.000 0.000 0.000 0.000 {getattr}
2 0.000 0.000 0.000 0.000 {thread.allocate_lock}
2 0.000 0.000 0.000 0.000 {method 'acquire' of 'thread.lock' objects}
2 0.000 0.000 0.000 0.000 {method 'add' of 'set' objects}
2 0.000 0.000 0.000 0.000 {method 'rstrip' of 'str' objects}
1 0.000 0.000 0.000 0.000 {range}
1 0.000 0.000 0.000 0.000 /home/admin/.envs/sydpnl/lib/python2.7/site-packages/subprocess32.py:1593(_internal_poll)
2 0.000 0.000 0.000 0.000 {posix.WEXITSTATUS}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
2 0.000 0.000 0.000 0.000 {len}
2 0.000 0.000 0.000 0.000 {posix.WIFEXITED}
2 0.000 0.000 0.000 0.000 {sys.getfilesystemencoding}
旁白
我的服务器上 运行 Debian 7.8 和 Python 2.7.3。
编辑 2015 年 1 月 4 日
稍微更改了代码和配置文件以突出显示(我认为)问题所在。
我正在回答(并结束)这个问题以避免 https://xkcd.com/979/。
看来问题出在我用来管理celery进程的进程管理器(马戏团)。 Circus 可能以某种方式减少了流程资源。
切换到另一个进程管理器解决了这个问题。
在 Web 应用程序的上下文中,我最近将一些耗时的处理(乳胶渲染)从我的 "main program" 切换到 celery。
虽然在开发期间它在我的笔记本电脑上运行得非常好,但在我的生产服务器上执行此处理所需的时间增加了两倍多。
我将问题缩小到核心处理本身,并对其进行了概要分析,但我完全不知道是什么导致了这种差异。
核心处理
def process(content, passes=2):
tmp_dir = tempfile.mkdtemp()
with open(os.path.join(tmp_dir, 'content.tex'), 'wb') as f:
f.write(content.encode('utf-8'))
pr = cProfile.Profile()
pr.enable()
for _ in range(passes):
process = subprocess32.Popen(
[
'/path/to/lualatex',
'--interaction',
'nonstopmode',
'--output-directory',
tmp_dir,
'--jobname',
'output',
'content.tex',
],
cwd=tmp_dir,
stdout=FNULL,
stderr=FNULL,
bufsize=0,
)
process.wait()
if process.returncode != 0:
with open(os.path.join(tmp_dir, 'output.log'), 'rb') as f:
raise LatexError(f.read())
pr.disable()
pr.dump_stats('/path/to/profile')
with open(os.path.join(tmp_dir, 'output.pdf'), 'rb') as pdf:
pdf_bytes = pdf.read()
shutil.rmtree(tmp_dir)
return pdf_bytes
简介"outside"芹菜
80 function calls in 1.973 seconds
Ordered by: internal time
ncalls tottime percall cumtime percall filename:lineno(function)
2 1.969 0.985 1.969 0.985 {posix.waitpid}
2 0.002 0.001 0.002 0.001 {_posixsubprocess.fork_exec}
2 0.001 0.001 0.001 0.001 {posix.read}
2 0.000 0.000 0.003 0.002 /home/admin/.envs/sydpnl/lib/python2.7/site-packages/subprocess32.py:1323(_execute_child)
2 0.000 0.000 1.969 0.985 /home/admin/.envs/sydpnl/lib/python2.7/site-packages/subprocess32.py:1644(wait)
2 0.000 0.000 0.003 0.002 /home/admin/.envs/sydpnl/lib/python2.7/site-packages/subprocess32.py:713(__init__)
4 0.000 0.000 1.970 0.493 /home/admin/.envs/sydpnl/lib/python2.7/site-packages/subprocess32.py:540(_eintr_retry_call)
2 0.000 0.000 0.000 0.000 /home/admin/.envs/sydpnl/lib/python2.7/site-packages/subprocess32.py:1578(_handle_exitstatus)
2 0.000 0.000 0.000 0.000 /home/admin/.envs/sydpnl/lib/python2.7/posixpath.py:127(dirname)
2 0.000 0.000 1.969 0.985 /home/admin/.envs/sydpnl/lib/python2.7/site-packages/subprocess32.py:1629(_try_wait)
4 0.000 0.000 0.000 0.000 {posix.close}
2 0.000 0.000 0.000 0.000 {_posixsubprocess.cloexec_pipe}
1 0.000 0.000 0.000 0.000 /home/admin/.envs/sydpnl/lib/python2.7/site-packages/subprocess32.py:872(__del__)
2 0.000 0.000 0.000 0.000 /home/admin/.envs/sydpnl/lib/python2.7/site-packages/subprocess32.py:1245(_get_handles)
10 0.000 0.000 0.000 0.000 {isinstance}
2 0.000 0.000 0.000 0.000 {sorted}
2 0.000 0.000 0.000 0.000 /home/admin/.envs/sydpnl/lib/python2.7/site-packages/subprocess32.py:525(_cleanup)
2 0.000 0.000 0.000 0.000 {posix.WIFSIGNALED}
3 0.000 0.000 0.000 0.000 {method 'get' of 'dict' objects}
2 0.000 0.000 0.000 0.000 {method 'release' of 'thread.lock' objects}
2 0.000 0.000 0.000 0.000 {method 'rfind' of 'str' objects}
1 0.000 0.000 0.000 0.000 {getattr}
2 0.000 0.000 0.000 0.000 {thread.allocate_lock}
4 0.000 0.000 0.000 0.000 {method 'fileno' of 'file' objects}
2 0.000 0.000 0.000 0.000 /home/admin/.envs/sydpnl/lib/python2.7/site-packages/subprocess32.py:1355(fs_encode)
2 0.000 0.000 0.000 0.000 {method 'rstrip' of 'str' objects}
2 0.000 0.000 0.000 0.000 {method 'acquire' of 'thread.lock' objects}
1 0.000 0.000 0.000 0.000 {range}
1 0.000 0.000 0.000 0.000 /home/admin/.envs/sydpnl/lib/python2.7/site-packages/subprocess32.py:1593(_internal_poll)
2 0.000 0.000 0.000 0.000 {posix.WEXITSTATUS}
2 0.000 0.000 0.000 0.000 {sys.getfilesystemencoding}
2 0.000 0.000 0.000 0.000 {len}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
2 0.000 0.000 0.000 0.000 {posix.WIFEXITED}
2 0.000 0.000 0.000 0.000 {method 'add' of 'set' objects}
配置芹菜
80 function calls in 6.260 seconds
Ordered by: internal time
ncalls tottime percall cumtime percall filename:lineno(function)
2 6.256 3.128 6.256 3.128 {posix.waitpid}
2 0.002 0.001 0.002 0.001 {_posixsubprocess.fork_exec}
2 0.001 0.001 0.001 0.001 {posix.read}
2 0.000 0.000 0.003 0.002 /home/admin/.envs/sydpnl/lib/python2.7/site-packages/subprocess32.py:1323(_execute_child)
2 0.000 0.000 0.003 0.002 /home/admin/.envs/sydpnl/lib/python2.7/site-packages/subprocess32.py:713(__init__)
2 0.000 0.000 6.256 3.128 /home/admin/.envs/sydpnl/lib/python2.7/site-packages/subprocess32.py:1644(wait)
4 0.000 0.000 6.258 1.564 /home/admin/.envs/sydpnl/lib/python2.7/site-packages/subprocess32.py:540(_eintr_retry_call)
4 0.000 0.000 0.000 0.000 {posix.close}
2 0.000 0.000 6.256 3.128 /home/admin/.envs/sydpnl/lib/python2.7/site-packages/subprocess32.py:1629(_try_wait)
2 0.000 0.000 0.000 0.000 /home/admin/.envs/sydpnl/lib/python2.7/site-packages/subprocess32.py:1578(_handle_exitstatus)
2 0.000 0.000 0.000 0.000 /home/admin/.envs/sydpnl/lib/python2.7/posixpath.py:127(dirname)
2 0.000 0.000 0.000 0.000 {_posixsubprocess.cloexec_pipe}
2 0.000 0.000 0.000 0.000 /home/admin/.envs/sydpnl/lib/python2.7/site-packages/subprocess32.py:1245(_get_handles)
1 0.000 0.000 0.000 0.000 /home/admin/.envs/sydpnl/lib/python2.7/site-packages/subprocess32.py:872(__del__)
2 0.000 0.000 0.000 0.000 {sorted}
10 0.000 0.000 0.000 0.000 {isinstance}
2 0.000 0.000 0.000 0.000 {posix.WIFSIGNALED}
2 0.000 0.000 0.000 0.000 /home/admin/.envs/sydpnl/lib/python2.7/site-packages/subprocess32.py:525(_cleanup)
3 0.000 0.000 0.000 0.000 {method 'get' of 'dict' objects}
2 0.000 0.000 0.000 0.000 /home/admin/.envs/sydpnl/lib/python2.7/site-packages/subprocess32.py:1355(fs_encode)
2 0.000 0.000 0.000 0.000 {method 'release' of 'thread.lock' objects}
2 0.000 0.000 0.000 0.000 {method 'rfind' of 'str' objects}
4 0.000 0.000 0.000 0.000 {method 'fileno' of 'file' objects}
1 0.000 0.000 0.000 0.000 {getattr}
2 0.000 0.000 0.000 0.000 {thread.allocate_lock}
2 0.000 0.000 0.000 0.000 {method 'acquire' of 'thread.lock' objects}
2 0.000 0.000 0.000 0.000 {method 'add' of 'set' objects}
2 0.000 0.000 0.000 0.000 {method 'rstrip' of 'str' objects}
1 0.000 0.000 0.000 0.000 {range}
1 0.000 0.000 0.000 0.000 /home/admin/.envs/sydpnl/lib/python2.7/site-packages/subprocess32.py:1593(_internal_poll)
2 0.000 0.000 0.000 0.000 {posix.WEXITSTATUS}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
2 0.000 0.000 0.000 0.000 {len}
2 0.000 0.000 0.000 0.000 {posix.WIFEXITED}
2 0.000 0.000 0.000 0.000 {sys.getfilesystemencoding}
旁白
我的服务器上 运行 Debian 7.8 和 Python 2.7.3。
编辑 2015 年 1 月 4 日
稍微更改了代码和配置文件以突出显示(我认为)问题所在。
我正在回答(并结束)这个问题以避免 https://xkcd.com/979/。
看来问题出在我用来管理celery进程的进程管理器(马戏团)。 Circus 可能以某种方式减少了流程资源。
切换到另一个进程管理器解决了这个问题。