0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Numbaでの配列の定義時間

Posted at

Numbaでの配列の定義時間

他記事でNumbaでの配列の定義時間が気になったので、測定しました。

測定

確認用コード(折りたたみ)
import matplotlib.pyplot as plt
from numba import njit
import numpy as np
import timeit

@njit
def make_arr_float64(loop,arr_len):
    for _ in range(loop):
        #s = np.empty(arr_len,dtype="float64")
        s = np.ones(arr_len,dtype="float64")
    return s
@njit
def make_arr_int8(loop,arr_len):
    for _ in range(loop):
        #s = np.empty(arr_len,dtype="int8")
        s = np.ones(arr_len,dtype="float64")
    return s
@njit
def make_arr_int64(loop,arr_len):
    for _ in range(loop):
        #s = np.empty(arr_len,dtype="int64")
        s = np.ones(arr_len,dtype="int64")
    return s
@njit
def make_arr_bool(loop,arr_len):
    for _ in range(loop):
        #s = np.empty(arr_len,dtype="bool")
        s = np.ones(arr_len,dtype="bool")
    return s
@njit
def make_arr_float64_assignment(loop,arr_len):
    for _ in range(loop):
        s = np.ones(arr_len,dtype="float64")
        for i in range(arr_len):
            s[i] = 0
    return s
@njit
def make_arr_int8_assignment(loop,arr_len):
    for _ in range(loop):
        #s = np.empty(arr_len,dtype="int8")
        s = np.ones(arr_len,dtype="float64")
        for i in range(arr_len):
            s[i] = 0
    return s
@njit
def make_arr_int64_assignment(loop,arr_len):
    for _ in range(loop):
        #s = np.empty(arr_len,dtype="int64")
        s = np.ones(arr_len,dtype="int64")
        for i in range(arr_len):
            s[i] = 0
    return s
@njit
def make_arr_bool_assignment(loop,arr_len):
    for _ in range(loop):
        #s = np.empty(arr_len,dtype="bool")
        s = np.ones(arr_len,dtype="bool")
        for i in range(arr_len):
            s[i] = False
    return s

def timer_for_make_arr(loop, arr_len,func):
    func(1,arr_len)
    t = timeit.timeit(lambda: func(loop,arr_len), number=1)/loop
    return t

def ploter_for_make_arr():
    loop = 100
    x = 2**np.arange(28)
    plt.xscale('log')
    plt.yscale('log')
    plt.xlabel("arr_len")
    plt.ylabel("time (s)")
    y = np.array([timer_for_make_arr(loop, arr_len,make_arr_float64) for arr_len in x])
    plt.plot(x,y,label="make_arr_float64")
    print("make_arr_float64 : Done")
    y = np.array([timer_for_make_arr(loop, arr_len,make_arr_int64) for arr_len in x])
    plt.plot(x,y,label="make_arr_int64")
    print("make_arr_int64 : Done")
    y = np.array([timer_for_make_arr(loop, arr_len,make_arr_int8) for arr_len in x])
    plt.plot(x,y,label="make_arr_int8")
    print("make_arr_int8 : Done")
    y = np.array([timer_for_make_arr(loop, arr_len,make_arr_bool) for arr_len in x])
    plt.plot(x,y,label="make_arr_bool")
    print("make_arr_bool : Done")
    y = np.array([timer_for_make_arr(loop, arr_len,make_arr_float64_assignment) for arr_len in x])
    plt.plot(x,y,label="make_arr_float64_assignment")
    print("make_arr_float64_assignment : Done")
    y = np.array([timer_for_make_arr(loop, arr_len,make_arr_int64_assignment) for arr_len in x])
    plt.plot(x,y,label="make_arr_int64_assignment")
    print("make_arr_int64_assignment : Done")
    y = np.array([timer_for_make_arr(loop, arr_len,make_arr_int8_assignment) for arr_len in x])
    plt.plot(x,y,label="make_arr_int8_assignment")
    print("make_arr_int8_assignment : Done")
    y = np.array([timer_for_make_arr(loop, arr_len,make_arr_bool_assignment) for arr_len in x])
    plt.plot(x,y,label="make_arr_bool_assignment")
    print("make_arr_bool_assignment : Done")
    plt.legend()
    plt.savefig("qiita_numba_make_arr_ones.png", format="png", dpi=400)
    plt.show()
ploter_for_make_arr()

結果

np.emptyを使用

loop = 100000で実行

image

float64,int64とint8,boolの2種に分かれていてちょうどarr_lenを8だけスライドすると重なる。
numpyはboolがなぜか1bitではなく1byteなので、np.emptyにかかる時間はデータサイズごとに決まっていそう。
int8のグラフはarr_lenがそのままbytesに変わるので、2**20 Byteを超えると途端に時間がかかっている
大体1メガバイト、うーんなんかあるんですかね。
ちなみに使っているSoc M1の高性能コアは、「192KBのL1命令キャッシュと128KBのL1データキャッシュ、12MBのL2キャッシュは高性能コア内で共有」だそう。関係あるかは不明

np.onesを使用

loop = 100で実行。x = 2**np.arange(28)までに

image

今回は、onesを1回するだけのものと、onesを0埋めするもの(assignment)の二つをとった。
np.onesは最初に1埋めするため、埋める時間がどんなもんなのかということをやっている。
結果見ての通り埋める時間はたいしたことない。
boolのみこれまた8ほどズレている。今回int8はズレていない。np.onesではbool有利か。

1kBくらいまでの配列は長さ最小の配列と同じくらいの時間を食う

image

次に、emptyを1埋めするもの(assignment)との比較。
今度は(見にくいが)int8のempty1埋めがboolと同じになっている。
あんまりint8使う人いないだろうから最適化されてないのかな。

考察

int8のグラフはarr_lenがそのままbytesに変わるのでその感じで読む。
ただ配列を定義するだけなら、emptyの方がonesより大幅にはやい。
配列の定義にかかる時間に比べて代入にかかる時間はたいしたことない。
が、emptyの1埋めはonesと同じくらい。
ので、emptyはほぼなんもしていないのでは?と思う。

配列の定義時間を測りたいときにはemptyではダメなのかなと思う。

そして、1kBくらいまでの配列は長さ最小の配列と同じくらいの時間を食う
これを以下のどちらで受け止めればいいかはまだよくわからない。

  • 小さい配列を複数定義する場合はなるべくまとめろ
  • 小さい配列を作る時間は1kBくらいまでたいしたことないからシビアにならなくていい

どっちかは処理にもよるだろうと思う。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?