草庐IT

python - 了解 Numpy 中的矢量化与通过 Numexpr 进行矢量化表达式的多线程之间的区别

coder 2023-08-24 原文

我对 NumPy 据说是对其算术数组操作进行矢量化的概念感到有点挣扎:它是否克服了 Python 的 GIL,因为 NumPy 的一部分是用 C 实现的?另外,Numexpr 是如何工作的呢?如果我理解正确的话,它通过优化的 JIT 运行代码并启用多线程,从而克服了 Python 的 GIL。

“真正的”矢量化不是更像是多进程而不是多线程吗?

最佳答案

在某些情况下,NumPy 可能会使用一个库,该库使用多个进程来进行处理,从而将负担分散到多个内核上。然而,这取决于库,与 NumPy 中的 python 代码没有太大关系。所以,是的,如果不是用 python 编写的,NumPy 和任何其他库都可以克服这些限制。甚至有一些库提供 GPU 加速功能。

NumExpr 使用相同的方法来绕过 GIL。从他们的主页:

此外,numexpr 直接在其内部虚拟机中实现了对多线程计算的支持,该虚拟机是用 C 语言编写的。这允许绕过 Python 中的 GIL

但是,NumPy 和 NumExpr 之间存在一些根本差异。 Numpy 专注于为数组操作创建一个良好的 Pythonic 接口(interface),NumExpr 具有更窄的范围和自己的语言。当 NumPy 执行操作数为数组的计算 c = 3*a + 4*b 时,会在过程中创建两个临时数组(3*a4 *b).在这种情况下,NumExpr 可能会优化计算,以便在不使用任何中间结果的情况下逐个元素地执行乘法和加法。

这会导致 NumPy 发生一些有趣的事情。下面的测试是在4核8线程的i7处理器上进行的,使用iPython的%timeit进行了计时:

import numpy as np
import numexpr as ne

def addtest_np(a, b): a + b
def addtest_ne(a, b): ne.evaluate("a+b")

def addtest_np_inplace(a, b): a += b
def addtest_ne_inplace(a, b): ne.evaluate("a+b", out=a)

def addtest_np_constant(a): a + 3
def addtest_ne_constant(a): ne.evaluate("a+3")

def addtest_np_constant_inplace(a): a += 3
def addtest_ne_constant_inplace(a): ne.evaluate("a+3", out=a)

a_small = np.random.random((100,10))
b_small = np.random.random((100,10))

a_large = np.random.random((100000, 1000))
b_large = np.random.random((100000, 1000))

# results: (time given is in nanoseconds per element with small/large array)
# np: NumPy
# ne8: NumExpr with 8 threads
# ne1: NumExpr with 1 thread
#
# a+b:
#  np: 2.25 / 4.01
#  ne8: 22.6 / 3.22
#  ne1: 22.6 / 4.21
# a += b:
#  np: 1.5 / 1.26 
#  ne8: 36.8 / 1.18
#  ne1: 36.8 / 1.48

# a+3:
#  np: 4.8 / 3.62
#  ne8: 10.9 / 3.09
#  ne1: 20.2 / 4.04
# a += 3:
#  np: 3.6 / 0.79
#  ne8: 34.9 / 0.81
#  ne1: 34.4 / 1.06

