行コードはPythonの運転速度を100倍にします。


pythonはスケーリングの運行速度が遅いですが、実際にはpythonの実行効率は遅くないです。遅いのはpython用のインタプリタCpythonの運行効率が悪いです。
「コードの一行がpythonの運行速度を100倍にする」というのは決して人気取りの論調ではない。
これの一番簡単な例を見てみます。1から1億円を加算します。
最初のコード:

import time
def foo(x,y):
  tt = time.time()
  s = 0
  for i in range(x,y):
    s += i
  print('Time used: {} sec'.format(time.time()-tt))
  return s
print(foo(1,100000000))
結果:
Time used:6.79874801635742 sec
4999999900000000
コードを追加して、結果を見ます。

from numba import jit
import time
@jit
def foo(x,y):
  tt = time.time()
  s = 0
  for i in range(x,y):
    s += i
  print('Time used: {} sec'.format(time.time()-tt))
  return s
print(foo(1,100000000))
結果:
Time used:0.04680037498474121 sec
4999999900000000
100倍以上速いですか?
じゃ、ここで「numbaライブラリのjitモジュールはなぜ牛がちぎれるのですか?」
NumPyの創始者Travis OliphantはEntthoughtを離れた後、CONTINUUMを創建し、Python大データ処理の応用に力を入れています。最近発売されたNumbaプロジェクトはNumPy配列を扱うPython関数JITをマシンコードにコンパイルして実行することができます。それによって、プログラムの演算速度を100倍上げることができます。
NumbaプロジェクトのホームページにLinux下の詳細なインストール手順があります。LLVMをコンパイルするには時間がかかります。
WindowsユーザーはUoffical Windows Binaries for Python Extens PackagesからLLVMPy、meta、numbaなどのいくつかの拡張ライブラリをダウンロードできます。
次の例を見ます。

import numba as nb
from numba import jit
@jit('f8(f8[:])')
def sum1d(array):
 s = 0.0
 n = array.shape[0]
 for i in range(n):
  s += array[i]
 return s
import numpy as np
array = np.random.random(10000)
%timeit sum1d(array)
%timeit np.sum(array)
%timeit sum(array)
10000 loops, best of 3: 38.9 us per loop
10000 loops, best of 3: 32.3 us per loop
100 loops, best of 3: 12.4 ms per loop
numbaにはいくつかの修飾器が提供されています。それらはその修飾の関数JITをマシンコード関数にコンパイルして、Pythonでマシンコードを呼び出すことができる包装対象を返します。Python関数を成能の高速で実行されるマシンコードにコンパイルするためには、JITコンパイラ関数の各パラメータと返却値の種類を教えなければなりません。タイプ情報は、上記の例では、複数の方法で指定できます。タイプ情報は文字列'f 8(f 8)'によって指定されます。ここで、'f 8'は8バイトのダブル精度浮動小数点を表し、括弧の前の'f 8'は戻り値タイプを表し、括弧の中のはパラメータタイプを表し、''''''は一次元配列を表します。したがって、全体のタイプの文字列はsum 1 d()を表し、パラメータがダブル精度の浮動小数点数の一次元配列であり、戻り値はダブル精度の浮動小数点数である。
注意したいのは、JITが生成した関数は指定されたタイプのパラメータのみを演算することができます。

print sum1d(np.ones(10, dtype=np.int32))
print sum1d(np.ones(10, dtype=np.float32))
print sum1d(np.ones(10, dtype=np.float64))
1.2095376009e-312
1.46201599944e+185
10.0
JITがすべての種類のパラメータに対して演算を行うことができれば、auttitを使用することができます。

from numba import autojit
@autojit
def sum1d2(array):
 s = 0.0
 n = array.shape[0]
 for i in range(n):
  s += array[i]
 return s
%timeit sum1d2(array)
print sum1d2(np.ones(10, dtype=np.int32))
print sum1d2(np.ones(10, dtype=np.float32))
print sum1d2(np.ones(10, dtype=np.float64))
10000 loops, best of 3: 143 us per loop
10.0
10.0
10.0
autitはパラメータタイプによってマシンコード関数を動的に生成することができますが、パラメータタイプを調べるたびに計算速度が低下します。numbaの使い方は簡単です。基本的にはjitとautujitの二つの修飾器といくつかのタイプの対象を使います。以下のプログラムはnumbaがサポートするすべてのタイプを示しています。

print [obj for obj in nb.__dict__.values() if isinstance(obj, nb.minivect.minitypes.Type)]
[size_t, Py_uintptr_t, uint16, complex128, float, complex256, void, int , long double,
unsigned PY_LONG_LONG, uint32, complex256, complex64, object_, npy_intp, const char *,
double, unsigned short, float, object_, float, uint64, uint32, uint8, complex128, uint16,
int, int , uint8, complex64, int8, uint64, double, long double, int32, double, long double,
char, long, unsigned char, PY_LONG_LONG, int64, int16, unsigned long, int8, int16, int32,
unsigned int, short, int64, Py_ssize_t]
仕事の原理
numbaのはmetaモジュールでPython関数のast構文樹を解析し、各変数に対応する型式情報を追加します。その後、llvmpyを呼び出してマシンコードを生成し、最後にマシンコードのPythonを生成してインターフェースを起動します。
メタモジュール
numbaの仕事原理を研究することによって、私たちは多くの有用な道具を見つけることができます。例えば、metaモジュールは、プログラムのソースコード、ast文法ツリー、Pythonバイナリコードの間で相互に変換することができます。次の例を見ます。

