当前位置:首页 > 科技  > 软件

线程使用越多程序越快?别瞎整

来源: 责编: 时间:2024-01-10 17:24:24 361观看
导读当运行 CPU 密集型的并行程序时,通常希望将线程或进程池的大小设置为计算机上的 CPU 核数量,但有没有考虑过是否真的是核数用的越多并行程序越快?理论上线程过少,无法充分利用所有核心,线程过多,程序会因为多个线程争夺同一

当运行 CPU 密集型的并行程序时,通常希望将线程或进程池的大小设置为计算机上的 CPU 核数量,但有没有考虑过是否真的是核数用的越多并行程序越快?jmy28资讯网——每日最新资讯28at.com

理论上线程过少,无法充分利用所有核心,线程过多,程序会因为多个线程争夺同一核心而变得运行缓慢。jmy28资讯网——每日最新资讯28at.com

事实上,确定要运行多少个线程没那么容易:jmy28资讯网——每日最新资讯28at.com

  • Python 标准库提供了多个获取此信息的 API,但没有一个是恰当的(稍后会举例)
  • 由于 CPU 具有指令级并行性和同时多线程等功能(在英特尔 CPU 上称为超线程),可以有效使用的核心数量取决于编写的代码

jmy28资讯网——每日最新资讯28at.com

从 Python 获取 CPU 内核数

前述提到在Python中获取内核数的API是不准确的,为啥这么说,我们看个例子jmy28资讯网——每日最新资讯28at.com

Python提供 os.cpu_count() 函数,可以返回 "系统中逻辑 CPU 的数量",文档说明 "len(os.sched_getaffinity(0))可以获取当前进程调用线程受限的逻辑 CPU 数量",调度器亲和性是一种限制进程使用特定内核的方法。jmy28资讯网——每日最新资讯28at.com

遗憾的是,这个 API 也不够恰当,例如使用Docker在创建容器时人为限制CPU数量,比如将 CPU 限制为2.25 个内核:jmy28资讯网——每日最新资讯28at.com

$ docker run -i -t --cpus=2.25 python:3.12-slimPython 3.12.1 (main, Dec  9 2023, 00:21:37) [GCC 12.2.0] on linuxType "help", "copyright", "credits" or "license" for more information.>>> import os>>> os.cpu_count()20>>> len(os.sched_getaffinity(0))20

在Docker中只提供了2.25个内核资源,但显然调用Python API时返回的数量仍不对。jmy28资讯网——每日最新资讯28at.com

说完这个问题,还需要先了解物理和逻辑 CPU 内核是什么再进入正题。jmy28资讯网——每日最新资讯28at.com

物理与逻辑 CPU内核

以英特尔 i7-12700K 处理器为例,它具有:jmy28资讯网——每日最新资讯28at.com

  • 12 个物理内核(8 个高性能内核和 4 个性能较弱的内核)
  • 20 个逻辑内核

现代 CPU 内核可以并行执行多条指令,但如果 CPU 在等待从 RAM 中加载某些数据时卡住了,会发生什么情况?在此之前,它可能无法执行任何工作。jmy28资讯网——每日最新资讯28at.com

为了充分利用这些可能被浪费的资源,CPU 物理内核的计算资源可以作为多个内核向操作系统公开。在这台电脑上,8 个高性能内核中的每一个都可以作为两个内核公开,总共有 16 个逻辑内核。成对的逻辑内核将共享单个物理内核的计算资源,例如,如果一个逻辑内核没有充分利用所有内部算术逻辑单元,比如因为它在等待内存加载,那么通过配对逻辑内核运行的代码仍然可以使用这些闲置资源。jmy28资讯网——每日最新资讯28at.com

这种技术被称为同步多线程技术,英特尔称之为超线程技术。如果你有一台电脑,通常可以在 BIOS 中禁用它。jmy28资讯网——每日最新资讯28at.com

这种解释非常不准确,而且不同型号的 CPU,即使是同一制造商生产的 CPU,实际执行情况也不尽相同。不过,逻辑内核与物理内核并不完全相同的一般意义足以满足这篇文章要表达的目的。jmy28资讯网——每日最新资讯28at.com

