Numpy: 对NumPy使用对齐的分配器?

创建于 2014-11-26  ·  68评论  ·  资料来源: numpy/numpy

关于NumPy 1.9中的f2py回归(在32位Windows上失败),问题是NumPy是否应开始使用能够保证对齐的分配器。

https://github.com/scipy/scipy/issues/4168

01 - Enhancement

最有用的评论

此功能对我很有帮助。 我使用的FPGA设备(Altera A10GX)中的DMA控制器要求使用64字节对齐的数据,这将我的代码速度提高了40倍(!!!)。 我怀疑@nachiket与我有同样的问题。 我写了一些类似于@eamartin的东西,但这有点hack。

所有68条评论

这是一个应该在所有平台上工作的分配器的示例。 它基于此是无耻的:

https://sites.google.com/site/ruslancray/lab/bookshelf/interview/ci/low-level/write-an-aligned-malloc-free-function

执行此操作的方法并不多,并且类似的代码在网络上不停地浮动,因此以这种方式进行扩展可能是可以的。 (此外,它不实现realloc。)

将此代码放入numpy/core/include/numpy/ndarraytypes.h应该确保新分配的ndarray在所有平台上都正确对齐。

这与平台无关的代码可能被替换为posix_memalign()对POSIX和_aligned_malloc()在Windows上。 但是,不可能将posix_memalign()realloc()组合在一起,所以最好自己实现。

#define NPY_MEMALIGN 32   /* 16 for SSE2, 32 for AVX, 64 for Xeon Phi */ 

static NPY_INLINE
void *PyArray_realloc(void *p, size_t n)
{
    void *p1, **p2, *base;
    size_t old_offs, offs = NPY_MEMALIGN - 1 + sizeof(void*);    
    if (NPY_UNLIKELY(p != NULL)) {
        base = *(((void**)p)-1);
        if (NPY_UNLIKELY((p1 = PyMem_Realloc(base,n+offs)) == NULL)) return NULL;
        if (NPY_LIKELY(p1 == base)) return p;
        p2 = (void**)(((Py_uintptr_t)(p1)+offs) & ~(NPY_MEMALIGN-1));
        old_offs = (size_t)((Py_uintptr_t)p - (Py_uintptr_t)base);
        memmove(p2,(char*)p1+old_offs,n);    
    } else {
        if (NPY_UNLIKELY((p1 = PyMem_Malloc(n + offs)) == NULL)) return NULL;
        p2 = (void**)(((Py_uintptr_t)(p1)+offs) & ~(NPY_MEMALIGN-1));   
    }
    *(p2-1) = p1;
    return (void*)p2;
}    

static NPY_INLINE
void *PyArray_malloc(size_t n)
{
    return PyArray_realloc(NULL, n);
}

static NPY_INLINE
void *PyArray_calloc(size_t n, size_t s)
{
    void *p;
    if (NPY_UNLIKELY((p = PyArray_realloc(NULL,n*s)) == NULL)) return NULL;
    memset(p, 0, n*s);
    return p;
}

static NPY_INLINE        
void PyArray_free(void *p)
{
    void *base = *(((void**)p)-1);
    PyMem_Free(base);
} 

我已经有一个分支,它添加了一个对齐的分配器,我将对其进行挖掘。

通过使用这样的东西,我们放弃了使用python tracemalloc框架和稀疏内存的选项(没有aligned_calloc)。
@njsmith您愿意在3.5发布之前再次与python开发人员合作,在其插槽中添加另一个分配器吗? 他们已经只为我们添加了calloc,如果我们现在不能使用它,那将是一场疯狂。

大概可以对齐PyMemAllocatorEx的上下文数据? 但是NumPy必须支持2.6及更高版本的Python版本,因此在Python 3.5中执行此操作可能无法解决问题。

我确实认为与3.5之前的python开发人员合作是一个好主意,
但我仍然不相信我们有充分的理由使用一致的
近期内的分配器。 struct {
double,double}实际上需要在win32或
SPARC,因为如果那是真的,那么什么都行不通。
2014年11月26日09:10,“朱利安·泰勒” [email protected]写道:

