首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >使用numba并行化加速代码的字典问题

使用numba并行化加速代码的字典问题
EN

Stack Overflow用户
提问于 2022-04-09 23:35:03
回答 1查看 5.5K关注 0票数 2

我已经写了一个代码,并尝试使用numba加速代码。代码的主要目标是根据条件对某些值进行分组。在这方面,iter_用于收敛代码以满足条件。我准备了下面的一个小例子来重现示例代码:

代码语言:javascript
运行
AI代码解释
复制
import numpy as np
import numba as nb

rng = np.random.default_rng(85)

# --------------------------------------- small data volume ---------------------------------------
# values_ = {'R0': np.array([0.01090976, 0.01069902, 0.00724112, 0.0068463 , 0.01135723, 0.00990762,
#                                        0.01090976, 0.01069902, 0.00724112, 0.0068463 , 0.01135723]),
#            'R1': np.array([0.01836379, 0.01900166, 0.01864162, 0.0182823 , 0.01840322, 0.01653088,
#                                        0.01900166, 0.01864162, 0.0182823 , 0.01840322, 0.01653088]),
#            'R2': np.array([0.02430913, 0.02239156, 0.02225379, 0.02093393, 0.02408692, 0.02110411,
#                                        0.02239156, 0.02225379, 0.02093393, 0.02408692, 0.02110411])}
#
# params = {'R0': [3, 0.9490579204466154, 1825, 7.070272000000002e-05],
#           'R1': [0, 0.9729203826820172, 167 , 7.070272000000002e-05],
#           'R2': [1, 0.6031363088057902, 1316, 8.007296000000003e-05]}
#
# Sno, dec_, upd_ = 2, 100, 200
# -------------------------------------------------------------------------------------------------

# ----------------------------- UPDATED (medium and large data volumes) ---------------------------
# values_ = np.load("values_med.npy", allow_pickle=True)[()]
# params = np.load("params_med.npy", allow_pickle=True)[()]
values_ = np.load("values_large.npy", allow_pickle=True)[()]
params = np.load("params_large.npy", allow_pickle=True)[()]

Sno, dec_, upd_ = 2000, 1000, 200
# -------------------------------------------------------------------------------------------------

# values_ = [*values_.values()]
# params = [*params.values()]


# @nb.jit(forceobj=True)
# def test(values_, params, Sno, dec_, upd_):

final_dict = {}
for i, j in enumerate(values_.keys()):
    Rand_vals = []
    goal_sum = params[j][1] * params[j][3]
    tel = goal_sum / dec_ * 10
    if params[j][0] != 0:
        for k in range(Sno):
            final_sum = 0.0
            iter_ = 0
            t = 1
            while not np.allclose(goal_sum, final_sum, atol=tel):
                iter_ += 1
                vals_group = rng.choice(values_[j], size=params[j][0], replace=False)
                # final_sum = 0.0016 * np.sum(vals_group)  # -----> For small data volume
                final_sum = np.sum(vals_group ** 3)        # -----> UPDATED For med or large data volume
                if iter_ == upd_:
                    t += 1
                    tel = t * tel
            values_[j] = np.delete(values_[j], np.where(np.in1d(values_[j], vals_group)))
            Rand_vals.append(vals_group)
    else:
        Rand_vals = [np.array([])] * Sno
    final_dict["R" + str(i)] = Rand_vals

#    return final_dict


# test(values_, params, Sno, dec_, upd_)

首先,在此代码上应用numba,使用了@nb.jit (forceobj=True用于避免警告和…)。,这将对演出产生不利影响。nopython也会使用@nb.njit进行检查,因为输入的不支持 (如12中提到的)字典类型导致了以下错误:

无法确定的Numba类型

我不知道Dict是否可以(如何)从numba.typed (通过将创建的python字典转换为numba )处理它,或者将字典转换为数组列表是否有什么好处。我认为,并行化可能是可能的,如果一些代码行,如Rand_vals.append(vals_group)或其他部分或…从函数中删除或修改以获得与以前相同的结果,但我不知道如何这样做。

我将非常感谢在这段代码中帮助使用numba。如果可以的话,numba parallelization 将是最理想的解决方案(可能是性能方面最适用的方法)。

数据:

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-04-13 17:05:12

此代码可以转换为Numba,但并不简单。

