Numba加速计算(CPU + GPU + prange)

news2025/4/6 2:13:01

文章目录

  • 加速方法:Numba、CuPy、PyTorch、PyCUDA、Dask、Rapids
  • 一、Numba简介
  • 二、Numba类型:CPU + GPU
  • 三、项目实战 —— 数组的每个元素加2
    • 3.1、使用 python - range 循环计算 —— (时耗:137.37 秒)
    • 3.2、使用 python - numpy 数组计算 —— (时耗:1.05 秒)
    • 3.3、使用 numba - CPU 加速计算 —— (时耗:13.85 秒)
    • 3.4、使用 numba - GPU 加速计算 —— (时耗:0.13 秒)
    • 3.5、使用 numba.prange 并行循环计算

在这里插入图片描述

加速方法:Numba、CuPy、PyTorch、PyCUDA、Dask、Rapids

加速方法简介支持平台适用范围
Numba通过即时编译(JIT)来加速Python函数CPU + GPU数值计算(密集循环)
CuPyNumPy的GPU加速库GPU数组操作和数学计算(大规模数据集)
PyTorch深度学习框架GPU张量操作和自动求导
PyCUDA与CUDA交互的Python库GPU在Python中编写CUDA代码并在GPU上执行
Dask并行计算库CPU + GPU并行操作(大规模数据集)
Rapids基于NVIDIA GPU加速的数据科学生态系统GPU提供数据处理和机器学习的库(如cuDF、cuML)

一、Numba简介

Numba官网:专为 NumPy 科学计算而打造的,用于加速 Python 代码的即时编译器(Just-In-Time, JIT Compiler)。

由 Anaconda 公司主导开发

  • 原理:使用行业标准LLVM编译器库在运行时,将 Python 和 NumPy 代码的子集转换为快速的机器代码。
  • 速度接近 C 或 FORTRAN 的速度
  • 操作简单只需将 Numba 装饰器之一应用到 Python 函数,Numba 将使用即时编译(JIT)编译为原生代码(机器代码),然后加速运行。
    • 不需要替换 Python 解释器、运行单独的编译步骤,也不需要安装 C/C++ 编译器)
    • 原生代码(Native code):直接在计算机硬件上执行的机器代码。

适用范围

  • 只支持NumPy库(Numba 基于 NumPy 底层代码开发)
  • 不支持其余的Python库(自定义-重开发:将函数分解为底层代码)
    • (1)NumPy数值计算(CPU):将 Python 函数即时编译为机器代码,用于加速数学运算等计算密集型任务(最初设计)。
    • (2)NumPy数组操作(CPU):高效处理 NumPy 的数组操作和广播操作,且可以加速大型数组操作。
    • (3)支持并行计算(CPU和GPU):使用 prange 来并行处理循环结构
    • (4)支持GPU加速:将代码转移到 GPU 上以加速执行

二、Numba类型:CPU + GPU

Numba官网案例1:并行化测试(@jit、prange、dask)
Numba官网案例2:GPU加速

import numba  # pip install numba
from numba import cuda, jit

##############################################################
"""Numba装饰器:CPU加速"""
函数说明:@jit(nopython=True, parallel=True, target='cpu')
输入参数:
		(1)加速模式:nopython=True(默认)、forceobj=True			
				备注:Numba默认使用nopython编译函数。若无法完成编译,将使用对象模式(将导致性能损失)。
		(2)并行模式:parallel=True(默认)
		(3)指定平台:target='cpu'(默认)、target='gpu'

> 多种不同的应用
		@jit()				# 适用于科学计算、数值计算和密集计算。
		@jit(target='cpu')	# CPU加速(与@jit()等效)
		@jit(target='cuda')	# GPU加速(与@cuda.jit()等效)
		@jit(nopython=True)	# 强制使用 nopython 模式,将函数尽可能编译成机器代码,如果无法完成编译则会引发错误(与@njit()等效)。
		@jit(forceobj=True)	# 强制使用对象模式,而不是默认的 nopython 模式。适用于一些特殊情况,如涉及动态类型的代码。
		@jit(parallel=True)	# 尝试并行化循环,充分利用多核处理器的性能(需将range转换为prange)。

		@njit()			# 强制使用 nopython 模式,比@jit更快但更严格(只接受Numpy数据类型)。
		@vectorize		# 适用于元素级别的向量化操作(单输入和单输出)、(输出数组的形状由输入数组的形状决定)
		@guvectorize	# 适用于元素级别的向量化操作(多输入和输出数组)、(输出数组的形状可以指定)
		@stencil		# 一种基于固定模板的局部计算。通过访问输入数组的邻域元素来计算输出数组的每个元素。
