dan*_*man 6 c python assembly sse numpy
所以,如果我有类似的东西x=np.random.rand(60000)*400-200.iPython %timeit说:
x.astype(int) 需要0.14msnp.rint(x)并np.around(x)采取1.01ms需要注意的是,在rint和around情况下,你仍然需要花费额外的0.14ms做最后的astype(int)(假设这是你最终想要的东西).
问题:我认为大多数现代硬件都能够在同等时间内完成两项操作.如果是这样的话,为什么numpy需要花费8倍的时间来进行舍入?
碰巧我对算术的准确性并不十分挑剔,但我看不出如何利用numpy的优势(我正在做杂乱的生物学而不是粒子物理学).
np.around(x).astype(int)并且x.astype(int)不会产生相同的值.前者甚至(它是相同的((x*x>=0+0.5) + (x*x<0-0.5)).astype(int)),而后者则向零舍入.然而,
y = np.trunc(x).astype(int)
z = x.astype(int)
Run Code Online (Sandbox Code Playgroud)
显示y==z但计算y速度要慢得多.所以,它是np.trunc和np.around它是缓慢的功能.
In [165]: x.dtype
Out[165]: dtype('float64')
In [168]: y.dtype
Out[168]: dtype('int64')
Run Code Online (Sandbox Code Playgroud)
因此np.trunc(x)从双倍到双倍向零舍入.然后astype(int)必须将double转换为int64.
在内部我不知道python或numpy正在做什么,但我知道我将如何在C中执行此操作.让我们讨论一些硬件.使用SSE4.1,可以使用以下方法执行从double到double的round,floor,ceil和trunc:
_mm_round_pd(a, 0); //round: round even
_mm_round_pd(a, 1); //floor: round towards minus infinity
_mm_round_pd(a, 2); //ceil: round towards positive infinity
_mm_round_pd(a, 3); //trunc: round towards zero
Run Code Online (Sandbox Code Playgroud)
但numpy需要支持没有SSE4.1的系统,所以它必须在没有SSE4.1以及SSE4.1的情况下构建,然后使用调度程序.
但是直到使用SSE/AVX从双直接到int64这样做在AVX512之前效率不高.但是,只使用SSE2可以有效地将double舍入到int32:
_mm_cvtpd_epi32(a); //round double to int32 then expand to int64
_mm_cvttpd_epi32(a); //trunc double to int32 then expand to int64
Run Code Online (Sandbox Code Playgroud)
这些将两个双精度转换为两个int64.
在你的情况下,这将工作正常,因为范围肯定在int32内.但除非python知道范围适合int32,否则它不能假设这样,所以它必须舍入或截断到int64,这是缓慢的.此外,无论如何,numpy必须构建以支持SSE2来执行此操作.
但也许您可以使用单个浮点数组开始.在那种情况下你可以做到:
_mm_cvtps_epi32(a); //round single to int32
_mm_cvttps_epi32(a) //trunc single to int32
Run Code Online (Sandbox Code Playgroud)
这些将四个单一转换为四个int32.
因此,为了回答您的问题,SSE2可以有效地从double舍入或截断为int32.AVX512也可以使用_mm512_cvtpd_epi64(a)或者有效地从double到int64进行舍入或截断_mm512_cvttpd_epi64(a).SSE4.1可以从浮动到浮动或圆形/截断/地板/细胞有效地加倍.
正如 @jme 在评论中指出的,rint和around函数必须确定是否将分数向上或向下舍入到最接近的整数。相反,该astype函数将始终向下舍入,因此它可以立即丢弃小数信息。还有许多其他函数可以做同样的事情。此外,您可以通过使用较少的整数位数来提高速度。但是,您必须小心,确保可以容纳全部输入数据。
%%timeit\nnp.int8(x)\n10000 loops, best of 3: 165 \xc2\xb5s per loop\nRun Code Online (Sandbox Code Playgroud)\n\n请注意,这不会存储 -128 到 127 范围之外的值,因为它是 8 位。您的示例中的某些值超出了此范围。
\n\n在我尝试过的所有其他方法中,np.intc似乎是最快的:
%%timeit\nnp.int16(x)\n10000 loops, best of 3: 186 \xc2\xb5s per loop\n\n%%timeit\nnp.intc(x)\n10000 loops, best of 3: 169 \xc2\xb5s per loop\n\n%%timeit\nnp.int0(x)\n10000 loops, best of 3: 170 \xc2\xb5s per loop\n\n%%timeit\nnp.int_(x)\n10000 loops, best of 3: 188 \xc2\xb5s per loop\n\n%%timeit\nnp.int32(x)\n10000 loops, best of 3: 187 \xc2\xb5s per loop\n\n%%timeit\n np.trunc(x)\n1000 loops, best of 3: 940 \xc2\xb5s per loop\nRun Code Online (Sandbox Code Playgroud)\n\n你的例子,在我的机器上:
\n\n%%timeit\nnp.around(x)\n1000 loops, best of 3: 1.48 ms per loop\n\n%%timeit\nnp.rint(x)\n1000 loops, best of 3: 1.49 ms per loop\n\n%%timeit\nx.astype(int)\n10000 loops, best of 3: 188 \xc2\xb5s per loop\nRun Code Online (Sandbox Code Playgroud)\n