我已经有一个分支,它添加了一个对齐的分配器,我将对其进行挖掘。

通过使用这样的东西,我们放弃了使用python的选项
tracemalloc框架和稀疏内存(没有aligned_calloc)。
@njsmith https://github.com/njsmith您愿意参与吗
python devs再次在3.5之前将另一个分配器添加到其插槽
被释放? 他们已经只为我们添加了calloc,如果我们愿意的话
现在无法使用它。

-
直接回复此电子邮件或在GitHub上查看
https://github.com/numpy/numpy/issues/5312#issuecomment -64534021。

关于f2py的问题是Fortran需要什么对齐方式,而不是C的最低要求。速度也是一个问题。 如果数据正确对齐,则索引编制和SIMD都可以更好地工作。

使用对齐分配器的原因实际上可能是速度,并确保SSE / AVX
兼容性将消除因采取不同的方法而产生的数值抖动
不同对齐数据的代码路径。

f2py早于Fortran中的ISO C绑定标准及其版本
实质上,工作是连接Fortran的事实上的标准方法
与C,每个人都广泛使用。 根据这种经验,
显然,系统malloc提供的对齐对于
在实践中对我们很重要的Fortran编译器。

请注意,英特尔建议AVX使用32字节对齐: https :

@pitrou对于Xeon Phi,建议使用64字节对齐。 看一下我的代码示例中NPY_MEMALIGN定义背后的注释。

提供统一分配的主要复杂之处在于,我们可以
要么连接到tracemalloc基础架构,要么进行对齐分配,
并解决此问题需要与CPython上游进行一些协调(请参阅

4663)。

2014年12月5日16:44,“ Sturla Molden” [email protected]写道:

@pitrou https://github.com/pitrou对于Xeon,建议使用64位
披看一看NPY_MEMALIGN定义背后的评论。
我的代码示例。

-
直接回复此电子邮件或在GitHub上查看
https://github.com/numpy/numpy/issues/5312#issuecomment -65816753。

因此,CPython问题位于http://bugs.python.org/issue18835。

考虑到realloc()的复杂性,期望CPython在3.5个时间范围内解决此问题可能并不现实。 Numpy也许应该使用其自己的对齐分配包装器(该包装器应能够遵循PyMem API,并且无论如何都要利用tracemalloc)。

上面包括了这种分配器的代码。 我不明白@juliantaylor的论点,但他可能比我更了解这一点。

我可以理解他对愈伤组织的含义。 一个释放calloc不是简单的malloc和memset的一个零。 一个内存集将要求操作系统在需要它们之前获取页面。 AFAIK没有PyMem_Calloc。

实际上,CPython 3.5具有PyMem_Calloc和朋友。
我认为@juliantaylor正在考虑使用OS函数(posix_memalign等)的实现案例。 但这听起来没有必要。

顺便说一下

另一个想法是,对齐分配对于小型阵列可能是浪费的。 也许应该有一个阈值,低于该阈值使用标准分配?
另外,对齐方式应可配置吗?

在NumPy 1.9中,分配器称为PyArray_malloc和PyArray_free。 NumPy 1.10进行了很多更改。

你确定吗? PyArray_NewFromDescr_int()调用npy_alloc_cache(),npy_alloc_cache()调用PyDataMem_NEW()。

Numpy具有多个分配接口,但它们并不十分明显
名称。 PyArray_malloc / free用于“常规”分配(例如,对象
结构)。 数据缓冲区(ndarray->数据指针,内部的临时缓冲区
ufuncs等),但是是通过PyDataMem_NEW分配的。

2015年1月15日,星期四,7:48 PM,Sturla Molden [email protected]
写道:

在Numpy 1.9中,分配器称为PyArray_malloc和PyArray_free。

-
直接回复此电子邮件或在GitHub上查看
https://github.com/numpy/numpy/issues/5312#issuecomment -70149126。

纳撒尼尔·史密斯
博士后研究员-信息学-爱丁堡大学
http://vorpus.org

@njsmith是的,有一天我们应该合理化分配宏...我将从用来为ndarray(IIRC)分配维的那个开始。