##############################################################
"""Numba装饰器:GPU加速"""	
		@cuda.jit()		# 用于在GPU上执行 CUDA 加速
	
# @cuda.jit()与 @cuda.jit ———— 若不传递参数,两者是等效的。
# 		@cuda.jit 	使用默认选项 @cuda.jit(),并且不需要传递任何参数。
# 		@cuda.jit() 显式调用 @cuda.jit 装饰器,并且可以传递一些选项参数。

三、项目实战 —— 数组的每个元素加2

3.1、使用 python - range 循环计算 —— (时耗:137.37 秒)

import numpy as np

def numpy_cpu_kernel(input_array):
    shape = input_array.shape  # 获取数组形状
    result_array = np.zeros(shape)  # 初始化一个全零数组,形状与输入数组相同
    # 循环遍历每个元素,将其加 2
    for z in range(shape[0]):
        for y in range(shape[1]):
            for x in range(shape[2]):
                result_array[z, y, x] = input_array[z, y, x] + 2
    return result_array

if __name__ == "__main__":
    # 在主机上创建 3D 数组
    input_data = np.zeros((1024, 1024, int(1024 * 0.5)))

    import time
    start_time = time.time()
    # 在主机上调用 NumPy 函数
    result_array_on_host = numpy_cpu_kernel(input_data)
    print(f"总共耗时: {time.time() - start_time:.2f} 秒")

    # 打印结果数组的形状和最大值
    print(result_array_on_host.shape)
    print(result_array_on_host.max())
    
    """
    总共耗时: 137.37 秒
    """

3.2、使用 python - numpy 数组计算 —— (时耗:1.05 秒)

import numpy as np

def numpy_cpu_kernel(input_array):
    return input_array + 2  # 将输入数组的每个元素加 2

if __name__ == "__main__":
    # 在主机上创建 3D 数组
    input_data = np.zeros((1024, 1024, int(1024 * 0.5)))

    import time
    start_time = time.time()
    # 在主机上调用 NumPy 函数
    result_array_on_host = numpy_cpu_kernel(input_data)
    print(f"总共耗时: {time.time() - start_time:.2f} 秒")

    # 打印结果数组的形状和最大值
    print(result_array_on_host.shape)
    print(result_array_on_host.max())

    """
    总共耗时: 1.05 秒
    """

3.3、使用 numba - CPU 加速计算 —— (时耗:13.85 秒)

from numba import jit
import numpy as np

# 使用 Numba 的 jit 装饰器进行即时编译
@jit(nopython=True)
def my_cpu_kernel(input_array, output_array):
    # 使用三个嵌套循环遍历 3D 输入数组的每个元素
    for x in range(input_array.shape[2]):
        for z in range(input_array.shape[0]):
            for y in range(input_array.shape[1]):
                # 执行简单的操作:将输入数组的元素加 2,并将结果存储到输出数组中
                output_array[z, y, x] = input_array[z, y, x] + 2


if __name__ == "__main__":
    # (1)在主机上创建输入数组
    input_data = np.zeros((1024, 1024, int(1024 * 0.5)))  # 创建一个全零的3D数组
    result_array_on_host = np.zeros_like(input_data)

    # (2)在 CPU 上调用加速函数
    import time
    start_time = time.time()
    my_cpu_kernel(input_data, result_array_on_host)
    print(f"总运行时间: {time.time() - start_time:.2f} 秒")  # 打印运行时间

    # 打印输出数组的形状和最大值
    print(result_array_on_host.shape)
    print(result_array_on_host.max())

	"""
	总共耗时: 13.858259439468384
	"""

3.4、使用 numba - GPU 加速计算 —— (时耗:0.13 秒)

【深度学习环境配置】Anaconda +Pycharm + CUDA +cuDNN + Pytorch + Opencv(资源已上传)

在CUDA编程中

  • CPU和主存(RAM):称为主机(Host)
  • GPU和显存(VRAM):称为设备(Device)
    • CPU无法直接读取显存数据,GPU无法直接读取主存数据;
    • 主机与设备必须通过总线(Bus)相互通讯;

