【问题标题】:Threading vs Multiprocessing线程与多处理
【发布时间】:2019-07-14 05:22:45
【问题描述】:

假设我有一个包含 100000 行的表和一个 python 脚本,它按顺序对该表的每一行执行一些操作。现在为了加快这个过程,我应该创建 10 个单独的脚本并同时运行它们来处理表的后续 10000 行,还是应该创建 10 个线程来处理行以提高执行速度?

【问题讨论】:

标签: python multithreading multiprocessing processing-efficiency


【解决方案1】:

线程

  • 由于全局解释器锁,python 线程并不是真正的并行。换句话说,一次只能运行一个线程。
  • 如果您正在执行 CPU 密集型任务,那么在线程之间划分工作负载不会加快您的计算速度。如果有的话,它会减慢它们的速度,因为解释器需要在更多线程之间切换。
  • 线程对于 IO 绑定任务更有用。例如,如果您同时与多个不同的客户端/服务器进行通信。在这种情况下,您可以在等待不同的客户端/服务器响应时在线程之间切换

多处理

  • 正如 Eman Hamed 所指出的,在多处理时共享对象可能很困难。

矢量化

  • 像 pandas 这样的库允许您在表上使用矢量化方法。这些是用 C 语言编写的高度优化的操作,在整个表或列上执行速度非常快。根据您的表结构和您要执行的操作,您应该考虑利用这一点

【讨论】:

  • FWIW:Python 线程确实运行 concurrently。也就是说,多个线程可以同时“进行中”。由于 GIL,他们无法实现真正​​的parallelism。即,两个或多个不同的 CPU同时代表两个或多个不同的线程执行指令。
  • @SolomonSlow 我不确定我是否理解您所说的可以同时进行中多个线程的意思? GIL 是线程运行必须获取的锁。由于任何时候只有一个线程可以获取锁 - 我的理解是任何时候只有一个线程可以运行....
  • “进行中”是指线程已经启动,但还没有结束。它可能正在运行,也可能没有运行,但无论哪种方式,它都有一个 context,它(至少)由一堆未完成的函数调用(包括它们的所有参数和局部变量)和一个指示线程接下来将执行什么语句的指令指针。 GIL 只允许一个 Python 线程在任何给定时刻运行,但可以启动任意数量的线程但尚未完成,这就是计算机科学家通常定义的“并发”的含义。
  • 这就像你可以告诉你的朋友或邻居“我正在改造我的厨房”,而事实上,就在那一刻,你实际上正在健身房锻炼。即使您实际上在在做其他事情,改造仍可能“正在进行中”。
  • @SolomonSlow 感谢您的解释。我还没有意识到并发和并行之间的区别。很高兴知道。我已经更正了我的答案
【解决方案2】:

进程线程有一个共同的(虚拟)内存块,称为堆进程没有。相对于整个进程(单独的脚本),线程消耗的操作系统资源也更少,并且不会发生上下文切换。

多线程执行的最大性能因素是没有 涉及的锁定/障碍是数据访问位置,例如。矩阵乘法内核。

假设数据以线性方式存储在堆中,即。 [0-4095] 字节中的第 0 行,[4096-8191] 字节中的第 1 行等。然后线程 0 应该在 0,10,20 中运行,...行,线程 1 在 1,11 中运行,21,... 行等。

主要思想是将一组 4K 页面保存在物理 RAM 中,将 64 字节块保存在 L3 缓存中并重复操作它们。计算机通常假设如果你“使用”一个特定的内存位置,那么你也会使用相邻的内存位置,你应该在你的程序中尽力做到这一点。最坏的情况是以随机方式访问相距约 10MiB 的内存位置,所以不要这样做。例如。如果单行是 1310720 doubles(64B) in 大小,那么您的线程应该以行内(单行)而不是行间(上)方式运行。

对您的代码进行基准测试并根据您的结果,如果您的算法可以处理大约 21.3GiB/s(DDR3-2666Mhz) 的行,那么您就有了一项内存受限的任务。如果您的代码具有 1GiB/s 的处理速度,那么您有一个计算密集型任务,这意味着执行数据指令比从 RAM 获取数据需要更多时间,您需要优化代码或通过使用 AVXx 指令集或达到更高的 IPC购买具有更多内核或更高频率的更新处理器。

【讨论】:

    猜你喜欢
    • 2011-09-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-18
    • 2019-09-04
    • 2022-08-16
    • 2021-06-26
    相关资源
    最近更新 更多