首先,必须定义字典和列表类型,因为Numba njit 函数不能直接在反射列表(又名)上操作。纯python列表)。在Numba中这样做有点乏味,其结果代码有点冗长:

代码语言:javascript
运行
AI代码解释
复制
String = nb.types.unicode_type
ValueArray = nb.float64[::1]
ValueDict = nb.types.DictType(String, ValueArray)
ParamDictValue = nb.types.Tuple([nb.int_, nb.float64, nb.int_, nb.float64])
ParamDict = nb.types.DictType(String, ParamDictValue)
FinalDictValue = nb.types.ListType(ValueArray)
FinalDict = nb.types.DictType(String, FinalDictValue)

然后,您需要转换输入字典:

代码语言:javascript
运行
AI代码解释
复制
nbValues = nb.typed.typeddict.Dict.empty(String, ValueArray)
for key,value in values_.items():
    nbValues[key] = value.copy()

nbParams = nb.typed.typeddict.Dict.empty(String, ParamDictValue)
for key,value in params.items():
    nbParams[key] = (nb.int_(value[0]), nb.float64(value[1]), nb.int_(value[2]), nb.float64(value[3]))

然后,您需要编写核心函数。np.allclosenp.isin没有在Numba中实现,因此应该手动重新实现。但要点是Numba不支持rng Numpy对象。我认为政府肯定不会在短期内支持这项建议。注意,Numba有一个随机数实现,试图模仿Numpy的行为,但是种子的管理有点不同。还请注意,如果种子设置为相同的值(Numpy和Numba有不同的种子变量而不是同步的),那么np.random.xxx Numpy函数的结果应该是相同的。

代码语言:javascript
运行
AI代码解释
复制
@nb.njit(FinalDict(ValueDict, ParamDict, nb.int_, nb.int_, nb.int_))
def nbTest(values_, params, Sno, dec_, upd_):
    final_dict = nb.typed.Dict.empty(String, FinalDictValue)
    for i, j in enumerate(values_.keys()):
        Rand_vals = nb.typed.List.empty_list(ValueArray)
        goal_sum = params[j][1] * params[j][3]
        tel = goal_sum / dec_ * 10
        if params[j][0] != 0:
            for k in range(Sno):
                final_sum = 0.0
                iter_ = 0
                t = 1

                vals_group = np.empty(0, dtype=nb.float64)

                while np.abs(goal_sum - final_sum) > (1e-05 * np.abs(final_sum) + tel):
                    iter_ += 1
                    vals_group = np.random.choice(values_[j], size=params[j][0], replace=False)
                    final_sum = 0.0016 * np.sum(vals_group)
                    # final_sum = 0.0016 * np.sum(vals_group)  # (for small data volume)
                    final_sum = np.sum(vals_group ** 3)        # (for med or large data volume)
                    if iter_ == upd_:
                        t += 1
                        tel = t * tel

                # Perform an in-place deletion
                vals, gr = values_[j], vals_group
                cur = 0
                for l in range(vals.size):
                    found = False
                    for m in range(gr.size):
                        found |= vals[l] == gr[m]
                    if not found:
                        # Keep the value (delete it otherwise)
                        vals[cur] = vals[l]
                        cur += 1
                values_[j] = vals[:cur]

                Rand_vals.append(vals_group)
        else:
            for k in range(Sno):
                Rand_vals.append(np.empty(0, dtype=nb.float64))
        final_dict["R" + str(i)] = Rand_vals
    return final_dict

请注意,np.isin的替换实现非常幼稚,但在您的输入示例中,它在实践中运行得很好。

可以使用以下方式调用该函数:

代码语言:javascript
运行
AI代码解释
复制
nbFinalDict = nbTest(nbValues, nbParams, Sno, dec_, upd_)

最后,应该将字典转换回基本Python对象:

代码语言:javascript
运行
AI代码解释
复制
finalDict = dict()
for key,value in nbFinalDict.items():
    finalDict[key] = list(value)

这个实现对于小的输入是快速的,但不是大的,因为np.random.choice 几乎所有的时间都是 (>96%)。问题是,当请求项的数量很小时,这个函数显然不是最优的(这就是您的情况)。实际上,它在输入数组的线性时间中运行,而不是在请求项数的线性时间中运行。

进一步优化

