Ipython 笔记本上的多核和多线程

Multicore and multithread on Ipython Notebook

我目前正在 python 中使用 threading 函数并得到以下信息:

In [1]:
import threading
threading.activeCount()

Out[1]:
4

现在在我的终端上,我使用 lscpu 并了解到每个核心有 2 个线程,我可以访问 4 个核心:

kitty@FelineFortress:~$ lscpu
Architecture:          x86_64
CPU op-mode(s):        32-bit, 64-bit
Byte Order:            Little Endian
CPU(s):                8
On-line CPU(s) list:   0-7
Thread(s) per core:    2
Core(s) per socket:    4
Socket(s):             1
NUMA node(s):          1
Vendor ID:             GenuineIntel
CPU family:            6
Model:                 60
Stepping:              3
CPU MHz:               800.000
BogoMIPS:              5786.45
Virtualization:        VT-x
L1d cache:             32K
L1i cache:             32K
L2 cache:              256K
L3 cache:              8192K
NUMA node0 CPU(s):     0-7

因此,我应该有超过 4 个线程可以访问。是否有 python 函数可以用来增加我正在使用的内核数量(例如)以获得超过 4 个线程?或者甚至在启动 ipython notebook 时在终端上键入如下内容:

ipython notebook n_cores=3

您可以使用 multiprocessing 来允许 Python 使用多核。一个重要的警告:您在 Python 会话之间传递的所有数据都必须是可挑选的或通过继承传递,并且在 Windows 上生成一个新的 Python 实例,而在 Unix 系统上它可以分叉。这对 Windows 系统有显着的性能影响。

使用多处理的基本 example 如下来自 "Python Module of the Week":

import multiprocessing

def worker():
    """worker function"""
    print 'Worker'
    return

if __name__ == '__main__':
    jobs = []
    for i in range(5):
        p = multiprocessing.Process(target=worker)
        jobs.append(p)
        p.start()

执行时输出:

Worker
Worker
Worker
Worker
Worker

多处理允许您在不同的内核上进行独立计算,允许 CPU 绑定的任务以很少的开销执行,比传统进程执行得更快。

您还应该意识到 Python 中的线程不会提高性能。它的存在是为了方便(例如在长时间计算期间保持 GUI 的响应能力)。原因是由于 Python 的全局解释器锁,或 GIL.

,这些不是本机线程

2018 年 2 月更新

这个还是很适用的,而且在可预见的将来。 Cpython 实现使用以下 definition 进行引用计数:

typedef struct _object {
    _PyObject_HEAD_EXTRA
    Py_ssize_t ob_refcnt;
    struct _typeobject *ob_type;
} PyObject;

值得注意的是,这 不是 线程安全的,因此必须实现全局解释器锁以仅允许一个线程执行 Python 对象以避免数据导致内存问题的比赛。

除了多处理(这需要 Windows 上的解释器的完整副本,而不是 fork,这使得它非常慢并且无法提高性能)。

赛通

最简单的解决方案是 Cython。简单地 cdef 一个函数,没有任何内部对象,并使用 with nogil 关键字释放 GIL。

取自 documentation 的一个简单示例,向您展示了如何释放并临时重新启用 GIL:

from cython.parallel import prange

cdef int func(Py_ssize_t n):
    cdef Py_ssize_t i

    for i in prange(n, nogil=True):
        if i == 8:
            with gil:
                raise Exception()
        elif i == 4:
            break
        elif i == 2:
            return i

使用不同的解释器

CPython 有 GI,而 Jython 和 IronPython 没有。要小心,因为许多用于高性能计算的 C 库可能无法与 IronPython 或 Jython 一起工作(SciPy 曾尝试过 IronPython 支持,但很久以前就放弃了,它将不适用于现代 Python 版本)。

使用 MPI4Py

MPI,即消息传递接口,是一种用于 C 和 C++ 等语言的高性能接口。它允许高效的并行计算,MPI4Py 为 Python 创建 MPI 绑定。为了提高效率,您应该只将 MPI4Py 与 NumPy 数组一起使用。

他们 documentation 的一个例子是:

from mpi4py import MPI
import numpy

def matvec(comm, A, x):
    m = A.shape[0] # local rows
    p = comm.Get_size()
    xg = numpy.zeros(m*p, dtype='d')
    comm.Allgather([x,  MPI.DOUBLE],
                   [xg, MPI.DOUBLE])
    y = numpy.dot(A, xg)
    return y