在 Python 中的线程之间共享字典时是否可以避免锁定开销?
Is it possible to avoid locking overhead when sharing dicts between threads in Python?
我在 Python 中有一个多线程应用程序,其中线程读取非常大(因此我无法将它们复制到线程本地存储)字典(从磁盘读取并且从未修改)。然后他们使用字典作为只读数据处理大量数据:
# single threaded
d1,d2,d3 = read_dictionaries()
while line in stdin:
stdout.write(compute(line,d1,d2,d3)+line)
我试图通过使用线程来加快速度,然后每个线程都会读取自己的输入并写入自己的输出,但是由于字典很大,我希望线程共享存储空间。
IIUC,每次线程从 dict 读取时,它都必须锁定它,这会给应用程序带来性能成本。不需要这种数据锁定,因为字典是只读的。
CPython 是单独锁定数据还是只使用 GIL?
如果确实存在 per-dict 锁定,有没有办法避免它?
python中的多线程处理没用。最好使用多处理模块。因为多线程只能在较少的情况下给予积极的努力。
Python implementation detail: In CPython, due to the Global
Interpreter Lock, only one thread can execute Python code at once
(even though certain performance-oriented libraries might overcome
this limitation). If you want your application to make better use of
the computational resources of multi-core machines, you are advised to
use multiprocessing. However, threading is still an appropriate model
if you want to run multiple I/O-bound tasks simultaneously.
Official documentation.
没有您身边的任何代码示例,我只能建议将您的大词典分成几个部分,并使用 Pool.map 处理每个部分。并在主进程中合并结果。
不幸的是,不可能在不同的 python 进程之间有效地共享大量内存(我们不是在讨论基于 mmap 的共享内存模式)。但是您可以在不同的过程中阅读字典的不同部分。或者只是在主进程中读取整个字典并给子进程一小部分。
另外,我应该警告你,你应该非常小心地使用多处理算法。因为每增加一兆字节,进程数就会成倍增加。
因此,根据您的伪代码示例,我可以假设两种基于 compute
函数的可能算法:
# "Stateless"
for line in stdin:
res = compute_1(line) + compute_2(line) + compute_3(line)
print res, line
# "Shared" state
for line in stdin:
res = compute_1(line)
res = compute_2(line, res)
res = compute_3(line, res)
print res, line
在第一种情况下,您可以创建多个 worker,根据 Process class 在单独的 worker 中读取每个字典(减少每个进程的内存使用是个好主意),然后像生产线一样计算它。
在第二种情况下,您有一个共享状态。对于下一个工人,您需要上一个工人的结果。这是 multithreading/multiprocessing 编程的最坏情况。但是您可以在那里编写算法,几个工作人员正在使用相同的队列并将结果推送到它而无需等待所有周期完成。您只需在进程之间共享一个 Queue 实例。
我在 Python 中有一个多线程应用程序,其中线程读取非常大(因此我无法将它们复制到线程本地存储)字典(从磁盘读取并且从未修改)。然后他们使用字典作为只读数据处理大量数据:
# single threaded
d1,d2,d3 = read_dictionaries()
while line in stdin:
stdout.write(compute(line,d1,d2,d3)+line)
我试图通过使用线程来加快速度,然后每个线程都会读取自己的输入并写入自己的输出,但是由于字典很大,我希望线程共享存储空间。
IIUC,每次线程从 dict 读取时,它都必须锁定它,这会给应用程序带来性能成本。不需要这种数据锁定,因为字典是只读的。
CPython 是单独锁定数据还是只使用 GIL?
如果确实存在 per-dict 锁定,有没有办法避免它?
python中的多线程处理没用。最好使用多处理模块。因为多线程只能在较少的情况下给予积极的努力。
Python implementation detail: In CPython, due to the Global Interpreter Lock, only one thread can execute Python code at once (even though certain performance-oriented libraries might overcome this limitation). If you want your application to make better use of the computational resources of multi-core machines, you are advised to use multiprocessing. However, threading is still an appropriate model if you want to run multiple I/O-bound tasks simultaneously. Official documentation.
没有您身边的任何代码示例,我只能建议将您的大词典分成几个部分,并使用 Pool.map 处理每个部分。并在主进程中合并结果。
不幸的是,不可能在不同的 python 进程之间有效地共享大量内存(我们不是在讨论基于 mmap 的共享内存模式)。但是您可以在不同的过程中阅读字典的不同部分。或者只是在主进程中读取整个字典并给子进程一小部分。
另外,我应该警告你,你应该非常小心地使用多处理算法。因为每增加一兆字节,进程数就会成倍增加。
因此,根据您的伪代码示例,我可以假设两种基于 compute
函数的可能算法:
# "Stateless"
for line in stdin:
res = compute_1(line) + compute_2(line) + compute_3(line)
print res, line
# "Shared" state
for line in stdin:
res = compute_1(line)
res = compute_2(line, res)
res = compute_3(line, res)
print res, line
在第一种情况下,您可以创建多个 worker,根据 Process class 在单独的 worker 中读取每个字典(减少每个进程的内存使用是个好主意),然后像生产线一样计算它。
在第二种情况下,您有一个共享状态。对于下一个工人,您需要上一个工人的结果。这是 multithreading/multiprocessing 编程的最坏情况。但是您可以在那里编写算法,几个工作人员正在使用相同的队列并将结果推送到它而无需等待所有周期完成。您只需在进程之间共享一个 Queue 实例。