我用补丁创建了PR#5457。 对该方法的反馈会很好。

据我所知,使用对齐方式目前没有任何好处
numpy中的分配器?

2015年1月16日星期五,晚上7:15,Antoine Pitrou [email protected]
写道:

我创建了PR#5457 https://github.com/numpy/numpy/pull/5457
补丁。 对该方法的反馈会很好。

-
直接回复此电子邮件或在GitHub上查看
https://github.com/numpy/numpy/issues/5312#issuecomment -70305997。

纳撒尼尔·史密斯
博士后研究员-信息学-爱丁堡大学
http://vorpus.org

通过Numba,我们确定AVX向量指令需要32字节对齐才能获得最佳性能。 如果您在启用AVX的情况下编译Numpy(我想需要特定的编译器选项),对齐方式也会有所作为。

出于好奇,您是否有任何真实的测量结果? 我问那里的b / c
有很多因素会影响这些因素(不同的开销/速度
不同数组大小的权衡,内存分配器的详细信息-
在不同的阵列大小上也表现不同-等等),我觉得很难
猜测是否最终会获得0.5%的端到端加速或50%的加速
端到端加速或什么。

2015年1月16日星期五,晚上8:16,Antoine Pitrou [email protected]
写道:

通过Numba,我们确定AVX向量指令需要32字节
对齐以获得最佳性能。 如果在启用AVX的情况下编译Numpy
(我猜需要特定的编译器选项),对齐方式应使
差异也一样。

-
直接回复此电子邮件或在GitHub上查看
https://github.com/numpy/numpy/issues/5312#issuecomment -70315434。

纳撒尼尔·史密斯
博士后研究员-信息学-爱丁堡大学
http://vorpus.org

在我的i5-4210u上,我发现在简单的负载添加存储测试中16字节和32字节对齐数据之间没有显着差异,最小周期计数似乎降低了5%,但中位数和第10个百分位数等于1%

是AVX吗?

@seibert使用Numpy数组使用

这是最新的Numba母带的基准测试:

http://nbviewer.ipython.org/gist/seibert/6957baddc067140e55fe

对于执行a + b * fabs(a)操作的float32阵列(大小= 10000),我们看到在3.70GHz(Ivy Bridge)的Intel Core i7-4820K CPU上运行时有40%的差异。

(我们为LLVM 3.5自动矢量化器没有生成任何循环剥离代码来校正对齐问题而感到困惑。如果gcc或clang对此进行校正,我不会感到惊讶。)

对于我们计划目标的其他体系结构(例如支持HSA标准的AMD APU),对齐需求更加严格,OpenCL开发人员建议我们对阵列进行256字节对齐以获得最佳性能。

我不知道40%的罚款是否过高,我认为i5 haswell的得分为零(授予的avx性能确实很差)
可能是jit编译器正在创建两个不同版本的循环吗?
您是否有此组装级别的配置文件(例如,通过性能记录)?

也可能是涡轮增压器在第二个循环中启动,禁用它或监视他是否适当的pmu可能很有趣

由于我们使用该系统进行基准测试,因此已在BIOS中禁用了TurboBoost。

JIT仅运行一次(一旦Numba为一组给定的输入类型编译一个函数,它将对其进行缓存),并且JIT在链接笔记本中由Cell 6进行基准测试之前被触发。

我以前没有使用过perf记录,但我会对其进行调查。 您是如何执行基准测试的? (实际上,如果不对齐导致可用的L2缓存带宽使用效率低下,我实际上希望Haswell比Ivy Bridge更好。)

英特尔的这份白皮书在第6页底部提供了有关对齐问题的更多信息:

https://software.intel.com/sites/default/files/m/d/4/1/d/8/Practical_Optimization_with_AVX.pdf

在未对齐的32字节向量上使用Intel AVX指令意味着,由于缓存行为64字节,因此每秒钟的加载将跨越缓存行拆分。 与使用16字节向量的Intel SSE代码相比,这使高速缓存行的拆分率提高了一倍。 内存密集型代码中的高速缓存行拆分率极有可能导致性能下降。 因此,强烈建议将数据对齐为32个字节,以用于Intel AVX。

