簡體   English   中英

與astype(int)相比,numpy / / rint變慢

[英]numpy around/rint slow compared to astype(int)

所以,如果我有像x=np.random.rand(60000)*400-200 iPython的%timeit說:

  • x.astype(int)需要0.14ms
  • np.rint(x)np.around(x)需要1.01ms

請注意,在rintaround情況下,您仍然需要花費額外的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) )相同,而后者則向零((x*x>=0+0.5) + (x*x<0-0.5)).astype(int) 然而,

y = np.trunc(x).astype(int)
z = x.astype(int)

顯示y==z但計算y要慢得多。 所以這是np.truncnp.around函數很慢。

In [165]: x.dtype
Out[165]: dtype('float64')
In [168]: y.dtype
Out[168]: dtype('int64')

所以np.trunc(x)從double到double向零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

但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

這些將兩個雙精度轉換為兩個int64。

在你的情況下,這將工作正常,因為范圍肯定在int32內。 但除非python知道范圍適合int32,否則它不能假設這樣,所以它必須舍入或截斷到int64,這是緩慢的。 此外,無論如何,numpy必須構建以支持SSE2來執行此操作。

但也許您可以使用單個浮點數組開始。 在那種情況下你可以做到:

_mm_cvtps_epi32(a); //round single to int32
_mm_cvttps_epi32(a) //trunc single to int32

這些將四個單一轉換為四個int32。

因此,為了回答您的問題,SSE2可以有效地從double舍入或截斷為int32。 使用_mm512_cvtpd_epi64(a)_mm512_cvttpd_epi64(a) AVX512也能夠有效地從double到int64進行舍入或截斷。 SSE4.1可以將float / trunc / floor / ceil從float轉為float或者double或double to double。

正如@jme在注釋中指出的那樣, rintaround函數必須確定是否將分數向上或向下舍入到最接近的整數。 相反, astype函數將始終向下舍入,因此它可以立即丟棄小數信息。 還有許多其他功能可以做同樣的事情。 此外,您可以通過使用較低的整數位來提高速度。 但是,您必須小心,您可以容納所有輸入數據。

%%timeit
np.int8(x)
10000 loops, best of 3: 165 µs per loop

注意,這不會存儲-128到127范圍之外的值,因為它是8位。 示例中的某些值超出此范圍。

在我試過的所有其他np.intcnp.intc似乎是最快的:

%%timeit
np.int16(x)
10000 loops, best of 3: 186 µs per loop

%%timeit
np.intc(x)
10000 loops, best of 3: 169 µs per loop

%%timeit
np.int0(x)
10000 loops, best of 3: 170 µs per loop

%%timeit
np.int_(x)
10000 loops, best of 3: 188 µs per loop

%%timeit
np.int32(x)
10000 loops, best of 3: 187 µs per loop

%%timeit
    np.trunc(x)
1000 loops, best of 3: 940 µs per loop

你的例子,在我的機器上:

%%timeit
np.around(x)
1000 loops, best of 3: 1.48 ms per loop

%%timeit
np.rint(x)
1000 loops, best of 3: 1.49 ms per loop

%%timeit
x.astype(int)
10000 loops, best of 3: 188 µs per loop

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM