Numba 并行代码比顺序代码慢

2023-12-11

我是 Numba 新手,我正在尝试使用 Numba (版本 0.54.1)在 Python 中实现旧的 Fortran 代码,但是当我添加parallel = True程序实际上变慢了。我的程序非常简单:我更改 L x L 网格中的位置 x 和 y,并对网格中的每个位置执行求和

import numpy as np
import numba as nb

@nb.njit(parallel=True)
def lyapunov_grid(x_grid, y_grid, k, N):
    L = len(x_grid)
    lypnv = np.zeros((L, L))
    for ii in nb.prange(L):
        for jj in range(L):
            x = x_grid[ii]
            y = y_grid[jj]
            beta0 = 0
            sumT11 = 0

            for j in range(N):
                y = (y - k*np.sin(x)) % (2*np.pi)
                x = (x + y) % (2*np.pi)
                J = np.array([[1.0, -k*np.cos(x)], [1.0, 1.0 - k*np.cos(x)]])
                beta = np.arctan((-J[1,0]*np.cos(beta0) + J[1,1]*np.sin(beta0))/(J[0,0]*np.cos(beta0) - J[0,1]*np.sin(beta0)))
                T11 = np.cos(beta0)*(J[0,0]*np.cos(beta) - J[1,0]*np.sin(beta)) - np.sin(beta0)*(J[0,1]*np.cos(beta) - J[1,1]*np.sin(beta))
                sumT11 += np.log(abs(T11))/np.log(2)

                beta0 = beta
            
            lypnv[ii, jj] = sumT11/N
    return lypnv

# Compile
_ = lyapunov_grid(np.linspace(0, 1, 10), np.linspace(0, 1, 10), 1, 10)
# Parameters
N = int(1e3)
L = 128
pi = np.pi
k = 1.5
# Limits of the phase space
x0 = -pi
xf = pi
y0 = -pi
yf = pi
# Grid positions
x = np.linspace(x0, xf, L, endpoint=True)
y = np.linspace(y0, yf, L, endpoint=True)

lypnv = lyapunov_grid(x, y, k, N)

With parallel=False运行大约需要8秒,但是parallel=True大约需要14秒。我还用另一个代码进行了测试https://github.com/animator/mandelbrot-numba在这种情况下,并行化起作用了。

import math
import numpy as np
import numba as nb

WIDTH = 1000
MAX_ITER = 1000

@nb.njit(parallel=True)
def mandelbrot(width, max_iter):     
    pixels = np.zeros((width, width, 3), dtype=np.uint8)
    for y in nb.prange(width):
        for x in range(width):
            c0 = complex(3.0*x/width - 2, 3.0*y/width - 1.5) 
            c = 0
            for i in range(1, max_iter): 
                if abs(c) > 2: 
                    log_iter = math.log(i) 
                    pixels[y, x, :] = np.array([int(255*(1+math.cos(3.32*log_iter))/2), 
                                                int(255*(1+math.cos(0.774*log_iter))/2), 
                                                int(255*(1+math.cos(0.412*log_iter))/2)], 
                                               dtype=np.uint8) 
                    break
                c = c * c + c0
    return pixels

# compile
_ = mandelbrot(WIDTH, 10)

calcpixels = mandelbrot(WIDTH, MAX_ITER)

一个主要问题是第二个函数调用再次编译该函数。事实上,提供的参数的类型发生了变化:在第一次调用中,第三个参数是一个整数(int转变为np.int_)而在第二个调用中第三个参数(k) 是一个浮点数 (float转变为np.float64)。 Numba 会针对不同的参数类型重新编译函数,因为它们是从参数的类型推导出来的,并且它不知道您要使用np.float64第三个参数的类型(自从第一次编译该函数以来np.int_类型)。解决该问题的一个简单解决方案是将第一个调用更改为:

_ = lyapunov_grid(np.linspace(0, 1, 10), np.linspace(0, 1, 10), 1.0, 10)

然而,这并不是解决问题的有效方法。您可以为 Numba 指定参数类型,以便它在声明时编译该函数。这也消除了人为调用该函数的需要(使用无用的参数)。

@nb.njit('float64[:,:](float64[::1], float64[::1], float64, float64)', parallel=True)

注意(J[0,0]*np.cos(beta0) - J[0,1]*np.sin(beta0))第一次为零导致除以 0。

另一个主要问题来自于循环中许多小数组的分配导致标准分配器的争用 (see 这个帖子了解更多信息)。虽然 Numba 理论上可以优化它(即用局部变量替换数组),但实际上并没有,导致速度大幅下降和争用。希望在您的情况下,您不需要实际创建数组。最后,您可以仅在包围循环中创建它并在最内层循环中修改它。这是优化后的代码:

@nb.njit('float64[:,:](float64[::1], float64[::1], float64, float64)', parallel=True)
def lyapunov_grid(x_grid, y_grid, k, N):
    L = len(x_grid)
    lypnv = np.zeros((L, L))
    for ii in nb.prange(L):
        J = np.ones((2, 2), dtype=np.float64)

        for jj in range(L):
            x = x_grid[ii]
            y = y_grid[jj]
            beta0 = 0
            sumT11 = 0

            for j in range(N):
                y = (y - k*np.sin(x)) % (2*np.pi)
                x = (x + y) % (2*np.pi)
                J[0, 1] = -k*np.cos(x)
                J[1, 1] = 1.0 - k*np.cos(x)
                beta = np.arctan((-J[1,0]*np.cos(beta0) + J[1,1]*np.sin(beta0))/(J[0,0]*np.cos(beta0) - J[0,1]*np.sin(beta0)))
                T11 = np.cos(beta0)*(J[0,0]*np.cos(beta) - J[1,0]*np.sin(beta)) - np.sin(beta0)*(J[0,1]*np.cos(beta) - J[1,1]*np.sin(beta))
                sumT11 += np.log(abs(T11))/np.log(2)

                beta0 = beta
            
            lypnv[ii, jj] = sumT11/N
    return lypnv

以下是旧 2 核机器(具有 4 个硬件线程)上的结果:

Original sequential:   15.9 s
Original parallel:     11.9 s
Fix-build sequential:  15.7 s
Fix-build parallel:    10.1 s
Optimized sequential:  2.73 s
Optimized parallel:    0.94 s