RAM是CPU的主内存,显存是GPU的专用内存

GPU计算流程:

  • (1)cuda.to_device():将主机端的数据拷贝到设备端上,并在GPU上分配与主机上数据相同大小的内存。
  • (2)cuda.device_array_like()或cuda.device_array():在GPU上分配用于输出数据的内存。
  • (3)gpu[blocks_per_grid, threads_per_block]:在CPU上调用GPU函数,启动GPU多核并行计算(详细看1.2)。
  • (4)CPU与GPU异步计算;
    • GPU函数的启动方式是异步的
      • 异步计算CPU不会等待GPU函数执行完毕才执行下一行代码。
      • 同步计算:在调用的GPU函数后面添加 cuda.synchronize() —— 表示CPU需要等待GPU函数执行后再计算。
  • (5)cuda.copy_to_host():将GPU设备端的计算结果拷贝回CPU主机端上。Python通过Numba实现GPU加速
  1. numba的GPU加速 —— 1天到1分钟的转变
  2. numba的GPU加速 —— 超过Numpy的速度有多难?
from numba import cuda
import numpy as np

# 使用 Numba 的 CUDA 装饰器进行 GPU 加速
@cuda.jit
def my_cuda_kernel(input_array, output_array):
    x = cuda.grid(1)  # 使用1维索引
    if x < input_array.shape[2]:  # 检查索引是否在数组范围内
        for z in range(input_array.shape[0]):
            for y in range(input_array.shape[1]):
                # 执行简单的操作:将输入数组的元素加 2,并将结果存储到输出数组中
                output_array[z, y, x] = input_array[z, y, x] + 2

if __name__ == "__main__":
    # (1)在主机上创建3D数组
    input_data = np.zeros((1024, 1024, int(1024 * 0.5)))
    # (2)拷贝数据 + 在GPU上分配内存
    device_input_array = cuda.to_device(input_data)  # 将主机上的input_data复制到GPU上
    device_output_array = cuda.device_array_like(input_data)  # 在GPU上开辟一个与 input_data 相同形状的数组用于存储计算结果。
    # (3)定义线程块的大小 + 线程块的数量
    threads_per_block = (16,)
    blocks_per_grid_x = (input_data.shape[2] + threads_per_block[0] - 1) // threads_per_block[0]
    blocks_per_grid = (blocks_per_grid_x,)
    # (4)调用CUDA核函数
    import time
    start_time = time.time()
    my_cuda_kernel[blocks_per_grid, threads_per_block](device_input_array, device_output_array)
    print(f"总运行时间: {time.time() - start_time:.2f} 秒")  # 打印运行时间
    # (5)将结果从GPU复制回主机
    result_array_on_host = device_output_array.copy_to_host()  # 将计算结果从GPU复制回主机
    print(result_array_on_host.shape)
    print(result_array_on_host.max())
    """
    总共耗时: 0.136000394821167
    """

3.5、使用 numba.prange 并行循环计算

prange(parallel range)类似于Python标准库中的 range,但专用于并行化循环计算(引入额外的开销)。可以在多个处理器核心上同时执行循环,以提高密集型计算的性能。

适用范围:

  • 大规模数据和密集型计算只有在循环非常庞大时,才可以充分利用多核处理器,否则将导致耗时更长。
  • 独立迭代:每个迭代之间不存在依赖关系时,若后面的迭代依赖于前面迭代的计算结果(类似于递归函数),可能会导致耗时更长。
  • NumPy数组:使用NumPy数组可以表现出更好的性能。
import random
import numba
import time

###############################################
# numba加速 + 串行版本(Serial version)
###############################################
@numba.jit(nopython=True)
def monte_carlo_pi_serial(nsamples):
    acc = 0
    for i in range(nsamples):
        x = random.random()
        y = random.random()
        if (x ** 2 + y ** 2) < 1.0:
            acc += 1
    return 4.0 * acc / nsamples

###############################################
# numba加速 + 并行版本(Parallel version)
###############################################
@numba.jit(nopython=True, parallel=True)
def monte_carlo_pi_parallel(nsamples):
    acc = 0
    for i in numba.prange(nsamples):
        x = random.random()
        y = random.random()
        if (x ** 2 + y ** 2) < 1.0:
            acc += 1
    return 4.0 * acc / nsamples