jmy28资讯网——每日最新资讯28at.com

现在又有了一个新问题,抛开调度器亲和性等因素不谈,我们应该使用物理内核数还是逻辑内核数作为线程池大小?jmy28资讯网——每日最新资讯28at.com

示例

在该例中,用 Numba 将两个函数编译成机器代码,确保释放 GIL 以实现并行。jmy28资讯网——每日最新资讯28at.com

这两个函数做的事情一毛一样,但slow_threshold特意写成比较慢的方式而fast_threshold则更快(感兴趣的可以对比学习下为何另一个更快,很简单)。现在可以在多个线程上并行运行这些函数,在大多数人眼里,只需并行处理更多图像,就能线性提高吞吐量,直到内核耗尽,先从单核上进行测试:jmy28资讯网——每日最新资讯28at.com

from numba import njitimport numpy as np@njit(nogil=True)def slow_threshold(img, noise_threshold):    noise_threshold = img.dtype.type(noise_threshold)    result = np.empty(img.shape, dtype=np.uint8)    for i in range(result.shape[0]):        for j in range(result.shape[1]):            result[i, j] = img[i, j] // 256    for i in range(result.shape[0]):        for j in range(result.shape[1]):            if result[i, j] < noise_threshold // 256:                result[i, j] = 0    return result@njit(nogil=True)def fast_threshold(img, noise_threshold):    noise_threshold = np.uint8(noise_threshold // 256)    result = np.empty(img.shape, dtype=np.uint8)    for i in range(result.shape[0]):        for j in range(result.shape[1]):            value = img[i, j] >> 8            value = (                0 if value < noise_threshold else value            )            result[i, j] = value    return resultrng = np.random.default_rng(12345)def make_image(size=256):    noise = rng.integers(0, high=1000, size=(size, size), dtype=np.uint16)    signal = rng.integers(0, high=5000, size=(size, size), dtype=np.uint16)    # A noisy, hard to predict image:    return noise | signalNOISY_IMAGE = make_image()assert np.array_equal(    slow_threshold(NOISY_IMAGE, 1000),    fast_threshold(NOISY_IMAGE, 1000))

借助timeit测试单核上运行每个功能的性能,结果如下:jmy28资讯网——每日最新资讯28at.com

%timeit slow_threshold(NOISY_IMAGE, 1000)# 90.6 µs ± 77.7 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)
%timeit fast_threshold(NOISY_IMAGE, 1000)# 24.6 µs ± 10.8 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)

结果如前所述,确实fast_threshold表现更好。jmy28资讯网——每日最新资讯28at.com

并行化示例

现在我们使用线程池处理上述函数:jmy28资讯网——每日最新资讯28at.com

from multiprocessing.dummy import Pool as ThreadPooldef apply_in_thread_pool(    num_threads, function, images):    with ThreadPool(num_threads) as pool:        result = pool.map(            lambda img: function(img, 1000),            images,            chunksize=10        )        assert len(result) == len(images)

借助benchit绘制不同线程数运行不同函数所需的时间图:jmy28资讯网——每日最新资讯28at.com

import benchitbenchit.setparams(rep=1)# 4000 images to run through the pool:IMAGES = [make_image() for _ in range(4000)]def slow_threshold_in_pool(num_threads):    apply_in_thread_pool(num_threads, slow_threshold, IMAGES)def fast_threshold_in_pool(num_threads):    apply_in_thread_pool(num_threads, fast_threshold, IMAGES)# Measure the two functions with 1 to 24 threads:timings = benchit.timings(    [slow_threshold_in_pool, fast_threshold_in_pool],    range(1, 25),    input_name="Number of threads")timings.plot(logy=True, logx=False)

绘制的图片如下:jmy28资讯网——每日最新资讯28at.com

jmy28资讯网——每日最新资讯28at.com

可以注意到随着线程数变多,运行时间先是有明显下降,但到一定程度后无明显改进,且另一个发现是每个函数的最佳线程数不同:jmy28资讯网——每日最新资讯28at.com

timings.to_dataframe().idxmin(axis="rows")

Functions
jmy28资讯网——每日最新资讯28at.com

