差分

このページの2つのバージョン間の差分を表示します。

この比較画面へのリンク

両方とも前のリビジョン前のリビジョン
次のリビジョン
前のリビジョン
programming:python:packages:numba [2020/07/01] ikatakosprogramming:python:packages:numba [2023/10/03] (現在) ikatakos
行 13: 行 13:
  
   * PyPy   * PyPy
-    * ほぼ書き換えなしでそれなりに高速化される (( SciPyなどの外部モジュールや、一部標準モジュールは未対応のものがある ))+    * 対応している機能が多い 
 +      * 標準ライブラリの処理はほぼ書き換えなしでそれなりに高速化される 
 +      * Numpy, Scipyなどの外部ライブラリも、一部はPyPy用が提供されている((要インストール。どこまでちゃんと動くのかは未確認))
     * Pythonとは別の実行環境としてインストールが必要     * Pythonとは別の実行環境としてインストールが必要
 +      * 最新のPythonの機能がPyPyに取り込まれるまで、若干のタイムラグはある
     * 基本的に実行時コンパイル(JIT)     * 基本的に実行時コンパイル(JIT)
       * なので、実行時間に常にコンパイル時間が含まれる       * なので、実行時間に常にコンパイル時間が含まれる
行 27: 行 30:
     * 効果は高い(勿論、書き方や得意分野による)     * 効果は高い(勿論、書き方や得意分野による)
  
-速度比較は、参考文献の1つめが詳しい。+速度比較は、以下の参考文献のyniji氏の記事が詳しい。
  
 PyPyの方が、労力が少なく効果がそこそこでコスパが良い感はある。 PyPyの方が、労力が少なく効果がそこそこでコスパが良い感はある。
  
-Numbaは、後述のNoPythonモードによって高速化できないコードをエラーにすることが出来るので、着実に高速化させやすい。+Numbaは、後述のNoPythonモードによって高速化できないコードをエラーにできるので、着実に高速化させやすい。
  