if __name__ == "__main__":
    start_time = time.time()
    monte_carlo_pi_serial(33000000)  # 串行版本
    print(f"总共耗时: {time.time() - start_time:.2f} 秒")

    start_time = time.time()
    monte_carlo_pi_parallel(33000000)  # 并行版本
    print(f"总共耗时: {time.time() - start_time:.2f} 秒")

    """
    一万次循环:
        0.27770018577575684
        0.42730212211608887

    三千万次循环:
        0.46489500999450684
        0.47499847412109375

    10亿次循环:
        6.7297399044036865
        1.1980292797088623

    100亿次循环:
        67.73275828361511
        6.892062425613403
    """

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2064589.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

天空卫士五载出海路:让国际数据安全舞台,有我们的身影

在全球化和“一带一路”倡议的推动下&#xff0c;中国企业正加速出海&#xff0c;探索新的增长机会。中国联通联合天空卫士等合作伙伴&#xff0c;推出“安全产业链联合出海计划”&#xff0c;旨在汇聚资源&#xff0c;打造国家级网络安全产业平台&#xff0c;推动出海业务的发…

C# 必备技能—项目打包

目录 前言 准备工作 第一步 第二步 第三步 扩展 总结 最后 前言 在C#开发中&#xff0c;项目打包是一个重要的环节&#xff0c;将你的应用程序及其依赖项组织成一个或多个可以在目标系统上安装和运行的包。 这对于发布应用程序至关重要&#xff0c;因为它确保了最终用…

快9月才开强化❓张宇36讲+1000题速刷指南

很多同学问&#xff0c;基础跟的是张宇老师&#xff0c;但是感觉25版张宇36讲太厚&#xff0c;可不可以不看&#xff0c;换其他老师 当然可以&#xff0c;但是如果你基础跟的是张宇老师&#xff0c;那强化阶段换成其他老师&#xff0c;可能会重复听一些内容&#xff0c;造成时…

手机APP应用移动端身份证识别技术,实现扫描录入身份信息

随着移动互联网的的发展&#xff0c;越来越多的公司都推出了自己的手机APP&#xff0c;这些APP多数都涉及到个人身份证信息的输入认证&#xff08;即实名认证&#xff09;&#xff0c;如果手动去输入身份证号码和姓名&#xff0c;速度非常慢&#xff0c;且用户体验非常差。为了…

Godot《躲避小兵》实战之游戏开始界面制作

我们的游戏还需要用户可操作的界面&#xff0c;比如开始游戏&#xff0c;退出以及显示分数等UI界面。 创建新场景&#xff0c;点击“其他节点”按钮&#xff0c;然后添加一个 CanvasLayer 节点并命名为 HUD。“HUD”是“heads-up display”&#xff08;游戏信息显示&#xff0…

2055. 欧拉路

代码 #include<bits/stdc.h> using namespace std; int n,e,a[35][35],d[35],r[55],k0; void dfs(int x) {for(int i1;i<n;i){if(a[x][i]1){a[x][i]0;a[i][x]0;dfs(i);}}k;r[k]x; } int main() {int x,y,i,s1;cin>>n>>e;for(i1;i<e;i){cin>>x&g…

TCP协议中的三次握手

WHAT&#xff1a;什么是三次握手&#xff1f; 建立TCP需要三次握手才能建立&#xff0c;而断开连接则需要四次挥手。 TCP链接是全双工的&#xff0c; 因此每个方向上都必须要关闭 三次握手一定是B向S发起&#xff0c;但是四次挥手可以是B向S也可以是S向B发起的 比如&#xff1a…

【中仕公考怎么样】公务员行测考什么内容?

行政职业能力测验&#xff0c;也就是我们常说的“行测”。是公务员考试笔试环节中的核心科目&#xff0c;占据总成绩的50%。主要考察考生在言语理解与表达、数量关系、判断推理、资料分析和常识判断方面的能力。 国考行测分为副省级、地市级以及行政执法类&#xff0c;题目数量…

MyBatis入门(上)---初识

在应⽤分层学习时, 我们了解到web应⽤程序⼀般分为三层&#xff0c;即&#xff1a;Controller、Service、Dao . 之前的案例中&#xff0c;请求流程如下: 浏览器发起请求, 先请求Controller, Controller接收到请求之后, 调⽤ Service进⾏业务逻辑处理, Service再调⽤Dao, 但是Da…