def add2(a, b):
 return a + b
decomple le_funcは関数のコードオブジェクトをast文法ツリーに逆コンパイルできます。astはast構文ツリーを直感的に表示し、この2つのツールを使ってPythonのast構文ツリーを学ぶのに役立つ。

from meta.decompiler import decompile_func
from meta.asttools import str_ast
print str_ast(decompile_func(add2))
FunctionDef(args=arguments(args=[Name(ctx=Param(),
          id='a'),
         Name(ctx=Param(),
          id='b')],
       defaults=[],
       kwarg=None,
       vararg=None),
   body=[Return(value=BinOp(left=Name(ctx=Load(),
            id='a'),
          op=Add(),
          right=Name(ctx=Load(),
            id='b')))],
   decorator_list=[],
   name='add2')
python_sourceは、ast構文ツリーをPythonソースコードに変換することができます。

from meta.asttools import python_source
python_source(decompile_func(add2))
def add2(a, b):
 return (a + b)
decomple le_pycは上記の2つを結合し、Pythonをコンパイルした後のpycまたはpyoファイルをソースコードに逆コンパイルすることができます。次はtmp.pyファイルを書いて、py_を通します。compleはそれをtmp.pycにコンパイルします。

with open("tmp.py", "w") as f:
 f.write("""
def square_sum(n):
 s = 0
 for i in range(n):
  s += i**2
 return s
""")
import py_compile
py_compile.compile("tmp.py")
次はdecomppile_を呼び出します。pycはtmp.pycをソースコードとして表示します。

with open("tmp.pyc", "rb") as f:
 decompile_pyc(f)
def square_sum(n):
 s = 0
 for i in range(n):
  s += (i ** 2)
 return s
llvmpyモジュール
LLVMはダイナミックコンパイラであり、llvmpyはPythonを通じてLLVMを呼び出して動的にマシンコードを作成することができます。直接にllvmpyによってマシンコードを作成するのは比較的に煩わしいです。例えば、次のプログラムは二つの整数の和を計算する関数を作成し、その計算結果を呼び出します。

from llvm.core import Module, Type, Builder
from llvm.ee import ExecutionEngine, GenericValue
# Create a new module with a function implementing this:
#
# int add(int a, int b) {
# return a + b;
# }
#
my_module = Module.new('my_module')
ty_int = Type.int()
ty_func = Type.function(ty_int, [ty_int, ty_int])
f_add = my_module.add_function(ty_func, "add")
f_add.args[0].name = "a"
f_add.args[1].name = "b"
bb = f_add.append_basic_block("entry")
# IRBuilder for our basic block
builder = Builder.new(bb)
tmp = builder.add(f_add.args[0], f_add.args[1], "tmp")
builder.ret(tmp)
# Create an execution engine object. This will create a JIT compiler
# on platforms that support it, or an interpreter otherwise
ee = ExecutionEngine.new(my_module)
# Each argument needs to be passed as a GenericValue object, which is a kind
# of variant
arg1 = GenericValue.int(ty_int, 100)
arg2 = GenericValue.int(ty_int, 42)
# Now let's compile and run!
retval = ee.run_function(f_add, [arg1, arg2])
# The return value is also GenericValue. Let's print it.
print "returned", retval.as_int()
returned 142
f_addは動的に生成されたマシンコード関数であり、C言語のコンパイル後の関数として考えられます。上のプログラムの中で、私達はee.run_を通します。functionはこの関数を呼び出しますが、実際にはそのアドレスを取得してPythonのctypesモジュールで呼び出します。
まずee.get_を通しますポライトto_functionがf_を獲得しましたadd関数のアドレス:

addr = ee.get_pointer_to_function(f_add)
addr
2975997968L
そして、ctypes.PYFUCTYPEで関数タイプを作成します。

import ctypes
f_type = ctypes.PYFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_int)
最後にf_を通しますtype関数のアドレスを呼び出し可能なPython関数に変換して呼び出します。

f = f_type(addr)
f(100, 42)
142
numbaが完成した仕事は:
Python関数のast構文ツリーを解析して改造し、タイプ情報を追加します。
種類情報を持つast構文ツリーを、llvmpyを介してマシンコード関数に動的に変換し、ctypesと同様の技術でマシンコード関数にパッケージ関数を作成し、Pythonの呼び出しのために使用します。
締め括りをつける
以上は小编が绍介した一行のコードです。Pythonの运行速度を100倍にします。皆さんに助けてほしいです。もし何か疑问があれば、メッセージをください。小编はすぐに返事します。ここでも私たちのサイトを応援してくれてありがとうございます。