-また、既存コードに未対応の外部モジュールを使っていると、PyPyなら丸ごと実行できなくなる一方 ((何かしら方法はあるかも))、Numbaでは関数を切り分けて、使っていない部分だけコンパイルさせることができる。+また、未対応の外部モジュールを使っていると、PyPyなら丸ごと実行できなくなる一方、Numbaでは関数を切り分けて、使っていない部分だけコンパイルさせることができる。 
 +((PyPyでも、モジュール不使用の部分だけファイルを切り分けて、本流のPythonからsubprocessで実行するなど可能っちゃ可能))
  
  
 ===== 参考文献 ===== ===== 参考文献 =====
  
 +  * 公式リファレンス
 +    * [[https://numba.pydata.org/numba-doc/dev/reference/index.html|Reference Manual]]
 +      * [[https://numba.pydata.org/numba-doc/dev/reference/pysupported.html|Supported Python features]]
 +      * [[https://numba.pydata.org/numba-doc/dev/reference/numpysupported.html|Supported NumPy features]]
   * yniji氏のQiita記事   * yniji氏のQiita記事
     * [[https://qiita.com/yniji/items/d012bb9f938e0445a3ff|AtCoderで Python を高速化する Numpy + Numba を使う - Qiita]]     * [[https://qiita.com/yniji/items/d012bb9f938e0445a3ff|AtCoderで Python を高速化する Numpy + Numba を使う - Qiita]]
行 54: 行 62:
 高速化する上で重要な概念。 高速化する上で重要な概念。
  
-基本的にNumbaは関数のデコレータに「''@jit''」を付けるだけで実行時コンパイラが動くが、どんな型でもちゃんとコンパイルできるわけではない。+基本的にNumbaは関数のデコレータに「''@jit''」を付けるだけで実行時コンパイラが動くが、まだ発展途上であり、どんな型でもちゃんとコンパイルできるわけではない。
 未対応の関数やデータ型が含まれる場合、「Objectモード」でコンパイルされる。Objectモードだと、高速化の効果は少ない。 未対応の関数やデータ型が含まれる場合、「Objectモード」でコンパイルされる。Objectモードだと、高速化の効果は少ない。
  
行 60: 行 68:
  
   * リファレンス   * リファレンス
-    * [[https://numba.pydata.org/numba-doc/dev/reference/pysupported.html|Supported Python features — Numba 0.50.0.dev0+236.g64fbf2b-py3.7-linux-x86_64.egg documentation]]+    * [[https://numba.pydata.org/numba-doc/dev/reference/pysupported.html|Supported Python features]]
  
-そうでなく、全てがNumbaが対応する機能のみで記述されていれば、「NoPythonモード」となり、高速化の効果が大きくなる。+全てがNumbaが対応する機能のみで記述されていれば、「NoPythonモード」となり、高速化の効果が大きくなる。
  
 デコレータとして ''@njit'' を使うと、NoPythonモードを強制し、未対応の型があると警告やエラーを出してくれる。 デコレータとして ''@njit'' を使うと、NoPythonモードを強制し、未対応の型があると警告やエラーを出してくれる。
 一方、''@jit'' だとObjectモードでも許容する。 一方、''@jit'' だとObjectモードでも許容する。
  
-既存コードに手を加えたくない場合は ''@jit''、高速化目的なら ''@njit'' を使うとよい。(ただ、Objectモードはそのうちdeprecatedになるかも?)+<sxh python> 
 +from numba import jitnjit
  
-  NoPythonモードで未対応の型(PythonのList)を使用した際の警告例 +# Numba非対応の機能 
-   +from collections import defaultdict 
-  NumbaPendingDeprecationWarning:  + 
-  Encountered the use of a type that is scheduled for deprecation: type 'reflected listfound for argument 'a' of function 'function'.+@jit 
 +def func1()
 +    d = defaultdict(int) 
 +    return d 
 + 
 +print(func1()) 
 + 
 +# => 
 +# NumbaWarning:  
 +# Compilation is falling back to object mode WITH looplifting enabled 
 +# because Function "func1" failed type inference due to: 
 +# Untyped global name 'defaultdict': cannot determine Numba type of <class 'type'
 +# ... 
 +# NumbaWarning: Function "func1" was compiled in object mode without forceobj=True. 
 +#  
 +# defaultdict(<class 'int'>, {}) 
 +#  
 +# 警告は出るが実行はされる 
 + 
 + 
 +@njit 
 +def func2(): 
 +    d = defaultdict(int) 
 +    return d 
 + 
 +print(func2()) 
 + 
 +# => 
 +# numba.core.errors.TypingError: Failed in nopython mode pipeline (step: nopython frontend) 
 +# Untyped global name 'defaultdict': cannot determine Numba type of <class 'type'
 +
 +# エラー終了 
 + 
 +</sxh> 
 + 
 + 
 +既存コードに手を加えたくない場合は ''@jit''、高速化目的なら ''@njit'' を使うとよい。 
 + 
 +ただ、''@jit'' の「NoPythonモードでのコンパイルに失敗した場合、自動的にObjectモードでコンパイル」という挙動はDeprecatedであり、そのうち ''@jit''でも明示的にobjectモードを許容しない限りはエラーになるかもしれない。 
 + 
 +  * [[https://numba.pydata.org/numba-doc/latest/reference/deprecation.html#deprecation-of-object-mode-fall-back-behaviour-when-using-jit|Deprecation of object mode fall-back behaviour when using @jit]]
  
 以下、基本的にNoPythonモードを満たすように記述することを目標とする。 以下、基本的にNoPythonモードを満たすように記述することを目標とする。
行 81: 行 130:
 数値型とnumpy.ndarrayしか使えないくらいに思っておいてよい。 数値型とnumpy.ndarrayしか使えないくらいに思っておいてよい。
  
-一応SetやDictも使える。(ただしnumpy.ndarray等と比較して動作は速くな+一応SetやDictも使える。(ただし化の恩恵は若干少なくな
  
 引数や戻り値のような、Numba関数の中と外の橋渡し的な役割をする変数は、特に留意が必要となる。 引数や戻り値のような、Numba関数の中と外の橋渡し的な役割をする変数は、特に留意が必要となる。
行 88: 行 137:
 ==== 引数・戻り値に使える主な型 ==== ==== 引数・戻り値に使える主な型 ====
  
-  * 数値(byte, int, float, complex) +  * 数値(byte, int, float, complex) 
-  * numpy.ndarray +  * numpy.ndarray 
-  * UniTuple, Tuple +  * UniTuple, Tuple(※) 
-  * (*1) list, set(reflected list, reflected set)+  * (*1) 通常のlist, set(reflected list, reflected set)(※) 
 +  * (*2) Numba独自のList, Dict(typed.List, typed.Dict)(※
   * (*2) Unicode文字列(Python3における通常の文字列)   * (*2) Unicode文字列(Python3における通常の文字列)
-  * (*2) numba.typed.List, numba.typed.Dict +  * コンパイル済み関数(※)
-  * コンパイル済み関数 +
-    * 引数としてのみ使用可+
  
-その他もあるかも知れないけど、どこ見れば書いてあるのかよくわかってない。 +その他もあるかも知れない。
-なるべく上の3つのみを使うようにした方がよさそう。(個々の問題点は後述)+
  
-(*1)はdeprecatedで今後使えなくなる可能性高い+るべ★を付けた3つのみを使うようにした方よさそう
  
 +(*1)はdeprecatedで今後使えなくなる可能性が高い。\\
 (*2)は、対応はしているが、まだ十分高速に動くようコンパイル出来ない場合があるとリファレンスに書かれている。 (*2)は、対応はしているが、まだ十分高速に動くようコンパイル出来ない場合があるとリファレンスに書かれている。
 +
 +(※)を付けたものは、下記でもう少し詳しく記述している。
  
 == UniTuple, Tuple == == UniTuple, Tuple ==
  
-タプルは、複数の要素をまとめることが出来る。要素の型が全て統一されているかどうかで区別される。+タプルは、複数の要素をまとめることが出来る。戻り値として複数の値を返す場合に重宝する。 
 + 
 +要素の型が全て統一されているかどうかで大きく以下の2つに区別される。
  
-  * UniTuple: homogeneous = 統一されている +  * UniTuple: 統一されている(homogeneous) 
-  * Tuple: heterogeneous = 複数の型が混在している+  * Tuple: 複数の型が混在している(heterogeneous)
  
-heterogeneousなTupleは、変数によるindexアクセスなどいくつかの機能が使えない(型を特定できないのでそりゃそう)。+混在しているTupleは、変数によるindexアクセスなどいくつかの機能が使えない(型を特定できないのでそりゃそう)。
 また、イテレートするときに特殊な書き方が必要となる。 また、イテレートするときに特殊な書き方が必要となる。
  
-  * [[https://numba.pydata.org/numba-doc/dev/reference/pysupported.html#tuple|Supported Python features — Numba 0.50.0.dev0+236.g64fbf2b-py3.7-linux-x86_64.egg documentation]]+  * [[https://numba.pydata.org/numba-doc/dev/reference/pysupported.html#tuple|Supported Python features — Numba documentation]]
  
-引数や戻り値のために一瞬使うだけなら大した影響はないだろうが、内部でガッツリ使う場合はなるべくUniTupleな構造にした方がよい。+引数や戻り値のために一瞬使うだけなら大した影響はないだろうが、内部で使う場合はなるべくUniTupleな構造にした方がよい。
  
 型指定の書き方は、以下のようにする。 型指定の書き方は、以下のようにする。
行 127: 行 179:
 == reflected list と numba.typed.List == == reflected list と numba.typed.List ==
  
-  * [[https://numba.pydata.org/numba-doc/dev/reference/pysupported.html#list|Supported Python features — Numba 0.50.0.dev0+236.g64fbf2b-py3.7-linux-x86_64.egg documentation]]+  * [[https://numba.pydata.org/numba-doc/dev/reference/pysupported.html#list|Supported Python features — Numba documentation]]
  
-どちらも、Pythonのlist機能をNumbaで出来る範囲で表現することを目的としたデータ構造。+どちらも、Pythonのlist機能に似せようなデータ構造。一応、以下の違いがある
  
-  * reflected list: Pythonのlistをそのまま受け取ってNumbaで解釈できるようにしたもの+  * reflected list: 見た目上の扱いはPythonのlistのまま、制約を加えてNumbaで解釈できるようにしたもの
   * typed list: Numba独自のリスト構造クラス   * typed list: Numba独自のリスト構造クラス
  
-reflected list は deprecated。下記を読むと、ネスト等で複雑になると限界があるので typed listに置きかえていく方針らしい。+reflected listは deprecated。ネスト等で複雑になると限界があるのでtyped listに置きかえていく方針らしい。\\ 
 +そのため、(今のところ問題なく使えるものの)ver.0.45以降ではその旨の警告が出る
  
   * [[http://numba.pydata.org/numba-doc/latest/reference/deprecation.html#deprecation-of-reflection-for-list-and-set-types|Deprecation Notices — Numba 0.50.1 documentation]]   * [[http://numba.pydata.org/numba-doc/latest/reference/deprecation.html#deprecation-of-reflection-for-list-and-set-types|Deprecation Notices — Numba 0.50.1 documentation]]
- 
-そのため、reflected list は(問題なく使えるものの)ver.0.45以降では警告が出る。 
  
 また、typed listの方は「実験的機能」とされていて、バグがあったり、高速化の恩恵が少なくなる可能性が言及されている。 また、typed listの方は「実験的機能」とされていて、バグがあったり、高速化の恩恵が少なくなる可能性が言及されている。
行 149: 行 200:
  
   * reflected list   * reflected list
-    * 外部から与える際はネスト不可。要素の型は統一されている必要がある +    * 外部から与える際はネスト不可Numba関数内部で生成する場合はネスト可 
-    * Numba関数内部ではネスト可 +    * Numba関数内部で「''a = [0, 1]''」とすると、こちらになる
-    * Numba関数内部で「''a = [0, 1]''などとすると、こちらになる+
   * typed.List   * typed.List
     * ネスト可     * ネスト可
-    * ''numba.typed.List()'' で空のインスタンスを生成、1つずつappendする+    * 一度に生成することはできなくて、''numba.typed.List()'' で空のインスタンスを生成、1つずつappendする((ver.0.49で、コンストラクタにイテレータを渡せるようになった))
  
 また、Pythonのデータ構造にはListの他にSet, Dictがあるが、 また、Pythonのデータ構造にはListの他にSet, Dictがあるが、
行 198: 行 248:
 == コンパイル済み関数 == == コンパイル済み関数 ==
  
-  * [[https://numba.pydata.org/numba-doc/dev/reference/pysupported.html#functions-as-arguments|Supported Python features — Numba 0.50.0.dev0+236.g64fbf2b-py3.7-linux-x86_64.egg documentation]]+  * [[https://numba.pydata.org/numba-doc/dev/reference/pysupported.html#functions-as-arguments|Supported Python features — Numba documentation]]
  
-引数してみ使え、戻り値には出来ない。+んど処理が同じで一部だけ異る場合、関数を外部注入したことはある
  
-また、関数を引数とする関数事前コンパイルする方法がわからない。感触的に無理っぽい。+Numbaでは、関数オブジェクトは引数としてのみ使え、戻り値には出来ない。また、(おそらく)関数を引数とした関数はキャッシュや事前コンパイルは出来ない。
  
-まず、型指定の記述方法が不明。型指定しないと事前コンパイルは出来ない +まず、事前コンパイルには型指定が必須だが、その記述方法が不明。 
- +一応、後述の「[[#推論させた型指定]]」の方法使記述方法わからなくても型指定ること出来るが……
-一応、型指定しない ''@njit'' 関数に対し実際に引数て呼び出すと ''その関数.nopython_signatures''呼び出した引数・戻り値の型指定List形式で追加される。 +
-これを他の関数の型指定に用いること出来る+
  
 <sxh python> <sxh python>
 @njit('i8(i8)') @njit('i8(i8)')
-def double(a):+def double(a):    # 引数として渡したい関数
     return 2 * a     return 2 * a
  
 @njit @njit
-def fumidai(func, a):+def fumidai(func, a):    # 踏み台関数
     return func(a)     return func(a)
  
-fumidai(double, 2)+fumidai(double, 2)    # 実際に呼び出す
  
 @njit(fumidai.nopython_signatures[0]) @njit(fumidai.nopython_signatures[0])
-def hontai(func, a): +def hontai(func, a):    # fumidaiに記録された型定義をもって、hontaiを型指定 
-    return func(a)+    return func(a)      # →通る
  
-hontai(double, 3)+hontai(double, 3)       # => 6
 </sxh> </sxh>
  
 しかし、 しかし、
  
-  * ''hontai'' を ''@njit(cache=True)'' でキャッシュしようとすると「''TypeError: cannot pickle 'weakref' object''」が発生する +  * キャッシュや事前コンパイルはできない 
-  * double以外の関数を定義して渡すと「''TypeError: No matching definition for argument type(s) type(CPUDispatcher(<function 関数名 at 0x......))''」が発生+    * ''hontai'' を ''@njit(cache=True)'' や ''cc.export()'' でキャッシュしようとするとエラーが発生 
 +    * <code>TypeError: cannot pickle 'weakref' object</code> 
 +  * 異なる関数を与えることはできない 
 +    * ''hontai'' に ''double'' 以外の関数を定義して渡すと、たとえ引数型が同じでもエラーが発生 
 +    * <code>TypeError: No matching definition for argument type(s) type(CPUDispatcher(<function hontai at 0x......))</code> 
 +    * 結局決まった関数しか渡せないなら、引数にしなくてもグローバルで定義して呼び出のと変わんない
  
-実体のあるオブジェクトでなく参照として渡しているので、関数を置きかえたり、ファイルとして残すことはできないらしい。+実体のあるオブジェクトでなく参照として渡されるので、関数を置きかえたり、ファイルとして残すことはできないらしい。
  
-有効に使る場られる+''@njit'' をキャッシュを使わない本来の実行時コンパイルとして使っている場、これの制約は大きく影響しない。\\ 
 +せいぜい、異なる関数を引数とするたびに別々の関数としてコンパイルされるので、与える関数の種類は少ないに越したことはない、というくらい
  
 +キャッシュしたい場合は、関数は引数としても使えないと考えておいた方がよいだろう。
  
  
行 248: 行 303:
 特にdictは、イテレータのように渡されるのか、最初のkey-valueの組合せで型が確定する。例えば、int→floatはキャストできるが、float→intはできないため、以下のようになる。 特にdictは、イテレータのように渡されるのか、最初のkey-valueの組合せで型が確定する。例えば、int→floatはキャストできるが、float→intはできないため、以下のようになる。
  
-  d = {2: 2.0, 3: 3}  はOK   (key, value) = (int64, float64)+  d = {2: 2.0,   3: 3  }  はOK   (key, value) = (int64, float64)
      
-  d = {2: 2, 3: 3.0}  はエラー+  d = {2: 2,     3: 3.0}  はエラー
  
-ただ、使ってみた感触としてはこれらのパフォーマンスはあまり優れているとは言えない。実行時間が数倍遅くなることもある。なるべくならNumpy配列を使った方がよい。+ただ、使ってみた感触としてはこれらのパフォーマンスはあまり優れているとは言えない。 
 +実行時間が数倍遅くなることもある(それでもNumbaを使わないよりは十分速いが)。なるべくならNumpy配列を使った方がよい。
  
 また、Set, Dictに関しては、リスト内包表記で生成することは出来ない。(イテレート元とすることは出来る) また、Set, Dictに関しては、リスト内包表記で生成することは出来ない。(イテレート元とすることは出来る)
行 297: 行 353:
   * [[https://docs.python.org/ja/3/library/bisect.html|bisect]]   * [[https://docs.python.org/ja/3/library/bisect.html|bisect]]
     * 未実装     * 未実装
 +    * [[https://numpy.org/devdocs/reference/generated/numpy.searchsorted.html|numpy.searchsorted]] はあるのでそちらを使う
   * [[https://docs.python.org/ja/3/library/itertools.html|itertools]]   * [[https://docs.python.org/ja/3/library/itertools.html|itertools]]
     * 未実装     * 未実装
     * accumulate, combinations 等は使えない     * accumulate, combinations 等は使えない
 +    * accumulateに関しては、[[https://numpy.org/doc/stable/reference/generated/numpy.cumsum.html|numpy.cumsum]]、[[https://numpy.org/doc/stable/reference/generated/numpy.cumprod.html|numpy.cumprod]] は使える
  
   * [[https://docs.python.org/ja/3/library/heapq.html|heapq]]   * [[https://docs.python.org/ja/3/library/heapq.html|heapq]]
     * 意外と(?)、ほぼ全ての機能を使える     * 意外と(?)、ほぼ全ての機能を使える
  
 +==== Numpy関数 ====
  
 +公式にまとまってる。
 +
 +  * [[https://numba.pydata.org/numba-doc/dev/reference/numpysupported.html|Supported NumPy features]]
 +
 +それなりに対応しているが、関数自体は存在してもオプション引数は未対応のものも多い。
 +
 +個人的に axis オプションはよく使うが、未対応なのはもどかしい。
 +
 +=== 速度 ===
 +
 +無理にNumpy関数を使わずとも、単純なforループで代替可能な処理はforループで書いた方が速いこともある。
 +
 +NumPy関数の多くは非破壊的に、結果は新しい配列を生成して返すため、生成コストが発生する。 \\
 +引数 ''out'' を渡すとそこに格納してくれる関数もあるが、Numbaでは ''out'' 引数は基本的に使えない。
 +
 +破壊的な処理で問題ない場合は特に、forループで更新していく方が速い。
 +
 +たとえば、2つの配列で同じindex同士の最大値を求める場合、''np.maximum(aaa, bbb)'' よりforループで ''aaa[i] = max(aaa[i], bbb[i])'' とした方が速い。
 +
 +++++ テスト例 |
 +
 +<sxh python>
 +import sys
 +from timeit import timeit
 +
 +import numpy as np
 +
 +
 +def solve1(n, aaa, bbb):
 +    return np.maximum(aaa, bbb)
 +
 +
 +def solve2(n, aaa, bbb):
 +    for i in range(n):
 +        aaa[i] = max(aaa[i], bbb[i])
 +    return aaa
 +
 +
 +from numba import njit
 +
 +solve1 = njit('(i8,i8[:],i8[:])', cache=True)(solve1)
 +solve2 = njit('(i8,i8[:],i8[:])', cache=True)(solve2)
 +print('compiled', file=sys.stderr)
 +
 +n = 10 ** 7
 +aaa = np.random.randint(0, 1 << 30, size=n, dtype=np.int64)
 +bbb = np.random.randint(0, 1 << 30, size=n, dtype=np.int64)
 +
 +print(timeit('solve1(n, aaa, bbb)', globals=globals(), number=100))
 +print(timeit('solve2(n, aaa, bbb)', globals=globals(), number=100))
 +
 +# =>
 +# 4.919
 +# 1.482
 +</sxh>
 +
 +++++
 ===== グローバル変数 ===== ===== グローバル変数 =====
  
行 327: 行 443:
 </sxh> </sxh>
  
 +グローバル変数がリストなどのオブジェクトの場合、エラーとなる(この辺の詳細な条件は要確認)。
 +
 +<sxh python>
 +from numba import njit
 +
 +glb = [1, 2, 3]
 +
 +@njit('i8()', cache=True)
 +def global_test():  # => コンパイルエラー
 +    return glb[-1]
 +
 +</sxh>
 +
 +一方で、「大枠のNumba関数内で、リストもクロージャー関数も定義し、クロージャー関数からリストにアクセス」する場合はOK。コンパイル後の変更もちゃんと反映される。
 +
 +<sxh python>
 +from numba import njit
 +
 +@njit('void()', cache=True)
 +def solve():
 +    glb = [[1, 2, 3]]  # 変数定義自体は関数より前が必須
 +
 +    def global_test():
 +        return glb[-1]
 +
 +    print(global_test())  # => [1, 2, 3]  NestedなListだろうとアクセスできる
 +
 +    glb.append([4, 5, 6])
 +    print(global_test())  # => [4, 5, 6]  変更も反映
 +
 +    glb = [[7, 8, 9]]
 +    print(global_test())  # => [7, 8, 9]  代入も反映(ただし型は一致しないと代入の方でエラー)
 +
 +solve()
 +</sxh>
  
 ===== 型指定 ===== ===== 型指定 =====
  
-''@njit()'' の引数には、関数の引数と戻り値の型(Signature)を指定することができる。+''@njit()'' の引数には、コンパイルする関数の引数の型と戻り値の型(Signature)を指定することができる。 
 + 
 +事前コンパイルには必須となる。 
 + 
 +型指定が無いと、必然的に実行時コンパイルとなり、実際に呼び出されたときの引数に応じて型推論してからコンパイルされる。 
 + 
 +型を指定すると事前コンパイルが可能になるほか、以下の恩恵を期待できる。 
 + 
 +  * 予期せぬ重複コンパイルを防止できる 
 +    * JITは、引数の型が異なる呼び出しのたびに、別々の関数としてコンパイルされる 
 +    * float型でコンパイル済みのJIT関数に、うっかりint型を与えると、必要ないのに新規コンパイルが走ってしまう 
 +  * 読んだだけで挙動を想定しやすいコードになる 
 +  * よりメモリ効率を改善できる 
 +    * intやfloatは、(実行環境によるが、通常)64bit型としてコンパイルされる 
 +    * 8bitで済むような変数はそう明記することで、メモリ効率を改善でき、若干の高速化にも繋がる 
 + 
 + 
 +==== 記述方法 ====
  
-指定が無いと、関が呼ばれたときの引数に応じて型推論してからコンパイルされるに対し、 +「戻り値の型(引1型, 引数2の型, ...)」のように記述す。戻型を省略した場合は推論される。
-を指定すると指定した型に向けたコンパイルしか行われないで、 +
-「より頑健なコードになる」「8bitで済むような変数64bit割り当てられなどが無くな、より高速化できる」など恩恵が期待できる。+
  
-「戻り値の型(引数1の型, 引数2の型, ...)」のように記述する。 
 numbaで定義された型指定用クラスを使う方法と、文字列を使う方法があるが、文字列を使った方がimportなどの手間が省ける。 numbaで定義された型指定用クラスを使う方法と、文字列を使う方法があるが、文字列を使った方がimportなどの手間が省ける。
-戻り値の型を省略した場合は推論される。 
  
 文字列とデータ型の対応は以下の通り。 文字列とデータ型の対応は以下の通り。
  
-  * [[https://numba.pydata.org/numba-doc/dev/reference/types.html|Types and signatures — Numba 0.50.0.dev0+236.g64fbf2b-py3.7-linux-x86_64.egg documentation]]+  * [[https://numba.pydata.org/numba-doc/dev/reference/types.html|Types and signatures — Numba documentation]]
  
-型指定用のクラスは ''int64'' など数値がbit数を表すのに対し、文字列指定は ''i8'' などbyte数を表すに注意。+型指定用のクラスは ''int64'' など数値がbit数を表すのに対し、文字列指定は ''i8'' などbyte数を表す。微妙な相違に注意。
  
-また、配列は ''np.ndarray'' を指す。+また、''型名[:]'' でその型の配列であることを示すが、この配列は ''np.ndarray'' を指す。
  
 <sxh python> <sxh python>
行 374: 行 538:
 </sxh> </sxh>
  
 +
 +==== 推論させた型指定 ====
 +
 +型をどう記述すればいいかわかんない、という場合、一旦JITを走らせて推論させた型でもって、AOT用の型指定とさせることも(一応)できる。
 +
 +以下のコード、''fumidai.nopython_signatures'' で型を参照できるので、それを ''hontai'' の型指定に用いる。
 +
 +  * [[https://qiita.com/yniji/items/d012bb9f938e0445a3ff#numba-aot|AtCoderで Python を高速化する Numpy + Numba を使う - Qiita]](再掲)
 +
 +毎回、型推論のためにダミー実行させるのも無駄なので、以下の例ではコンパイルさせたい場合に限り実行時オプションに compile を付けることで区別している。
 +
 +
 +<sxh python>
 +
 +if sys.argv[1] == 'compile':
 +    from numba import njit
 +    from numba.pycc import CC
 +
 +    def func(a, b):          # コンパイルしたい関数
 +        return a + b
 +        
 +    fumidai = njit(func)     # 型指定無しでnjit化
 +
 +    fumidai(1.5, 2)          # 使用を想定している引数型で呼び出す
 +                             # この時点でこの引数型に対するJITが走る
 +                             # float64(float64, int64) になってるはず
 +
 +    # 同じ関数を、fumidaiで推論された型でもって型指定、事前コンパイル
 +    cc = CC('my_module')
 +    cc.export('hontai', fumidai.nopython_signatures[0])(func)
 +    cc.compile()
 +
 +from my_module import hontai
 +
 +print(hontai(3.5, 4))  # => 7.5
 +print(hontai(5, 6.5))  # => 11.0(6.5はintにキャストされる)
 +
 +</sxh>
 +
 +また、ここまで自動化しなくても、一度ダミー実行させた関数の ''noython_signatures'' をチェックすることで、型指定の方法のヒントを得ることができるかも。
 +ただし、''nopython_signatures'' で表示される型と、型指定の記述方法は若干異なるっぽい。
 +
 +===== クラス =====
 +
 +''numba.experimental.jitclass'' で、クラスのコンパイルも出来る。モジュール名の通り、実験的機能っぽい。
 +
 +  * [[https://numba.pydata.org/numba-doc/dev/user/jitclass.html|Compiling Python classes with @jitclass]]
 +
 +  * 事前コンパイル(AOT)はできず、実行時コンパイル(JIT)のみ
 +    * [[https://github.com/numba/numba/issues/6648|AOT compilation of jitclass · Issue #6648 · numba/numba · GitHub]]
 +  * ''cache=True''などのキャッシュもできないっぽい
 +    * [[https://github.com/numba/numba/issues/4830|cache=True for jitclasses · Issue #4830 · numba/numba · GitHub]]
 +  * 事前にクラス変数の型を、''Numba type instances''に用意された中から指定する必要がある
 +    * [[https://numba.pydata.org/numba-doc/dev/reference/types.html|Types and signatures]]
 +    * そのため、関数や他のクラスなど、型指定できない(難しい)型はクラス変数に持ちにくい
 +    * どのように指定するかは上記ドキュメント参照
 +  * nopythonモードでコンパイルを試みられる
 +    * うっかりobjectモードになってて高速化の恩恵が少なかった、などの心配は無い
 +
 +複雑なクラスになるとコンパイルに何十秒とかかってしまう。\\
 +もちろん何回も使うのであれば十分にお釣りが来るが、(今のところ)AOTやキャッシュが出来ない以上、使いどころは選ぶ必要がある。
 +
 +==== 型指定の例 ====
 +
 +|''numba.int64''     | 64bit整数(''numba.types.int64'' も同じ)|
 +|''numba.float64[:]''| 64bit浮動小数の1次numpy.ndarray|
 +|''numba.int64[:,:,:]''| 64bit整数の3次numpy.ndarray|
 +|''numba.types.List(numba.int64)''| 64bit整数のList \\ ''a = [0]'' のように初期化されるもの|
 +|''numba.types.ListType(numba.int64)''| 64bit整数のtyped.List \\ ''a = numba.typed.List.empty_list(numba.int64)'' のように初期化されるもの|
 +
 +''numba.typeof(○○)'' で型を調べられるので、わからない場合はそれを使う。
 +
 +typeofを使えば一応は関数や独自クラスの型もわかるが、''type(CPUDispatcher(<function <lambda> at 0x00000000123456789>))'' のようになり、
 +結局、型と言うよりは「メモリのどこどこに記録されたオブジェクト」という感じで、
 +''typeof''で調べる際に用いたその「○○」しか入れられない。
 +同質の他のオブジェクトを渡すとエラーになる。
 +
 +<sxh python>
 +
 +function_for_type_check = numba.njit()(lambda: 0)
 +function_truly_want_to_use = numba.njit()(lambda: 1)
 +
 +spec = [
 +    ('func', numba.typeof(function_for_type_check))
 +]
 +
 +@numba.experimental.jitclass(spec)
 +class A:
 +    def __init__(self):
 +        self.func = function_truly_want_to_use  # => Error
 +</sxh>
  
 ===== 事前コンパイル(AOT) ===== ===== 事前コンパイル(AOT) =====
行 379: 行 634:
 ''numba.pycc'' モジュールを使うことで、関数をコンパイルした結果を保存することができる。 ''numba.pycc'' モジュールを使うことで、関数をコンパイルした結果を保存することができる。
  
-  * [[https://numba.pydata.org/numba-doc/dev/user/pycc.html|Compiling code ahead of time — Numba 0.50.0.dev0+236.g64fbf2b-py3.7-linux-x86_64.egg documentation]]+  * [[https://numba.pydata.org/numba-doc/dev/user/pycc.html|Compiling code ahead of time — Numba documentation]]
  
 PCにCコンパイラがインストールされている必要がある。 PCにCコンパイラがインストールされている必要がある。
行 386: 行 641:
   * [[https://www.kkaneko.jp/tools/win/buildtool.html|Visual Studio ビルドツール 2019 (Build Tools for Visual Studio 2019) のインストール(Windows 上)]]   * [[https://www.kkaneko.jp/tools/win/buildtool.html|Visual Studio ビルドツール 2019 (Build Tools for Visual Studio 2019) のインストール(Windows 上)]]
  
-コンパイル時点で引数・返値の型がわかっている要がある。 +指定が必る。
-明示的に型を与える場合はよいが、''@njit'' のみを付けて型が未定義の関数はサンプルデータを与えて一度JITを走らせる、いう方法がある。 +
- +
-  * [[https://qiita.com/yniji/items/d012bb9f938e0445a3ff#numba-aot|AtCoderで Python を高速化する Numpy + Numba を使う - Qiita]](再掲)+
  
 使う際は、通常のモジュールと同様 ''import'' すればよい。 使う際は、通常のモジュールと同様 ''import'' すればよい。
 コンパイル後に出来るファイルは ''my_module.cp38-win_amd64.pyd'' などとプラットフォーム名が付く場合があるが、''import'' 時の記述は ''import my_module'' でよい。 コンパイル後に出来るファイルは ''my_module.cp38-win_amd64.pyd'' などとプラットフォーム名が付く場合があるが、''import'' 時の記述は ''import my_module'' でよい。
-IDEでは、「そんなモジュール無いで」という構文エラーが出たり、引数の型補間が行われない可能性はある。 
  
 +<note>
 +2023/10
 +
 +pycc モジュールは、近く非推奨になるらしい。
 +
 +  * [[https://numba.readthedocs.io/en/stable/reference/deprecation.html#deprecation-of-the-numba-pycc-module|Deprecation Notices — Numba documentation]]
 +
 +ざっと見たところでは、distutils モジュールが Python 3.12 で削除される予定であるが、pyccはそれに依存しているとのこと。
 +
 +  * [[https://docs.python.org/ja/3/library/distutils.html|distutils --- Python モジュールの構築とインストール — Python ドキュメント]]
 +
 +で、pyccで生成されるコードはjitでコンパイルされるコードと非互換だし制限が多いしで、これを機に他の方法を作った方がよいと判断されたらしい。
 +
 +代替手段が用意されるまではpyccは維持される、と書いてはいる(本当に信じていいかはわかんない。Python3.12 がメジャーになるまでのタイムリミットもあるし)ので、しばらくはAOTが必要なら使い続けてよいだろうが、Python本体の3.12へのアップデートは少し調べてからの方がいいし、どうしてもというわけでなければJITへの切り替えも考えた方がよいかも。
 +</note>
  
  
programming/python/packages/numba.1593593576.txt.gz · 最終更新: 2020/07/01 by ikatakos
CC Attribution 4.0 International
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0