如何在毫秒级同步Python进程?
我试图同时在两个内核上运行两个Python函数。每个进程运行一个很长的循环(理论上是无限循环)。重要的是,它们同时保持同步,即使是最轻微的延迟也可能导致长期问题 我想我的问题是我像这样连续运行它们如何在毫秒级同步Python进程?,python,python-3.x,parallel-processing,multiprocessing,python-multiprocessing,Python,Python 3.x,Parallel Processing,Multiprocessing,Python Multiprocessing,我试图同时在两个内核上运行两个Python函数。每个进程运行一个很长的循环(理论上是无限循环)。重要的是,它们同时保持同步,即使是最轻微的延迟也可能导致长期问题 我想我的问题是我像这样连续运行它们 # define the processes and assign them functions first_process = multiprocessing.Process(name='p1', target='first_function') second_process = multipro
# define the processes and assign them functions
first_process = multiprocessing.Process(name='p1', target='first_function')
second_process = multiprocessing.Process(name='p2', target='second_function')
# start the processes
first_process.start()
second_process.start()
我在每个函数的开头打印了time.time()
,以测量时差。结果是:
first function time: 1553812298.9244068
second function time: 1553812298.9254067
差值为0.000999275207519531
秒。如前所述,这种差异将对长期产生重大影响
总之,如何在两个不同的内核上同时运行两个函数?
如果Python无法做到这一点,我还应该检查哪些其他选项?您可以为每个进程指定一个
多处理.Queue
对象,并在函数开始时为进程指定一个,使用multiprocessing.queue.put
将一个项目放入另一个进程的队列中,然后立即尝试使用multiprocessing.queue.get
将其自己的队列出列。由于multiprocessing.Queue.get
会一直阻塞,直到队列中有一个项目,因此这将有效地同步两个进程:
import multiprocessing
import time
def func(queue_self, queue_other):
queue_other.put(None)
queue_self.get()
print(time.time())
q1 = multiprocessing.Queue()
q2 = multiprocessing.Queue()
p1 = multiprocessing.Process(target=func, args=(q1, q2))
p2 = multiprocessing.Process(target=func, args=(q2, q1))
if __name__ == '__main__':
p1.start()
p2.start()
样本输出:
1553814412.7520192
1553814412.7520192
你所要求的并不是通常的操作系统应该提供的。您有操作系统调度、核心迁移、通过cpu热力学变化的时钟速度、不同的缓存命中和未命中等。可以提高流程优先级,并将流程固定到某些核心(对此进行调查),但这样做不太可能带来稳定的改进。你的操作系统通常比你在这里做得更好 对于真正困难的实时约束,您必须进行研究。此外,您还必须选择一种中级语言(例如C/C++),它允许细粒度内存管理(减少代价高昂的cpu缓存未命中)。你可能要求以不同的方式做一些事情(),所以当我继续向你展示如何获得一些同步时,我不理解这是对你解决任何问题的整个方法的认可
这里选择的武器是一个
多处理障碍。这是一个同步原语,允许指定需要在barrier实例上调用.wait()
的多个执行器(线程/进程)。当指定数量的执行器调用了wait()
时,屏障会同时释放所有等待的执行器。这样,所有执行器都可以在这种屏障操作上同步
请注意,一个这样的操作对于您的要求是不够的。我前面提到的操作系统因素总是会带来混乱,cpu时间将再次偏离同步点。这意味着您必须在特定的时间间隔内一次又一次地重复同步。当然,这会使您损失一些吞吐量。较短的同步间隔意味着平均发散较小
下面您将看到实现该技术的两个函数syncstart\u-foo
只同步一次(如@blhsing的答案),sync\u-foo
每隔sync\u间隔
迭代一次。完成所有迭代后,函数将time.time()
返回到父级,在父级中计算时间增量
import time
from multiprocessing import Process, Barrier, Queue
def syncstart_foo(outqueue, barrier, n_iter):
barrier.wait() # synchronize only once at start
for _ in range(int(n_iter)):
pass # do stuff
outqueue.put(time.time())
def sync_foo(outqueue, barrier, n_iter, sync_interval):
for i in range(int(n_iter)):
if i % sync_interval == 0: # will sync first time for i==0
barrier.wait()
# do stuff
outqueue.put(time.time())
用于运行基准测试的助手函数:
def test_sync():
"""Run test for `sync_foo`."""
special_args = (SYNC_INTERVAL,)
_run_test(sync_foo, special_args)
def test_syncstart():
"""Run test for `syncstart_foo`."""
_run_test(syncstart_foo)
def _run_test(f, special_args=None):
outqueue = Queue()
barrier = Barrier(N_WORKERS)
args = (outqueue, barrier, N_ITER)
if special_args:
args += special_args
pool = [Process(target=f, args=args) for _ in range(N_WORKERS)]
print(f'starting test for {f.__name__}')
for p in pool:
p.start()
results = [outqueue.get() for _ in range(N_WORKERS)]
for p in pool:
p.join()
print(f"delta: {(abs(results[1] - results[0])) * 1e3:>{6}.{2}f} ms")
print("-" * 60)
主要条目:
if __name__ == '__main__':
N_WORKERS = 2
N_ITER = 50e6 # 1e6 == 1M
SYNC_INTERVAL = 250_000 # synchronize every x iterations
for _ in range(5):
test_syncstart()
test_sync()
示例输出:
同步启动的启动测试\u foo
增量:28.90毫秒
------------------------------------------------------------
正在启动sync_foo的测试
增量:1.38毫秒
------------------------------------------------------------
同步启动的启动测试\u foo
增量:70.33毫秒
------------------------------------------------------------
正在启动sync_foo的测试
增量:0.33毫秒
------------------------------------------------------------
同步启动的启动测试\u foo
增量:4.45毫秒
------------------------------------------------------------
正在启动sync_foo的测试
增量:0.17毫秒
------------------------------------------------------------
同步启动的启动测试\u foo
增量:168.80毫秒
------------------------------------------------------------
正在启动sync_foo的测试
增量:0.30毫秒
------------------------------------------------------------
同步启动的启动测试\u foo
增量:79.42毫秒
------------------------------------------------------------
正在启动sync_foo的测试
增量:1.24毫秒
------------------------------------------------------------
进程已完成,退出代码为0
您可以看到,像
syncstart\u foo
那样只同步一次是不够的。您不能使用time
或datetime
模块使它们同步吗?比如,让它们等到同一时间才开始执行?您如何确保它们在不同的内核上运行?你有多少内核,你在运行什么操作系统?你说它们必须在同一时间启动,然后你说它们必须在很长时间内保持同步-请问哪一个?什么误差是允许的?最后,恕我直言,这个需求是如何/为什么产生的?这不是Python的问题,而是硬件问题、操作系统调度程序问题或更一般的并行处理问题。你能描述一下为什么你必须让这些过程完美地并行运行吗?因为有可能有一个更好的解决方案,你正试图实现的(在许多情况下,由于许多原因,这几乎是不可能的)?这是天才!