Haswell具有两倍于Sandy / Ivy Bridge的L2缓存带宽,因此未对齐的阵列对Haswell的影响可能不大...

我的简单基准是这样的:
https://gist.github.com/juliantaylor/68e578d140f427ed80bb
在那个i7上看到会很有趣

Core i5-2500K(桑迪桥)上的结果:
4644 6656 7704 10100

请注意,我必须在基准测试开始时增加一个更长的预热阶段:

    for (i=0; i<10; i++)
        add(a, 1);

那是一个平均值/中位数/ ...? 看起来相当重要,似乎intel为haswell做了大量工作。

经过数百次循环运行后,它是基准测试的近乎稳定的输出。

我添加了打包的SSE2实现,以下是数字(i5-2500K):
4660 6492 7468 5108 10096
(32B对齐的AVX,16B(未对齐)的AVX,8B(未对齐)的AVX,对齐的SSE2,标量)

这是更新的源代码: https :

Core i5-4200U(便携式Haswell CPU)的输出几乎稳定:
4120 4152 4148 4260 7308

至少在这里,未对准的AVX​​并不比具有相似对准的SSE2差。

这是来自我的MBP(四核i7-3635QM,2.4 GHz,常春藤桥):
5060 4932 5820 5704 5040

我必须将avxintrin.h更改immintrin.h并使用Intel icc进行编译,因为gcc 4.8.1拒绝编译代码(clang也是如此)。

@sturlamolden ,icc是否有可能对标量循环进行矢量化处理(数字的最后结果)?

(请注意,如果添加基准涉及两个单独的输入数组,则基准测试可能会对缓存子系统施加更大的压力)

我不知道。

@sturlamolden该代码对于与某些SSE2连接非常有用。

我在Windows上遇到了一个问题(所有版本的VC,包括2015年)都不喜欢这一行

memmove((void*)p2,p1+old_offs,n);    

因为它们不支持对void*指针运算。 作为短期修复,我将其转换为char*以进行数学计算。 这可能是不对的-您是否有一个更好的主意,它将使它在Windows上正确编译?

我的错。 void *上的指针算术是非法的C。强制转换为char *是正确的。
更新了代码示例。

如何获取64字节对齐的numpy数组? 来自https://docs.scipy.org/doc/numpy-1.10.0/reference/generated/numpy.require.html的“已对齐”地址似乎以不同的长度对齐。 对齐长度是否有一些用户可配置的参数?

我制作了自己的(Python)对齐分配器,该分配器可用于Numpy提供的(未对齐)内存。

import numpy as np

def empty_aligned(n, align):
    """                                                                                                                                     
    Get n bytes of memory wih alignment align.                                                                                              
    """

    a = np.empty(n + (align - 1), dtype=np.uint8)
    data_align = a.ctypes.data % align
    offset = 0 if data_align == 0 else (align - data_align)
    return a[offset : offset + n]


def test(n):
    for i in xrange(1, 1024):
        b = empty_aligned(n, i)

        try:
            assert b.ctypes.data % i == 0
            assert b.size == n
        except AssertionError:
            print i, b.ctypes.data % i, b.size

也许像这样的Python解决方法是可行的解决方案?

@eamartin这是有关NumPy的内部C代码以及f2py生成的Fortran的接口代码(也是C代码)。 出于显而易见的原因,NumPy的实现不能依赖于NumPy。

不过,您可以在您的Python项目中使用该技巧。

@sturlamolden :它可以帮助@nachiket和其他类似情况的人。

@sturlamolden :我读得不够仔细,以至于没有意识到这是关于C内部的。

但是,在Python接口中提供更好的对齐方式选项对于在Numpy和对参数有对齐要求的本机库之间开发Python接口很有价值。

我不反对在Python接口中提供更好的对齐方式

我建议的“ Python对齐分配器”解决方案是黑客。 我认为在Python接口中提供对齐方式会很好,但是正确的方法是在C级别处理对齐方式。