当然,对于所使用的计时方法来说,这不是很准确,但是有一定的总体趋势:

  • NumPy 使用更少的 cloc 周期 (np <>
  • 并行性对非常大的数组 (10-20 %) 有一点帮助
  • NumExpr 对于小数组要慢得多
  • NumPy 在就地操作方面非常强大

NumPy 并没有让简单的算术运算并行化,但是从上面可以看出,这其实无关紧要。速度主要受内存带宽限制,而不是处理能力。

如果我们做一些更复杂的事情,事情就会改变。

np.sin(a_large)               # 19.4 ns/element
ne.evaluate("sin(a_large)")   # 5.5 ns/element

速度不再受内存带宽限制。要查看这是否真的是由于线程(而不是由于 NumExpr 有时使用一些快速库):

ne.set_num_threads(1)
ne.evaluate("sin(a_large)")    # 34.3 ns/element

在这里,并行性真的很有帮助。

NumPy 可以使用并行处理更复杂的线性运算,例如矩阵求逆。 NumExpr 不支持这些操作,所以没有有意义的比较。实际速度取决于所使用的库 (BLAS/Atlas/LAPACK)。此外,在执行 FFT 等复杂运算时,性能取决于库。 (据我所知,NumPy/SciPy 还没有 fftw 支持。)

总而言之,似乎在某些情况下 NumExpr 非常快速且有用。然后在某些情况下 NumPy 是最快的。如果你有愤怒的数组和逐元素操作,NumExpr 非常强大。然而,应该注意的是,一些并行性(甚至跨计算机传播计算)通常很容易通过 multiprocessing 或类似的东西合并到代码中。


关于“多处理”和“多线程”的问题有点棘手,因为术语有点不稳定。在python中“线程”是运行在同一个GIL下的东西,但是如果我们谈论操作系统线程和进程,两者之间可能没有任何区别。例如,在 Linux 中,两者之间没有区别。

关于python - 了解 Numpy 中的矢量化与通过 Numexpr 进行矢量化表达式的多线程之间的区别,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24498178/

有关python - 了解 Numpy 中的矢量化与通过 Numexpr 进行矢量化表达式的多线程之间的区别的更多相关文章

  1. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  2. ruby-on-rails - Ruby on Rails : . 常量化 : wrong constant name error? - 2

    我正在使用这个:4.times{|i|assert_not_equal("content#{i+2}".constantize,object.first_content)}我之前声明过局部变量content1content2content3content4content5我得到的错误NameError:wrongconstantnamecontent2这个错误是什么意思?我很确定我想要content2=\ 最佳答案 你必须用一个大字母来调用ruby​​常量:Content2而不是content2。Aconstantnamestart

  3. ruby - RuntimeError(自动加载常量 Apps 多线程时检测到循环依赖 - 2

    我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("

  4. Python 相当于 Perl/Ruby ||= - 2

    这个问题在这里已经有了答案:关闭10年前。PossibleDuplicate:Pythonconditionalassignmentoperator对于这样一个简单的问题表示歉意,但是谷歌搜索||=并不是很有帮助;)Python中是否有与Ruby和Perl中的||=语句等效的语句?例如:foo="hey"foo||="what"#assignfooifit'sundefined#fooisstill"hey"bar||="yeah"#baris"yeah"另外,类似这样的东西的通用术语是什么?条件分配是我的第一个猜测,但Wikipediapage跟我想的不太一样。

  5. java - 什么相当于 ruby​​ 的 rack 或 python 的 Java wsgi? - 2

    什么是ruby​​的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht

  6. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  7. python - 如何读取 MIDI 文件、更改其乐器并将其写回? - 2

    我想解析一个已经存在的.mid文件,改变它的乐器,例如从“acousticgrandpiano”到“violin”,然后将它保存回去或作为另一个.mid文件。根据我在文档中看到的内容,该乐器通过program_change或patch_change指令进行了更改,但我找不到任何在已经存在的MIDI文件中执行此操作的库.他们似乎都只支持从头开始创建的MIDI文件。 最佳答案 MIDIpackage会为您完成此操作,但具体方法取决于midi文件的原始内容。一个MIDI文件由一个或多个音轨组成,每个音轨是十六个channel中任何一个上的

  8. 「Python|Selenium|场景案例」如何定位iframe中的元素? - 2

    本文主要介绍在使用Selenium进行自动化测试或者任务时,对于使用了iframe的页面,如何定位iframe中的元素文章目录场景描述解决方案具体代码场景描述当我们在使用Selenium进行自动化测试的时候,可能会遇到一些界面或者窗体是使用HTML的iframe标签进行承载的。对于iframe中的标签,如果直接查找是无法找到的,会抛出没有找到元素的异常。比如近在咫尺的例子就是,CSDN的登录窗体就是使用的iframe,大家可以尝试通过F12开发者模式查看到的tag_name,class_name,id或者xpath来定位中的页面元素,会抛出NoSuchElementException异常。解决

  9. python ffmpeg 使用 pyav 转换 一组图像 到 视频 - 2

    2022/8/4更新支持加入水印水印必须包含透明图像,并且水印图像大小要等于原图像的大小pythonconvert_image_to_video.py-f30-mwatermark.pngim_dirout.mkv2022/6/21更新让命令行参数更加易用新的命令行使用方法pythonconvert_image_to_video.py-f30im_dirout.mkvFFMPEG命令行转换一组JPG图像到视频时,是将这组图像视为MJPG流。我需要转换一组PNG图像到视频,FFMPEG就不认了。pyav内置了ffmpeg库,不需要系统带有ffmpeg工具因此我使用ffmpeg的python包装p

  10. ruby - 如何让Ruby捕获线程中的语法错误 - 2

    我正在尝试使用ruby​​编写一个双线程客户端,一个线程从套接字读取数据并将其打印出来,另一个线程读取本地数据并将其发送到远程服务器。我发现的问题是Ruby似乎无法捕获线程内的错误,这是一个示例:#!/usr/bin/rubyThread.new{loop{$stdout.puts"hi"abc.putsefsleep1}}loop{sleep1}显然,如果我在线程外键入abc.putsef,代码将永远不会运行,因为Ruby将报告“undefinedvariableabc”。但是,如果它在一个线程内,则没有错误报告。我的问题是,如何让Ruby捕获这样的错误?或者至少,报告线程中的错误?

随机推荐