[C++]set和map的介绍及使用

关于set和map的接口函数部分&#xff0c;只重点介绍一些相较于别的容器有特殊地方的接口&#xff0c;set和map的接口可以触类旁通。 一、概念 &#xff08;一&#xff09;、关联式容器 关联式容器存储的元素是一个个的键值对<key,value>。通过键&#xff08;key&#x…

多线程中常见问题

1、为什么不建议使用Executors来创建线程池&#xff1f; 除开有可能造成的OOM外&#xff0c;使用Executors来创建线程池也不能自定义线程的名字&#xff0c;不利于排查问题&#xff0c;所以建议是直接使用ThreadPoolExecutor来定义线程池&#xff0c;这样可以灵活控制 2、线程…

队列操作(深入理解FreeRTOS队列之队列实战)

文章目录 一、队列的操作二、学习总结 在FreeRTOS中&#xff0c;队列的本质是环形缓冲区。 一、队列的操作 1、创建队列 2、写队列 3、读队列 详细可看此篇博客&#xff1a;FreeRTOS——队列&#xff08;基于百问网DshanMCU-F103实现挡球板游戏改造&#xff09;-CSDN博客 基…

css之grid布局(网格布局)

简述&#xff1a; 网格布局顾名思义就是将元素呈现为网状的整齐布局 简单使用&#xff1a; <div><div class"test"><div class"item">1</div><div class"item">2</div><div class"item">…

开发一个免费的图表网站 Free Charts

Free Charts 项目背景 最近在使用图表网站时&#xff0c;发现许多都需要收费&#xff0c;因此萌生了自己做一个免费图表网站的想法。 不仅给自己做一个&#xff0c;也准备给大家做一个&#xff01; 项目历程 经过两三周的努力&#xff0c;完成了一个图表网站。以下是技术栈的…

Tomcat热加载和热部署

2. Tomcat热加载和热部署 在项目开发过程中&#xff0c;经常要改动Java/JSP 文件&#xff0c;但是又不想重新启动Tomcat&#xff0c;有两种方式:热加载和热部署。热部署表示重新部署应⽤&#xff0c;它的执行主体是Host。 热加载表示重新加载class&#xff0c;它的执行主体是C…

视频文件太大怎么变小?教你学会快速压缩

视频文件太大怎么变小&#xff1f;在数字时代&#xff0c;视频已成为我们日常生活中不可或缺的一部分&#xff0c;无论是工作汇报、学习资料、还是休闲娱乐&#xff0c;视频都扮演着重要角色。但高清视频往往占用了大量的存储空间&#xff0c;还可能在分享或上传时遇到诸多不便…

语雀:高效记录与整理编程学习笔记的最佳实践

目录 语雀&#xff1a;高效记录与整理编程学习笔记的最佳实践 一、编程学习笔记的要求与目的 二、记录编程学习笔记的目的 三、如何高效地记录与整理编程学习笔记 四、推荐平台&#xff1a;语雀 1、语雀的优势&#xff1a; 2、如何使用语雀整理编程学习笔记&#xff1a;…

【大模型系列篇】人工智能与智能计算的发展

&#x1f525;&#x1f525;&#x1f525; 来自 中国工程院院士、中国科学院计算技术研究所研究员 孙凝晖 第十四届全国人大常委会专题讲座上的讲稿《人工智能与智能计算的发展》 “把新一代人工智能作为推动科技跨越发展、 产业优化升级、生产力整体跃升的驱动力量&#xff0c…

项目启动端口报冲突如何处理?

你是否在Angular项目启动的时候试过端口报冲突呢&#xff1f;那么要如何解决呢&#xff1f;vue2又如何处理呢&#xff1f; 一、Angular冲突原因 Angular CLI 默认使用 4200 端口&#xff0c;如果这个端口已被占用&#xff08;比如启动了两次&#xff0c;或者本地可能有别的项目…

用IDEA创建一个SpringBoot项目和用官网创建一共SpringBoot项目导入IDEA(SpringBoot 学1)

一、用IDEA创建一个SpringBoot项目&#xff08;其实就说idea把网址集成到了软件上&#xff09; 1、选择java initializr创建项目框架 2、到下面这一步就和Maven项目的格式差不多&#xff0c;把项目名这些写上就行&#xff0c;选择对应的java版本 3、选择SpringBoot版本和勾选S…