Note
Go to the end to download the full example code.
First examples with onnxruntime¶
Example First examples with onnx-array-api defines a custom
loss and then executes it with class
onnx.reference.ReferenceEvaluator.
Next example replaces it with onnxruntime.
Example¶
import numpy as np
from onnx_array_api.npx import absolute, jit_onnx
from onnx_array_api.ort.ort_tensors import JitOrtTensor, OrtTensor
def l1_loss(x, y):
return absolute(x - y).sum()
def l2_loss(x, y):
return ((x - y) ** 2).sum()
def myloss(x, y):
l1 = l1_loss(x[:, 0], y[:, 0])
l2 = l2_loss(x[:, 1], y[:, 1])
return l1 + l2
ort_myloss = jit_onnx(myloss, JitOrtTensor, target_opsets={"": 17}, ir_version=8)
x = np.array([[0.1, 0.2], [0.3, 0.4]], dtype=np.float32)
y = np.array([[0.11, 0.22], [0.33, 0.44]], dtype=np.float32)
xort = OrtTensor.from_array(x)
yort = OrtTensor.from_array(y)
res = ort_myloss(xort, yort)
print(res.numpy())
0.042
Profiling¶
from onnx_array_api.profiling import profile, profile2graph
x = np.random.randn(10000, 2).astype(np.float32)
y = np.random.randn(10000, 2).astype(np.float32)
xort = OrtTensor.from_array(x)
yort = OrtTensor.from_array(y)
def loop_ort(n):
for _ in range(n):
ort_myloss(xort, yort)
def loop_numpy(n):
for _ in range(n):
myloss(x, y)
def loop(n=1000):
loop_numpy(n)
loop_ort(n)
ps = profile(loop)[0]
root, nodes = profile2graph(ps, clean_text=lambda x: x.split("/")[-1])
text = root.to_text()
print(text)
var -- 2000 2000 -- 0.00304 0.02256 -- npx_core_api.py:54:var (var)
__init__ -- 2000 2000 -- 0.01280 0.01952 -- npx_var.py:287:__init__ (__init__) +++
get_cst_var -- 2000 2000 -- 0.00300 0.00494 -- npx_var.py:216:get_cst_var (get_cst_var)
parent -- 2000 2000 -- 0.00115 0.00194 -- <frozen importlib._bootstrap>:645:parent (parent)
<method 'rpartition' of 'str' objects> -- 2000 2000 -- 0.00078 0.00078 -- ~:0:<method 'rpartition' of 'str' objects> (<method 'rpartition' of 'str' objects>)
__init__ -- 3000 3000 -- 0.02129 0.03181 -- npx_var.py:287:__init__ (__init__)
__init__ -- 3000 3000 -- 0.00080 0.00080 -- npx_var.py:281:__init__ (__init__)
self_var -- 2000 2000 -- 0.00067 0.00100 -- npx_var.py:375:self_var (self_var) +++
<method 'items' of 'dict' objects> -- 3000 3000 -- 0.00067 0.00067 -- ~:0:<method 'items' of 'dict' objects> (<method 'items' of 'dict' objects>) +++
<built-in method builtins.hasattr> -- 3000 3000 -- 0.00056 0.00056 -- ~:0:<built-in method builtins.hasattr> (<built-in method builtins.hasattr>) +++
<built-in method builtins.isinstance> -- 23000 23000 -- 0.00587 0.00587 -- ~:0:<built-in method builtins.isinstance> (<built-in method builtins.isinstance>) +++
<built-in method builtins.len> -- 6000 6000 -- 0.00117 0.00117 -- ~:0:<built-in method builtins.len> (<built-in method builtins.len>) +++
<method 'ravel' of 'numpy.ndarray' objects> -- 1000 1000 -- 0.00046 0.00046 -- ~:0:<method 'ravel' of 'numpy.ndarray' objects> (<method 'ravel' of 'numpy.ndarray' objects>)
self_var -- 4000 4000 -- 0.00139 0.00211 -- npx_var.py:375:self_var (self_var)
<built-in method builtins.hasattr> -- 4000 4000 -- 0.00072 0.00072 -- ~:0:<built-in method builtins.hasattr> (<built-in method builtins.hasattr>) +++
loop_numpy -- 1 1 -- 0.00043 0.09823 -- plot_onnxruntime.py:61:loop_numpy (loop_numpy)
myloss -- 1000 1000 -- 0.00299 0.09780 -- plot_onnxruntime.py:28:myloss (myloss)
__add__ -- 1000 1000 -- 0.00064 0.01949 -- npx_var.py:645:__add__ (__add__)
_binary_op -- 1000 1000 -- 0.00256 0.01885 -- npx_var.py:615:_binary_op (_binary_op)
var -- 1000 1000 -- 0.00184 0.01280 -- npx_core_api.py:54:var (var) +++
get_cst_var -- 1000 1000 -- 0.00147 0.00249 -- npx_var.py:216:get_cst_var (get_cst_var) +++
self_var -- 1000 1000 -- 0.00036 0.00055 -- npx_var.py:375:self_var (self_var) +++
<built-in method builtins.isinstance> -- 1000 1000 -- 0.00045 0.00045 -- ~:0:<built-in method builtins.isinstance> (<built-in method builtins.isinstance>) +++
l1_loss -- 1000 1000 -- 0.00972 0.05069 -- plot_onnxruntime.py:20:l1_loss (l1_loss)
wrapper -- 1000 1000 -- 0.00902 0.02593 -- npx_core_api.py:142:wrapper (wrapper)
annotation -- 1000 1000 -- 0.00013 0.00013 -- inspect.py:2773:annotation (annotation)
kind -- 2000 2000 -- 0.00025 0.00025 -- inspect.py:2777:kind (kind)
parameters -- 2000 2000 -- 0.00028 0.00028 -- inspect.py:3058:parameters (parameters)
return_annotation -- 1000 1000 -- 0.00013 0.00013 -- inspect.py:3062:return_annotation (return_annotation)
__init__ -- 1000 1000 -- 0.00849 0.01229 -- npx_var.py:287:__init__ (__init__) +++
<method 'items' of 'mappingproxy' objects> -- 1000 1000 -- 0.00038 0.00038 -- ~:0:<method 'items' of 'mappingproxy' objects> (<method 'items' of 'mappingproxy' objects>)
<method 'append' of 'list' objects> -- 1000 1000 -- 0.00028 0.00028 -- ~:0:<method 'append' of 'list' objects> (<method 'append' of 'list' objects>) +++
<method 'items' of 'dict' objects> -- 1000 1000 -- 0.00023 0.00023 -- ~:0:<method 'items' of 'dict' objects> (<method 'items' of 'dict' objects>) +++
<built-in method builtins.any> -- 1000 1000 -- 0.00094 0.00184 -- ~:0:<built-in method builtins.any> (<built-in method builtins.any>)
<genexpr> -- 2000 2000 -- 0.00066 0.00089 -- npx_core_api.py:143:<genexpr> (<genexpr>)
<built-in method ...tins.isinstance> -- 1000 1000 -- 0.00024 0.00024 -- ~:0:<built-in method builtins.isinstance> (<built-in method builtins.isinstance>) +++
<built-in method builtins.isinstance> -- 2000 2000 -- 0.00042 0.00042 -- ~:0:<built-in method builtins.isinstance> (<built-in method builtins.isinstance>) +++
<built-in method builtins.issubclass> -- 2000 2000 -- 0.00047 0.00047 -- ~:0:<built-in method builtins.issubclass> (<built-in method builtins.issubclass>) +++
<built-in method builtins.len> -- 1000 1000 -- 0.00019 0.00019 -- ~:0:<built-in method builtins.len> (<built-in method builtins.len>) +++
sum -- 1000 1000 -- 0.00062 0.01504 -- npx_var.py:890:sum (sum)
reduce_function -- 1000 1000 -- 0.00166 0.01442 -- npx_var.py:871:reduce_function (reduce_function)
var -- 1000 1000 -- 0.00120 0.00976 -- npx_core_api.py:54:var (var) +++
get_cst_var -- 1000 1000 -- 0.00153 0.00244 -- npx_var.py:216:get_cst_var (get_cst_var) +++
self_var -- 1000 1000 -- 0.00037 0.00056 -- npx_var.py:375:self_var (self_var) +++
l2_loss -- 1000 1000 -- 0.01685 0.02464 -- plot_onnxruntime.py:24:l2_loss (l2_loss)
<method 'sum' of 'numpy.ndarray' objects> -- 1000 1000 -- 0.00082 0.00779 -- ~:0:<method 'sum' of 'numpy.ndarray' objects> (<method 'sum' of 'numpy.ndarray' objects>)
_sum -- 1000 1000 -- 0.00061 0.00696 -- _methods.py:49:_sum (_sum)
<method 'reduce' of...py.ufunc' objects> -- 1000 1000 -- 0.00635 0.00635 -- ~:0:<method 'reduce' of 'numpy.ufunc' objects> (<method 'reduce' of 'numpy.ufunc' objects>) +++
<built-in method builtins.isinstance> -- 59000 59000 -- 0.01767 0.01767 -- ~:0:<built-in method builtins.isinstance> (<built-in method builtins.isinstance>)
<method 'append' of 'list' objects> -- 13000 13000 -- 0.00433 0.00433 -- ~:0:<method 'append' of 'list' objects> (<method 'append' of 'list' objects>)
<method 'items' of 'dict' objects> -- 16000 16000 -- 0.00505 0.00505 -- ~:0:<method 'items' of 'dict' objects> (<method 'items' of 'dict' objects>)
<built-in method builtins.issubclass> -- 38000 38000 -- 0.01102 0.01102 -- ~:0:<built-in method builtins.issubclass> (<built-in method builtins.issubclass>)
<built-in method builtins.len> -- 576000 576000 -- 0.07698 0.07698 -- ~:0:<built-in method builtins.len> (<built-in method builtins.len>)
<built-in method builtins.hasattr> -- 7000 7000 -- 0.00128 0.00128 -- ~:0:<built-in method builtins.hasattr> (<built-in method builtins.hasattr>)
<method 'reduce' of 'numpy.ufunc' objects> -- 9000 9000 -- 0.04297 0.04297 -- ~:0:<method 'reduce' of 'numpy.ufunc' objects> (<method 'reduce' of 'numpy.ufunc' objects>)
Benchmark¶
from pandas import DataFrame
from tqdm import tqdm
from onnx_array_api.ext_test_case import measure_time
data = []
for n in tqdm([1, 10, 100, 1000, 10000, 100000]):
x = np.random.randn(n, 2).astype(np.float32)
y = np.random.randn(n, 2).astype(np.float32)
obs = measure_time(lambda x=x, y=y: myloss(x, y))
obs["name"] = "numpy"
obs["n"] = n
data.append(obs)
xort = OrtTensor.from_array(x)
yort = OrtTensor.from_array(y)
obs = measure_time(lambda xort=xort, yort=yort: ort_myloss(xort, yort))
obs["name"] = "ort"
obs["n"] = n
data.append(obs)
df = DataFrame(data)
piv = df.pivot(index="n", columns="name", values="average")
piv
0%| | 0/6 [00:00<?, ?it/s]
17%|█▋ | 1/6 [00:00<00:02, 1.75it/s]
33%|███▎ | 2/6 [00:01<00:02, 1.41it/s]
50%|█████ | 3/6 [00:04<00:05, 1.69s/it]
67%|██████▋ | 4/6 [00:04<00:02, 1.32s/it]
83%|████████▎ | 5/6 [00:05<00:01, 1.17s/it]
100%|██████████| 6/6 [00:07<00:00, 1.16s/it]
100%|██████████| 6/6 [00:07<00:00, 1.17s/it]
Plots¶
import matplotlib.pyplot as plt
fig, ax = plt.subplots(1, 2, figsize=(12, 4))
piv.plot(
title="Comparison between numpy and onnxruntime", logx=True, logy=True, ax=ax[0]
)
piv["ort/numpy"] = piv["ort"] / piv["numpy"]
piv["ort/numpy"].plot(title="Ratio ort/numpy", logx=True, ax=ax[1])
fig.savefig("plot_onnxruntime.png")

Total running time of the script: (0 minutes 14.385 seconds)