python - Python for 循环:正确实现多处理
问题描述
以下 for 循环是迭代模拟过程的一部分,是计算时间的主要瓶颈:
import numpy as np
class Simulation(object):
def __init__(self,n_int):
self.n_int = n_int
def loop(self):
for itr in range(self.n_int):
#some preceeding code which updates rows_list and diff with every itr
cols_red_list = []
rows_list = list(range(2500)) #row idx for diff where negative element is known to appear
diff = np.random.uniform(-1.323, 3.780, (2500, 300)) #np.random.uniform is just used as toy example
for row in rows_list:
col = next(idx for idx, val in enumerate(diff[row,:]) if val < 0)
cols_red_list.append(col)
# some subsequent code which uses the cols_red_list data
sim1 = Simulation(n_int=10)
sim1.loop()
因此,我尝试使用multiprocessing包将其并行化,以减少计算时间:
import numpy as np
from multiprocessing import Pool, cpu_count
from functools import partial
def crossings(row, diff):
return next(idx for idx, val in enumerate(diff[row,:]) if val < 0)
class Simulation(object):
def __init__(self,n_int):
self.n_int = n_int
def loop(self):
for itr in range(self.n_int):
#some preceeding code which updates rows_list and diff with every
rows_list = list(range(2500))
diff = np.random.uniform(-1, 1, (2500, 300))
if __name__ == '__main__':
num_of_workers = cpu_count()
print('number of CPUs : ', num_of_workers)
pool = Pool(num_of_workers)
cols_red_list = pool.map(partial(crossings,diff = diff), rows_list)
pool.close()
print(len(cols_red_list))
# some subsequent code which uses the cols_red_list data
sim1 = Simulation(n_int=10)
sim1.loop()
不幸的是,与顺序代码相比,并行化要慢得多。因此我的问题是:在那个特定的例子中,我是否正确使用了 multiprocessing 包?是否有其他方法可以并行化上述 for 循环?
解决方案
免责声明:当您试图通过并行化减少代码的运行时间时,这并不能严格回答您的问题,但它可能仍然是一个很好的学习机会。
作为黄金法则,在转向多处理以提高性能(执行时间)之前,应该首先优化单线程情况。
您的
rows_list = list(range(2500))
将数字生成0
到2499
(即range
)并将它们存储在内存中(list
),这需要时间来分配所需的内存和实际写入。然后,您只需按可预测的顺序从内存中读取它们(这也需要时间),每次只使用一次这些可预测的值:
for row in rows_list:
loop
当您重复执行此操作时,这与您的函数的运行时特别相关( for itr in range(n_int):
)。
相反,请考虑仅在需要时生成数字,而不需要中间存储(这在概念上消除了访问 RAM 的任何需要):
for row in range(2500):
其次,除了共享相同的问题(对内存的不必要访问)之外,还有以下内容:
diff = np.random.uniform(-1, 1, (2500, 300))
# ...
col = next(idx for idx, val in enumerate(diff[row,:]) if val < 0)
在我看来,在数学(或逻辑)层面上是可以优化的。
您要做的是通过将随机变量(该col
索引)定义为“我第一次在 [-1;1] 中遇到小于 0 的随机变量”来获取随机变量(该索引)。但请注意,确定在 [-α;α] 上具有均匀分布的随机变量是否为负,与在 {0,1} 上具有随机变量(即a bool
)相同。
因此,您现在使用bool
的是 s 而不是float
s,而且您甚至不必进行比较 ( val < 0
),因为您已经有了一个布尔值。这可能会使代码更快。使用与 for 相同的想法rows_list
,您可以bool
仅在需要时生成它;测试它直到它是True
(或者False
,选择一个,这显然无关紧要)。通过这样做,您只生成bool
所需数量的随机 s,而不是更多也不是更少(顺便说一句,如果行中的所有 300 个元素都是负数,您的代码中会发生什么?;)):
for _ in range(n_int):
cols_red_list = []
for row in range(2500):
col = next(i for i in itertools.count() if random.getrandbits(1))
cols_red_list.append(col)
或者,使用列表理解:
cols_red_list = [next(i for i in count() if getrandbits(1))
for _ in range(2500)]
我敢肯定,通过适当的统计分析,您甚至可以将该col
随机变量表示为 [0; limit
[,让你计算得更快。
请首先测试单线程实现的“优化”版本的性能。如果运行时仍然不可接受,那么您应该研究多线程。
推荐阅读
- c++ - OpenGL - 渲染器设计
- wicket - 是否可以/如何在反馈面板上多次显示消息
- hadoop - Hive:删除数据库
- ios - INVoiceShortcutCenter.shared.setShortcutSuggestions(suggestions) 在 iOS 13 Beta 中崩溃。Xcode 11 测试版
- sql - 将分钟转换为时钟时间
- reactjs - 在反应中,您如何释放持久事件?
- swift - 如何从 Swift 中的字符串中删除单引号?
- docker - 如何使用 docker 安装 guacenc?
- android - 用于横向对象检测的 TensorFlow Lite Android
- electron - 通过 ssh 隧道 (ngrok) 连接到 SQL 服务器