该算法可以完全重写,只提取12个随机项,并以更有效的方式从主醋栗数组中丢弃它们。其思想是将数组末尾的n项(小目标示例)与任意位置的其他项交换,然后检查和,重复此过程直到满足条件,最后在调整视图大小之前将视图提取到最后一个n项,从而丢弃最后一个项。所有这些都可以在O(n)时间完成,而不是O(m)时间,其中m是带有n << m的主电流数组的大小(例如。12对20_000)。它也可以在不需要任何昂贵分配的情况下进行计算。以下是生成的代码:

代码语言:javascript
运行
AI代码解释
复制
@nb.njit(nb.void(ValueArray, nb.int_, nb.int_))
def swap(arr, i, j):
    arr[i], arr[j] = arr[j], arr[i]

@nb.njit(FinalDict(ValueDict, ParamDict, nb.int_, nb.int_, nb.int_))
def nbTest(values_, params, Sno, dec_, upd_):
    final_dict = nb.typed.Dict.empty(String, FinalDictValue)
    for i, j in enumerate(values_.keys()):
        Rand_vals = nb.typed.List.empty_list(ValueArray)
        goal_sum = params[j][1] * params[j][3]
        tel = goal_sum / dec_ * 10
        values = values_[j]
        n = params[j][0]

        if n != 0:
            for k in range(Sno):
                final_sum = 0.0
                iter_ = 0
                t = 1

                m = values.size
                assert n <= m
                group = values[-n:]

                while np.abs(goal_sum - final_sum) > (1e-05 * np.abs(final_sum) + tel):
                    iter_ += 1

                    # Swap the group view with other random items
                    for pos in range(m - n, m):
                        swap(values, pos, np.random.randint(0, m))

                    # For small data volume:
                    # final_sum = 0.0016 * np.sum(group)

                    # For med/large data volume
                    final_sum = 0.0
                    for v in group:
                        final_sum += v ** 3

                    if iter_ == upd_:
                        t += 1
                        tel *= t

                assert iter_ > 0
                values = values[:m-n]
                Rand_vals.append(group)
        else:
            for k in range(Sno):
                Rand_vals.append(np.empty(0, dtype=nb.float64))
        final_dict["R" + str(i)] = Rand_vals
    return final_dict

除了速度更快以外,这种实现作为更简单的好处也是如此。结果看起来与以前的实现非常相似,尽管具有随机性,这使得对结果的检查变得困难(特别是因为这个函数没有使用相同的方法来选择随机样本)。请注意,与前一个实现相比,此实现没有删除valuesgroup中的项(这可能并不需要)。

基准测试

以下是我的机器上最后一个实现的结果(不包括编译和转换时间):

代码语言:javascript
运行
AI代码解释
复制
Provided small input (embedded in the question):
 - Initial code:   42.71 ms
 - Numba code:      0.11 ms

Medium input:
 - Initial code:   3481 ms
 - Numba code:       11 ms

Large input:
 - Initial code:   6728 ms
 - Numba code:       20 ms

请注意,转换时间与计算时间大致相同。

最后一个实现是316~388倍,比小输入上的初始代码快一倍。

备注

注意,由于dict和列出类型,编译时间需要几秒钟。

请注意,虽然可以并行化实现,但只有最包容的循环才能并行化。问题是,需要计算的项目很少,而且时间已经很短了(这不是多线程的最佳情况)。此外,创建许多临时数组(由rng.choice创建)肯定会导致并行循环不能很好地扩展。此外,Numba并行化倾向于显著增加已经很重要的编译时间。最后,由于每个Numba线程都有自己的随机数生成器种子,并且不能用prange (取决于在目标平台上选择的并行运行时)来预测线程计算的项,所以结果不那么确定。请注意,在Numpy中,默认情况下有一个全局种子被通常的随机函数使用(不推荐的方式),而RNG对象有自己的种子(新的首选方式)。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71814612