优化后的实现比其他的要快得多。与原始版本相比,并行优化版本的扩展性非常好(比顺序版本快 2.9 倍)。最后,最好的版本是关于快 12 倍比原来的并行版本。我期望在具有更多内核的最新机器上计算速度更快。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Numba 并行代码比顺序代码慢 的相关文章

  • 如何跳过财务图中的空日期(周末)

    ax plot date dates dates highs lows 我目前正在使用此命令来绘制财务高点和低点Matplotlib http en wikipedia org wiki Matplotlib 效果很好 但如何删除 x 轴上
  • 修复类以在 Flask 会话中启用对象存储[重复]

    这个问题在这里已经有答案了 我有一个自定义类 Passport 其中包含活动用户身份和权限 我曾经将它存储在会话中 如下所示 p Passport p do something fancy session passport p 它就奏效了
  • [python]没有属性“TessBaseAPI”

    当我编译代码时出现错误 import tessercat api tesseract TessBaseAPI 错误是 AttributeError 模块 对象没有属性 TessBaseAPI 我已经安装了tesseract via pip
  • S3 选择检索 CSV 中的标头

    我尝试使用以下代码从存储在 S 存储桶中的 CSV 中获取记录子集 s3 boto3 client s3 bucket bucket file name file sql stmt SELECT S FROM s3object S LIMI
  • 检查多维 numpy 数组的所有边是否都是零数组

    n 维数组有 2n 个边 1 维数组有 2 个端点 2 维数组有 4 个边或边 3 维数组有 6 个 2 维面 4 维数组有 8 个边 ETC 这类似于抽象 n 维立方体发生的情况 我想检查 n 维数组的所有边是否仅由零组成 以下是边由零组
  • 使用Python进行图像识别[关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我有一个想法 就是我想识别图像中的字母 可能是 bmp或 jpg 例如 这是一个包含字母 S 的 bmp 图像 我想做的是使用Pyth
  • 将分布拟合到直方图

    I want to know the distribution of my data points so first I plotted the histogram of my data My histogram looks like th
  • 比较两个文本文件并计算差异

    我一直在尝试在Python中比较两个文本文件 本质上我想打开它们并一次比较一个字符 如果字符不同 则向计数器添加1 然后显示该值 这是我到目前为止所拥有的 usr bin env python diff 0 import random im
  • 直接打开Spyder还是通过Pythonxy打开?

    之前 我一直在运行PythonSpyder 我总是开始Spyder直接双击其图标 今天突然发现我还有一个东西叫Python x y 我注意到我也可以开始Spyder通过它 这两种方法有什么区别吗 如果不是的话 有什么意义Python x y
  • PyPI 上的轮子平台约束有什么限制吗?

    是否有任何地方 PEP 或其他地方 声明关于 Linux 轮子上传范围的限制 PyPI http pypi io 应该有 具体来说 上传是否被认为是可接受的做法linux x86 64轮子到 PyPI 而不是manylinux1 x86 6
  • 如何在C中同时运行两个子进程?

    所以我开始学习并发编程 但由于某种原因我什至无法掌握基础知识 我有一个名为 fork c 的文件 其中包含一个 main 方法 在此方法中 我将 main 分叉两次 分别进入子进程 1 和 2 在孩子 1 中 我打印了字符 A 50 次 在
  • MPI 从文本文件中读取

    我正在学习 MPI 编程 我遇到了这个问题 假设我有一个包含 100 000 行 行的 txt 文件 如何将它们分块以供 4 个处理器处理 即我想让处理器 0 负责第 0 25000 行的处理 让处理器 1 负责第 25001 50000
  • 哪个更快:清除集合或实例化新集合

    我的代码中有一些通用列表 其中有数十或数百个元素 有时我需要用其他对象重新填充此列表 所以问题是 调用什么会更快Clear 方法或创建一个new List
  • 让 TensorFlow 在 ARM Mac 上使用 GPU

    我已经安装了TensorFlow在 M1 上 ARM Mac 根据这些说明 https github com apple tensorflow macos issues 153 一切正常 然而 模型训练正在进行CPU 如何将培训切换到GPU
  • 如何从外语线程调用Python函数(C++)

    我正在开发一个程序 使用 DirectShow 来抓取音频数据 媒体文件 DirectShow 使用线程将音频数据传递给回调 我的程序中的函数 然后我让该回调函数调用另一个函数 Python 中的函数 我使用 Boost Python 来包
  • Python 可以替代 Java 小程序吗?

    除了制作用于物理模拟 如抛射运动 重力等 的教育性 Java 小程序之外 还有其他选择吗 如果你想让它在浏览器中运行 你可以使用PyJamas http pyjs org 这是一个 Python 到 Javascript 的编译器和工具集
  • 在哪里可以找到Python内置序列类型的时间和空间复杂度

    我一直无法找到此信息的来源 无法亲自查看 Python 源代码来确定这些对象是如何工作的 有谁知道我可以在网上找到这个吗 结帐时间复杂度 http wiki python org moin TimeComplexitypy dot org
  • 需要一个从 yaml 文件中提取内容并输出为 csv 文件的脚本

    我对 python 很陌生 但我很感激您帮助指导我创建一个简单的脚本 该脚本读取一堆 yaml 文件 同一目录中的大约 300 个文件 并从 yaml 文件并将其转换为 csv yaml 文件中内容的示例 code 9313 degrees
  • Chrome + 另一个进程:进程间通信比 HTTP/XHR 请求更快?

    我有一个进程 1 对视频流进行实时图像处理 我需要在 Chrome 中的 HTML 页面中渲染该视频 同一台计算机上的进程 2 在canvas or img or videoHTML5 元素 由于我有 1000x1000 像素 x 3 字节
  • 使用Python的线程模块调用ctypes函数比使用多处理更快?

    我一生都无法找出这个问题的答案 我编写了一个可以执行数百次繁重计算的脚本 我有一个绝妙的主意 将这些计算任务编写为 C 然后使用 Python 的 ctypes 与它们交互 我心想 我什至可以使用并行性进一步优化它 我最初的方法是使用线程

随机推荐

  • 将 SupportMapFragment 放置在 DialogFragment 上

    我试图在 DialogFragment 上添加 SupportMapFragment 但它返回error inflating class fragment 我不明白为什么它会被退回error inflating class fragment
  • Zend 框架有文件结构的修复版本吗?

    作为 Zend 框架的新手 我对该框架有一些与版本相关的问题 Zend Framework 是否有固定的文件结构 即文件布局的固定形式 如果是这样 这个文件结构是否会根据框架版本而变化 如果是这样 是否有任何参考资料可以了解文件结构的所有差
  • R 中的对数概率图?

    Does anyone know how to create log probability plot like this one in R where the x axis is probability and y axis is in
  • ListFragment 作为 DialogFragment

    是否可以显示ListFragment as Dialog 或者没有办法 我应该实现我自己的ListView empty TextView和不确定的ProgressBar inside DialogFragment myself 另外一个选择
  • 关于“self”关键字

    void Foo void Foo 在该方法中 void Foo 关键字self表示该类的一个实例 但在方法中 void Foo 关键字是什么self意思是 这是否意味着Class self是每个方法的两个隐式参数之一 它是一个指向对象的指
  • 使用 SQLAlchemy 的 sql.func 注册自定义函数

    如何在 sqlalchemy 中应用自定义过滤器 我尝试过 hybrid property 和 hybrid method 然而 他们给出了错误 这是我的代码 class Product db Model tablename product
  • 每天在 Swift 中重置 NSUserDefault 键

    我正在编写一个应用程序 需要每天重置存储在 NSUserDefaults 中的密钥 00 00 时 我已经实现了这一目标 但我使用的方法是一种混乱且不可靠的方法 有没有简单的方法可以实现我的目标 这是代码 extension NSDate
  • WooCommerce 在结帐时使用 Optgroup 选择下拉菜单

    我在用着WordPress 5 0 2 with WooCommerce 3 5 3我在结帐页面上创建了一个选择下拉菜单 效果很好 但是我想在其中添加一些选项组来组织选择选项 这是我的代码函数 php add action woocomme
  • WatchKit 扩展包 ID 不可用

    我已将手表套件应用程序添加到我的 iOS 应用程序中 一切工作正常且运行良好 直到我想在两个应用程序之间共享数据 每当我尝试在手表套件扩展上添加 应用程序组 功能时 它都会告诉我我的捆绑包 ID com myrealappid watchk
  • 虚拟子域:每个用户一个子域

    在我的网站上 我使用虚拟主机 因此我的用户可以拥有虚拟域 如 user1 mydomain com user2 mydomain com 等 问题是 在 user1 domain com 等虚拟域上 索引页面始终与我的索引页面 http m
  • 检测未分配的局部变量的错误(当动态变量影响代码流预测时)

    文档意味着 out 参数在发送到函数之前不需要初始化 只需声明 然而 这段代码 class Program static void Main dynamic p string s if p null T out s System Conso
  • RStudio:使用 roxygen2 构建包。不生成 NAMESPACE 文件

    这是我第一次构建 R 包 并在 devtools 和 roxygen2 的帮助下完成 在 R 目录中编写一个简单的函数并使用 devtools 制作一个说明文件后 我第一次尝试构建并重新加载 但出现错误 gt devtools docume
  • 确定函数返回类型的最简单方法

    给定一个非常简单但冗长的函数 例如 int foo int a int b int c int d return 1 using ReturnTypeOfFoo 确定函数返回类型的最简单和简洁的方法是什么 ReturnTypeOfFoo 在
  • 正在加载 Apple Pay 送货地址 无街道

    我正在尝试从以下地址中提取送货地址ABRecordRef由苹果公司提供 我有以下内容 但我的街道总是返回nil ABMultiValueRef addresses ABRecordCopyValue abRecordRef kABPerso
  • 如何使用 P3D 渲染器实现 noSmooth()?

    我想使用 P3D 渲染器通过 PGraphics 实例渲染基本的 3D 形状 而无需任何锯齿 平滑 但 noSmooth 似乎不起作用 在 OF 我记得打电话给setTextureMinMagFilter GL NEAREST GL NEA
  • 在没有公共块的情况下通过子例程将一组变量值传递给函数有哪些方法?

    我不想在我的程序中使用公共块 我的主程序调用一个子例程 该子例程调用一个函数 该函数需要来自子例程的变量 将信息集从子例程传递到函数有哪些方法 program call CONDAT i j end program SUBROUTINE C
  • 未安装新组件的文件,因为旧组件具有相同的文件

    我们遇到重大更新时未安装文件的问题 我们有一个重大更新
  • 如何在 Swift 中为 Int 数组(自定义字符串结构)实现 Hashable 协议

    我正在制作一个结构 其作用就像String 不同之处在于它仅处理 Unicode UTF 32 标量值 因此 它是一个数组UInt32 See 这个问题了解更多背景 我想做的事 我希望能够使用我的自定义ScalarStringstruct
  • 如何防止Windows进入空闲状态?

    我正在开发一个 C 应用程序 该应用程序在后台运行 无需任何 Windows 控件 我想通知 Windows 我的应用程序仍然处于活动状态 以防止 Windows 进入空闲状态 是否有任何 API 可供我的应用程序调用 以通知 Window
  • Numba 并行代码比顺序代码慢

    我是 Numba 新手 我正在尝试使用 Numba 版本 0 54 1 在 Python 中实现旧的 Fortran 代码 但是当我添加parallel True程序实际上变慢了 我的程序非常简单 我更改 L x L 网格中的位置 x 和