此功能对我很有帮助。 我使用的FPGA设备(Altera A10GX)中的DMA控制器要求使用64字节对齐的数据,这将我的代码速度提高了40倍(!!!)。 我怀疑@nachiket与我有同样的问题。 我写了一些类似于@eamartin的东西,但这有点hack。

我绝对鼓励64字节对齐:

  1. 那就是缓存行的大小
  2. 它适用于高达AVX512的任何SIMD对齐

在这里,我们已经快5年了。

是否有对此标准(特别是64字节对齐)标准功能的想法?

现在,此cython代码位于NumPy中。 当然,这不会更改默认值。

我的2美分:与硬件设备和内核级调用接口时,对齐的分配器会有所帮助。 这些接口可能会受益于将缓冲区与页面对齐。

在合并randomgen时,我们获得了PyArray_realloc_aligned和朋友。 我们应该将这些例程移到numpy/core/include吗?

@mattip肯定会有用。 也可以从Python访问此功能吗?

在合并randomgen时,我们获得了PyArray_realloc_aligned和朋友。 我们应该将这些例程移到numpy/core/include吗?

撕下我的代码,是吗? 😂

从randomgen转向numpy的某个时刻可能迷路了。 一世
以为我有一个记录,证明这是你的。

在2019年11月17日星期日,10:29 Sturla Molden [email protected]写道:

看来我不是为所编写的代码编写日志的人
NumPy🧐:

https://github.com/numpy/numpy/blob/v1.17.2/numpy/_build_utils/src/apple_sgemv_fix.c

https://github.com/numpy/numpy/blob/v1.17.2/numpy/random/src/aligned_malloc/aligned_malloc.h

-
您收到此邮件是因为您发表了评论。
直接回复此电子邮件,在GitHub上查看
https://github.com/numpy/numpy/issues/5312?
或退订
https://github.com/notifications/unsubscribe-auth/ABKTSRM7IZIKPGKT4D2W4IDQUEMJJANCNFSM4AYDJQ4Q

代码的原始来源是什么?

该链接已失效,但它是从对齐的malloc改编而成的,如下所示:

https://tianrunhe.wordpress.com/2012/04/23/aligned-malloc-in-c/

这是来自Sturla的github帖子。 没有原始代码文件。

在星期日,2019年11月17日,12:04 Matti Picus [email protected]写道:

代码的原始来源是什么?

-
您收到此邮件是因为您发表了评论。
直接回复此电子邮件,在GitHub上查看
https://github.com/numpy/numpy/issues/5312?
或退订
https://github.com/notifications/unsubscribe-auth/ABKTSRIVMQFEJ5EP227PXL3QUEXNDANCNFSM4AYDJQ4Q

这是用于对齐的malloc。

星期日,2019年11月17日,12:14凯文·谢泼德
写道:

这是来自Sturla的github帖子。 没有原始代码文件。

在星期日,2019年11月17日,12:04 Matti Picus [email protected]写道:

代码的原始来源是什么?

-
您收到此邮件是因为您发表了评论。
直接回复此电子邮件,在GitHub上查看
https://github.com/numpy/numpy/issues/5312?
或退订
https://github.com/notifications/unsubscribe-auth/ABKTSRIVMQFEJ5EP227PXL3QUEXNDANCNFSM4AYDJQ4Q

向Numpy贡献50行代码的每个人都获得专用的版权标头吗? 我可能会仔细检查一下,看是否有必要:-)

看来我不再是为NumPy written写的代码的贡献者:

您现在将永远是:)

向Numpy贡献50行代码的每个人都获得专用的版权标头吗? 我可能会仔细检查一下,看是否有必要:-)

不。 我们试图避免在源代码中对这些东西进行编码,因为这总是非常不完整且难以维护。 我们确实要求人们在THANKS.txt列出自己; 我正在寻找一种更好的替代方法,因为该文件通常会产生合并冲突。

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

kevinzhai80 picture kevinzhai80  ·  4评论

inducer picture inducer  ·  3评论

dmvianna picture dmvianna  ·  4评论

manuels picture manuels  ·  3评论

qualiaa picture qualiaa  ·  3评论