Optimal number of threads
jmy28资讯网——每日最新资讯28at.com

slow_threshold
jmy28资讯网——每日最新资讯28at.com

19
jmy28资讯网——每日最新资讯28at.com

fast_threshold
jmy28资讯网——每日最新资讯28at.com

9
jmy28资讯网——每日最新资讯28at.com

slow_threshold函数基本上可以利用所有逻辑内核,单线程可能无法充分利用特定物理内核的所有可用处理能力,因此逻辑内核允许更多并行性。jmy28资讯网——每日最新资讯28at.com

相比之下,fast_threshold函数使用超过 9 个内核后,速度就开始减慢。可能遇到计算以外的瓶颈,比如内存带宽。jmy28资讯网——每日最新资讯28at.com

总结

  • 考虑到操作系统限制 CPU 使用的所有不同方式,很难获得准确的内核数量
  • 最佳并行程度(如线程数)取决于工作量
  • 内核数量并不是唯一的瓶颈

如果有一个长期运行的数据处理任务,需要在多个线程中运行相同的代码一段时间,通常也值得这样做,花一点时间根据经验测算出最佳线程数。jmy28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-60378-0.html线程使用越多程序越快?别瞎整

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 微服务的颗粒度难题:找到合适的微服务大小

下一篇: Linux下利用Docker快速部署配置Kafka服务

标签:
  • 热门焦点
  • 对标苹果的灵动岛 华为带来实况窗功能

    继苹果的灵动岛之后,华为也在今天正式推出了“实况窗”功能。据今天鸿蒙OS 4.0的现场演示显示,华为的实况窗可以更高效的展现出实时通知,比如锁屏上就能看到外卖、打车、银行
  • 2023年Q2用户偏好榜:12+256G版本成新主流

    3月份的性能榜、性价比榜和好评榜之后,就要轮到2023年的第二季度偏好榜了,上半年的新机潮已经过去,最明显的肯定就是大内存和存储的机型了,另外部分中端机也取消了屏幕塑料支架
  • 帅气纯真少年!日本最帅初中生选美冠军出炉

    日本第一帅哥初一生选美大赛冠军现已正式出炉,冠军是来自千叶县的宗田悠良。日本一直热衷于各种选美大赛,从&ldquo;最美JK&rdquo;起到&ldquo;最美女星&r
  • 多线程开发带来的问题与解决方法

    使用多线程主要会带来以下几个问题:(一)线程安全问题  线程安全问题指的是在某一线程从开始访问到结束访问某一数据期间,该数据被其他的线程所修改,那么对于当前线程而言,该线程
  • 共享单车的故事讲到哪了?

    来源丨海克财经与共享充电宝相差不多,共享单车已很久没有被国内热点新闻关照到了。除了一再涨价和用户直呼用不起了。近日多家媒体再发报道称,成都、天津、郑州等地多个共享单
  • 腾讯盖楼,字节拆墙

    来源 | 光子星球撰文 | 吴坤谚编辑 | 吴先之&ldquo;想重温暴刷深渊、30+技能搭配暴搓到爽的游戏体验吗?一起上晶核,即刻暴打!&rdquo;曾凭借直播腾讯旗下代理格斗游戏《DNF》一
  • 华为和江淮汽车合作开发百万元问界MPV?双方回应来了

    8月1日消息,郭明錤今天在社交平台发文称,华为正在和江淮汽车合作,开发售价在100万元的问界MPV,预计在2024年第2季度量产,销量目标为上市首年交付5万辆。
  • 超级标准版旗舰!iQOO 11S全球首发iQOO超算独显芯片

    上半年已接近尾声,截至目前各大品牌旗下的顶级旗舰都已悉数亮相,而下半年即将推出的顶级旗舰已经成为了数码圈爆料的主流,其中就包括全新的iQOO 11S系
  • 滴滴违法违规被罚80.26亿 共存在16项违法事实

    滴滴违法违规被罚80.26亿 存在16项违法事实开始于2121年7月,历经一年时间,网络安全审查办公室对“滴滴出行”网络安全审查终于有了一个暂时的结束。据“网信
Top