复制
相关文章
利用numba給Python代码加速 [1]
Numba @jit 装饰器有两种编译模式, Nopython 模式和Object 模式。nopython编译模式的行为本质上是编译修饰后的函数,使其完全运行而不需要Python解释器的参与。这是使用Numba jit装饰器的推荐和最佳实践方法,因为它可以获得最佳性能。@jit(nopython=True) 等效于@njit()。
用户6021899
2022/01/10
1.6K0
利用numba給Python代码加速 [1]
用Numba加速Python代码
说这句话的人也没有错。与许多其他编程语言相比,Python很慢。Benchmark game有一些比较不同编程语言在不同任务上的速度的可靠的基准。
AiTechYun
2019/07/04
2.2K0
利用numba給Python代码加速 [0]
Numba 利用LLVM将python函数编译成优化后的机器码。Numba编译的由python写的数学算法能够接近C或Fortran的运行速度。LLVM 不仅能编译numba代码,还擅长优化它。
用户6021899
2021/12/30
3500
利用numba給Python代码加速 [0]
利用numba給Python代码加速 [3]
vectorize()允许您编写一次只能处理一个元素的UFUNC,但guvectorize()装饰器将这一概念更进一步,允许您编写可以处理任意数量的输入数组元素的UFUNC,并获取和返回不同维度的数组。
用户6021899
2022/03/04
4630
利用numba給Python代码加速 [3]
利用numba給Python代码加速 [2]
Numba 的 @vectorize 装饰器可以将以标量为输入的的python函数编译为类似Numpy的 ufuncs。创建一个传统的NumPy ufunc并不是最简单的过程,它可能需要编写一些C代码。Numba让这很容易。使用@vectorize装饰器 ,Numba可以将纯Python函数编译成ufunc,该ufunc在NumPy数组上运行的速度与用C编写的传统ufunc一样快。
用户6021899
2022/01/10
9030
利用numba給Python代码加速 [2]
python的numba加速
        之前笔者写过一个pypy的加速方法,可以参阅笔者之前的文章:http://blog.csdn.net/qtlyx/article/details/78078636
钱塘小甲子
2019/01/28
1.2K0
使用numba加速python科学计算
python作为一门编程语言,有非常大的生态优势,但是其执行效率一直被人诟病。纯粹的python代码跑起来速度会非常的缓慢,因此很多对性能要求比较高的python库,需要用C++或者Fortran来构造底层算法模块,再用python进行上层封装的方案。在前面写过的这篇博客中,介绍了使用f2py将fortran代码编译成动态链接库的方案,这可以认为是一种“事前编译”的手段。但是本文将要介绍一种即时编译(Just In Time,简称JIT)的手段,也就是在临近执行函数前,才对其进行编译。以下截图来自于参考链接4,讲述了关于常见的一些编译场景的区别:
DechinPhy
2021/05/21
2K0
让python快到飞起-numba加速
python是一门高效动态编程语言,由于其采用简洁明了的语法以及灵活性深受大家欢迎。但是,这既是它最大的优势,也是最大的劣势。它的灵活性和无类型的高级语法可能会导致数据和计算密集型程序的性能不佳,因为运行本地编译代码要比运行动态解释代码快很多倍。
自学气象人
2022/11/02
8960
让python快到飞起-numba加速
Dask教程:使用dask.delayed并行化代码
在本节中,我们使用 Dask 和 dask.delayed 并行化简单的 for 循环样例代码。通常,这是将函数转换为与 Dask 一起使用所需的唯一函数。
郭好奇同学
2021/08/26
4.6K0
Dask教程:使用dask.delayed并行化代码
使用NumPy、Numba的简单使用(一)
Numpy是python的一个三方库,主要是用于计算的,数组的算数和逻辑运算。与线性代数有关的操作。
小菜的不能再菜
2019/09/18
9890
使用NumPy、Numba的简单使用(二)
  本来要写NLP第三课动态规划的,日了,写到一半发现自己也不会了,理论很简单,动态规划咋回事也知道,但是实现在源码上还是有点难度,现在简单给予题目描述,小伙伴也可以来思考一下,例题一,我们现在有1元硬币,2元硬币,5元硬币和10元硬币。我们要将M金额的钱换为硬币,保证硬币数目最少,我们的换法是什么,例题二,我们现在有M米的绳子,截成N段(N的长度一定为整数),将N段绳子的长度相乘,保证乘积结果为最大值,我们需要截取,过几天再回头来写这个吧。我们今天来继续说说numpy的用法,这次我们通过习题来看看numpy的用法。
