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

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

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

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

代码语言:javascript
代码运行次数:0
运行
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
代码运行次数:0
运行
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
代码运行次数:0
运行
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
代码运行次数:0
运行
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
代码运行次数:0
运行
AI代码解释
复制
nbFinalDict = nbTest(nbValues, nbParams, Sno, dec_, upd_)

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

代码语言:javascript
代码运行次数:0
运行
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
代码运行次数:0
运行
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
代码运行次数:0
运行
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

复制
相关文章
Winform 中 DesignMode 返回值不正确的问题。
本文转载:http://blog.csdn.net/sabty/article/details/5325260
跟着阿笨一起玩NET
2018/09/18
1.6K0
js --- return返回值 闭包
有权访问另一个函数作用域内变量的函数都是闭包。这里 inc 函数访问了构造函数 a 里面的变量 n,所以形成了一个闭包。
小蔚
2019/09/11
4.4K0
Go:命名返回值和直接返回值的使用与潜在隐患
在这个例子中,虽然给命名返回值result赋值了,但最终函数却直接返回了a + b的计算结果,而不是result的值。
运维开发王义杰
2023/08/21
3200
Go:命名返回值和直接返回值的使用与潜在隐患
带返回值的函数,闭包,沙箱,递归详解
那了解了函数 this 指向的不同场景之后,我们知道有些情况下我们为了使用某种特定环境的 this 引用, 这时候时候我们就需要采用一些特殊手段来处理了,例如我们经常在定时器外部备份 this 引用,然后在定时器函数内部使用外部 this 的引用。 然而实际上对于这种做法我们的 JavaScript 为我们专门提供了一些函数方法用来帮我们更优雅的处理函数内部 this 指向问题。 这就是接下来我们要学习的 call、apply、bind 三个函数方法。
Dream城堡
2018/10/09
1.9K0
toupper和tolower的返回值是int型值
如题,C++中函数库<cctype>定义了函数toupper和tolower,需要注意的是它们的返回值是int型的。直接上程序
用户7886150
2021/02/09
9000
Gorm Update 返回的影响条数不正确问题
Gorm 连接 mysql 使用的是 github.com/go-sql-driver/mysql 驱动, 但是这个驱动中 clientFoundRows 默认是 false, 也就是说返回的影响条数是实际的影响条数, 而不是匹配的条数, 也就是说如果要修改的数据和表中数据一样, 就不会返回影响条数, 这和 mysql 官方不一致
北漂的我
2021/11/25
2.5K0
scala:把函数作为值或参数进行传递、作为返回值进行返回,以及什么是闭包和柯里化
内层函数访问外层函数的局部变量,会自动延长外层函数局部变量的生命周期,与内层函数形成一个闭合的效果,我们称之为闭包
孙晨c
2021/02/25
1.9K0
Go语言函数的参数和返回值
第一类对象(first-class object)指可在运行期创建,可用作函数参数或返回值,可存入变量的实体。最常见的用法就是匿名函数。
博文视点Broadview
2020/06/12
2.5K0
Go语言函数的参数和返回值
一道正确率只有15%的命名返回值和闭包的问题
这道题考查的点就是命名返回值+闭包,把上面的代码换成等效的匿名返回值代码你就明白了:
Golang梦工厂
2022/07/11
5450
Go 100 mistakes之不正确的值比较
我们从一个具体的例子开始。我们将创建一个customer结构体,并使用 == 操作符来比较两个实例。下面的代码将会输出什么呢?
Go学堂
2023/01/31
1.1K0
HarmonyOS实战—滑动事件的坐标和返回值
1. 滑动事件获取手指位置 滑动事件的三个动作: [在这里插入图片描述] 获取手指的位置就涉及到坐标的概念,通过获取到 x、y、z就可以缺任意一个点的位置 [在这里插入图片描述] 手机中的坐标: [在这里插入图片描述] 除了 x、y轴,还有z轴,在鸿蒙手机当中,完整的坐标如下,是一个立体的三维体系,但平时z轴用的非常少,一般情况只需考虑x、y轴就行了。 [在这里插入图片描述] 结合滑动事件的三个动作和坐标来分析滑动 [在这里插入图片描述] [在这里插入图片描述] [在这里插入图片描述] 2. 获取按下时手指
兮动人
2021/08/23
1.1K0
HarmonyOS实战—滑动事件的坐标和返回值
Java 遍历对象的属性和值,封装返回 Map
有时候需要获取对象的属性值,属性少的话就好设置了,属性一多就不好设置了。 我们可以通过反射来遍历 UpdateWeb updateWeb = new UpdateWeb(); Field[] fields = updateWeb.getClass().getDeclaredFields(); for (Field field : fields){ field.setAccessible(true); String
Alone88
2019/11/11
7.6K0
ExecuteScalar()_getchar的返回值
大家好,我是架构君,一个会写代码吟诗的架构师。今天说一说ExecuteScalar()_getchar的返回值,希望能够帮助大家进步!!!
Java架构师必看
2022/06/19
1.7K0
[Python基础06]函数的参数&返回值
我们通常在进行一些功能处理的过程中,需要执行一行或者多行代码来完成整个业务流程的处理,如:
周小董
2022/04/12
4K0
[Python基础06]函数的参数&返回值
printf函数的返回值!
这样的题目第一眼看上去有一点懵,其实它考的就是printf函数的返回值,及输出字符的个数。 输出结果
lexingsen
2022/02/24
3.3K0
返回指针值的函数(2)
请自己理解,比较简单 /**对前面那个问题,输出有不及格成绩的学生的所有成绩**/ #include <stdio.h> #include <stdlib.h> int main() { float a[3][4]={{50,99,80,50},{55,60,85,90},{10,54,74,26}}; float *p; float *aa(float (*pt)[4]); int i; for(i=0;i<3;i++) { p=aa((
谙忆
2021/01/19
2.7K0
JS|函数的返回值
这个看似能输出结果,实则是在逻辑上是不合理的,我们函数是做某件事或者实现某种功能,而在上面的代码中,函数提供了‘aru’,结果自己却把该参数给输出了。这好比是鸡生了蛋,自己却把蛋给吃了。那我们吃蛋壳吗?所以,接下来我会介绍一种逻辑更严谨的代码。
算法与编程之美
2020/03/12
11.4K0
ExecuteScalar 返回值
Execute=执行; scalar=数量; so, 从字面意思来讲,可将ExecuteScalar 和ExecuteNonQuery对比来学习。 ExecuteScalar()方法的作用是: 执行查询,并返回查询所返回的结果集中第一行的第一列。所有其他的列和行将被忽略。
全栈程序员站长
2022/09/14
1.9K0
函数的变量+返回值
函数的变量: 局部变量 和 全局变量 Python中的任何变量都有特定的作用域 在函数中定义的变量一般只能在该函数内部使用,这些只能在程序的特定部分使用的变量我们称之为局部变量 在一个文件顶部定义的变量可供文件中的任何函数调用,这些可以为整个程序所使用的变量称为全局变量 (1)、局部函数: #!/usr/bin/python def fun(): x = 100 ##定义一个内部的函数是 x = 100,只在fun() 内部有效。 print x fun() 执行结果: [[e
老七Linux
2018/05/31
4.9K0
controller方法的返回值
需要方法结束时,定义ModelAndView,将model和view分别进行设置。
用户10325771
2023/03/14
2K0
controller方法的返回值

相似问题

雪碧包和colorWithPatternImage

31

雪碧包归还?

50

雪碧的位置不正确

24

雪碧工具包-如何移动雪碧?

12

iAds禁用触动雪碧包和Swift

12
添加站长 进交流群

领取专属 10元无门槛券

AI混元助手 在线答疑

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

洞察 腾讯核心技术

剖析业界实践案例

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