小菜的不能再菜
2019/09/18
8340
用 Numba 加速 Python 代码,变得像 C++ 一样快
注意: 这篇文章的 Jupyter Notebook 代码在我的 Github 上:SpeedUpYourAlgorithms-Numba
昱良
2019/07/04
2.7K0
用 Numba 加速 Python 代码,变得像 C++ 一样快
超过Numpy的速度有多难?试试Numba的GPU加速
Numpy是在Python中非常常用的一个库,不仅具有良好的接口文档和生态,还具备了最顶级的性能,这个库很大程度上的弥补了Python本身性能上的缺陷。虽然我们也可以自己使用Cython或者是在Python中调用C++的动态链接库,但是我们自己实现的方法不一定有Numpy实现的快,这得益于Numpy对于SIMD等技术的深入实现,把CPU的性能发挥到了极致。因此我们只能考虑弯道超车,尝试下能否用自己实现的GPU的算法来打败Numpy的实现。
DechinPhy
2021/08/31
2.4K0
超过Numpy的速度有多难?试试Numba的GPU加速
从头开始进行CUDA编程:Numba并行编程的基本概念
PU(图形处理单元)最初是为计算机图形开发的,但是现在它们几乎在所有需要高计算吞吐量的领域无处不在。这一发展是由GPGPU(通用GPU)接口的开发实现的,它允许我们使用GPU进行通用计算编程。这些接口中最常见的是CUDA,其次是OpenCL和最近刚出现的HIP。
deephub
2023/01/18
1.4K0
【他山之石】Pytorch/Tensorflow-gpu训练并行加速trick(含代码)
“他山之石,可以攻玉”,站在巨人的肩膀才能看得更高,走得更远。在科研的道路上,更需借助东风才能更快前行。为此,我们特别搜集整理了一些实用的代码链接,数据集,软件,编程技巧等,开辟“他山之石”专栏,助你乘风破浪,一路奋勇向前,敬请关注。
马上科普尚尚
2021/01/28
1.5K0
【他山之石】Pytorch/Tensorflow-gpu训练并行加速trick(含代码)
Python的GPU编程实例——近邻表计算
GPU加速是现代工业各种场景中非常常用的一种技术,这得益于GPU计算的高度并行化。在Python中存在有多种GPU并行优化的解决方案,包括之前的博客中提到的cupy、pycuda和numba.cuda,都是GPU加速的标志性Python库。这里我们重点推numba.cuda这一解决方案,因为cupy的优势在于实现好了的众多的函数,在算法实现的灵活性上还比较欠缺;而pycuda虽然提供了很好的灵活性和相当高的性能,但是这要求我们必须在Python的代码中插入C代码,这显然是非常不Pythonic的解决方案。因此我们可以选择numba.cuda这一解决方案,只要在Python函数前方加一个numba.cuda.jit的修饰器,就可以在Python中用最Python的编程语法,实现GPU的加速效果。
DechinPhy
2021/09/08
1.9K0
Numba向量运算的强大
Hi! 大家好,又和大家见面了。上次给大家介绍了Numba中一句话加速for循环的@jit加速你的python脚本,今天继续给大家介绍另外一个我觉得很不错的Numba的用法。
阿凡亮
2020/04/14
1.2K0
【Go 语言社区】GO语言多核并行化的问题
package main import "fmt" type Vector []float64 func (v Vector) DoSome(i,n int, u Vector, c chan float64) { var sum float64 for ; i<n; i++ { sum += u[i] } c <- sum } const NCPU = 2 func (v *Vector) DoAll(u Vector) { c := make(chan float64,
李海彬
2018/03/19
8280
Python中最简单易用的并行加速技巧
我们在日常使用Python进行各种数据计算处理任务时,若想要获得明显的计算加速效果,最简单明了的方式就是想办法将默认运行在单个进程上的任务,扩展到使用多进程或多线程的方式执行。
朱卫军 AI Python
2022/05/18
1.3K0
Python中最简单易用的并行加速技巧
加速Python列表和字典,让你代码更加高效
今天,我们将讨论Python中的优化技术。在本文中,您将了解如何通过避免在列表和字典中进行重新计算来加快代码的速度。
HuangWeiAI
2020/07/27
7110

相似问题

Numba不会加速“使用numba加速代码”示例中的代码。

11

使用Numba进行并行化

00

使用numba.jit加速代码

12

使用Numba加速以下代码

19

numba循环并行化

13
添加站长 进交流群

领取专属 10元无门槛券

AI混元助手 在线答疑

扫码加入开发者社群
关注 腾讯云开发者公众号

洞察 腾讯核心技术

剖析业界实践案例

扫码关注腾讯云开发者公众号
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文