আছে numexpr , numba এবং cython চারপাশে, এই উত্তর লক্ষ্য বিবেচনা এই সম্ভাবনার নিতে হয়।
তবে প্রথমে সুস্পষ্টভাবে বলা যাক: আপনি অজস্র-অ্যারেতে পাইথন-ফাংশনটিকে কীভাবে ম্যাপ করবেন না কেন, এটি পাইথন ফাংশনটি স্থির করে রাখে যার অর্থ প্রতিটি মূল্যায়নের জন্য:
- নম্পি-অ্যারে উপাদানটি অবশ্যই পাইথন-অবজেক্টে রূপান্তর করতে হবে (উদাঃ a
Float
)।
- সমস্ত গণনা পাইথন-অবজেক্টস দিয়ে সম্পন্ন হয় যার অর্থ দোভাষী, গতিশীল প্রেরণ এবং অপরিবর্তনীয় বস্তুর ওভারহেড থাকে।
সুতরাং উপরে উল্লিখিত ওভারহেডের কারণে অ্যারে দিয়ে লুপটি ব্যবহার করতে কোন যন্ত্রপাতি ব্যবহৃত হয় তা বড় ভূমিকা রাখে না - এটি নম্পির অন্তর্নির্মিত কার্যকারিতা ব্যবহারের চেয়ে অনেক ধীর থাকে।
আসুন নীচের উদাহরণটি একবার দেখুন:
# numpy-functionality
def f(x):
return x+2*x*x+4*x*x*x
# python-function as ufunc
import numpy as np
vf=np.vectorize(f)
vf.__name__="vf"
np.vectorize
খাঁটি-পাইথন ফাংশন ক্লাসের পদ্ধতির প্রতিনিধি হিসাবে নেওয়া হয়। ব্যবহার perfplot
(এই উত্তর পরিশিষ্ট কোড দেখুন) আমরা নিম্নলিখিত চলমান বার পাবেন:
আমরা দেখতে পাচ্ছি, খাঁটি পাইথন সংস্করণটির চেয়ে ন্যাক্পি-অ্যাপ্রোচ 10x-100x দ্রুত। বড় অ্যারে-আকারগুলির জন্য কর্মক্ষমতা হ্রাস সম্ভবত ডেটা আর ক্যাশে ফিট করে না কারণ।
এটিও উল্লেখ করার মতো, এটি vectorize
প্রচুর স্মৃতিও ব্যবহার করে, তাই প্রায়শই স্মৃতি-ব্যবহার হ'ল বোতল-ঘাড় (সম্পর্কিত এসও-প্রশ্ন দেখুন )। এছাড়াও নোট করুন, যে নাম্পির ডকুমেন্টেশন এতে np.vectorize
উল্লেখ করেছে যে এটি "প্রাথমিকভাবে সুবিধার জন্য সরবরাহ করা হয়, পারফরম্যান্সের জন্য নয়"।
স্ক্র্যাচ থেকে সি-এক্সটেনশন লেখার পাশাপাশি অন্যান্য সরঞ্জামগুলি ব্যবহার করা উচিত, যখন পারফরম্যান্স পছন্দ হয় following
একজন প্রায়শই শুনেন যে, নিম্পি-পারফরম্যান্স যতটা ভাল হয় ততই ভাল, কারণ এটি হুডের নীচে খাঁটি সি। তবুও উন্নতির অনেক জায়গা আছে!
ভেক্টরাইজড নম্পি-সংস্করণে প্রচুর অতিরিক্ত মেমরি এবং মেমরি-অ্যাক্সেস ব্যবহার করা হয়। নিউমএক্সপ্যাক-লাইব্রেরি নম্পি-অ্যারে টাইল করার চেষ্টা করে এবং এর ফলে আরও ভাল ক্যাশে ব্যবহার পেতে পারে:
# less cache misses than numpy-functionality
import numexpr as ne
def ne_f(x):
return ne.evaluate("x+2*x*x+4*x*x*x")
নিম্নলিখিত তুলনা বাড়ে:
আমি উপরের চক্রান্তের সমস্ত কিছু ব্যাখ্যা করতে পারি না: আমরা শুরুতে numexpr-গ্রন্থাগারের জন্য আরও বড় ওভারহেড দেখতে পারি, তবে এটি ক্যাশে আরও ভালভাবে ব্যবহার করার কারণে এটি বড় অ্যারেগুলির জন্য প্রায় 10 বার দ্রুত!
আর একটি পদ্ধতি হ'ল ফাংশনটি জিট-সংকলন করা এবং এইভাবে সত্যিকারের খাঁটি সি ইউফুঙ্ক পাওয়া। এটি নাম্বার পদ্ধতি:
# runtime generated C-function as ufunc
import numba as nb
@nb.vectorize(target="cpu")
def nb_vf(x):
return x+2*x*x+4*x*x*x
এটি আসল নম্পী-পদ্ধতির চেয়ে 10 গুণ দ্রুত:
তবে, কাজটি বিব্রতকরভাবে সমান্তরাল, সুতরাং আমরা prange
সমান্তরালভাবে লুপটি গণনা করতে ব্যবহার করতে পারি :
@nb.njit(parallel=True)
def nb_par_jitf(x):
y=np.empty(x.shape)
for i in nb.prange(len(x)):
y[i]=x[i]+2*x[i]*x[i]+4*x[i]*x[i]*x[i]
return y
প্রত্যাশিত হিসাবে, সমান্তরাল ফাংশন ছোট ইনপুটগুলির জন্য ধীর, তবে বৃহত আকারের জন্য দ্রুত (প্রায় 2 গুণনীয়):
নাম্বা যখন নিম্পি-অ্যারেগুলির সাথে অপারেশনগুলি অনুকূল করতে বিশেষীকরণ করে তবে সাইথন আরও সাধারণ সরঞ্জাম। নাম্বার মতো একই পারফরম্যান্সটি বের করা আরও জটিল - প্রায়শই এটি এলএলভিএম (নাম্বা) বনাম স্থানীয় সংকলক (জিসিসি / এমএসভিসি) এর নীচে থাকে:
%%cython -c=/openmp -a
import numpy as np
import cython
#single core:
@cython.boundscheck(False)
@cython.wraparound(False)
def cy_f(double[::1] x):
y_out=np.empty(len(x))
cdef Py_ssize_t i
cdef double[::1] y=y_out
for i in range(len(x)):
y[i] = x[i]+2*x[i]*x[i]+4*x[i]*x[i]*x[i]
return y_out
#parallel:
from cython.parallel import prange
@cython.boundscheck(False)
@cython.wraparound(False)
def cy_par_f(double[::1] x):
y_out=np.empty(len(x))
cdef double[::1] y=y_out
cdef Py_ssize_t i
cdef Py_ssize_t n = len(x)
for i in prange(n, nogil=True):
y[i] = x[i]+2*x[i]*x[i]+4*x[i]*x[i]*x[i]
return y_out
সাইথনের ফলাফল কিছুটা ধীর গতিতে:
উপসংহার
স্পষ্টতই, শুধুমাত্র একটি ফাংশনের জন্য পরীক্ষা করা কিছুই প্রমাণ করে না। এছাড়াও একটি মনে রাখা উচিত, যে চুসেন ফাংশন-উদাহরণস্বরূপ, মেমরির ব্যান্ডউইথ ছিল 10 ^ 5 উপাদানগুলির চেয়ে বড় আকারের বোতল ঘাড় - এইভাবে আমরা এই অঞ্চলে নাম্বা, নিউমেক্সপ্রে এবং সিথনের ক্ষেত্রে একই পারফরম্যান্স পেয়েছি।
শেষ পর্যন্ত, আলটিমেটিভ উত্তরটি ফাংশন, হার্ডওয়্যার, পাইথন-বিতরণ এবং অন্যান্য কারণগুলির ধরণের উপর নির্ভর করে। উদাহরণস্বরূপ Anaconda- র-বিতরণের জন্য numpy এর কাজকর্মের জন্য ইন্টেলের VML ব্যবহার করে এবং এইভাবে numba তূলনায় (যদি না তা SVML ব্যবহার করে, এই দেখুন তাই-পোস্ট তুরীয় ফাংশন পছন্দ সহজে জন্য) exp
, sin
, cos
এবং অনুরূপ - যেমন নিম্নলিখিত দেখতে তাই-পোস্ট ।
তবুও এই তদন্ত থেকে এবং এখনও পর্যন্ত আমার অভিজ্ঞতা থেকে, আমি বলব, যতক্ষণ পর্যন্ত কোনও ট্রান্সেন্ডেন্টাল ফাংশন জড়িত না হয়, ততক্ষণ সেরা পারফরম্যান্সের সাথে নাম্বাটিকে সবচেয়ে সহজতম সরঞ্জাম বলে মনে হচ্ছে।
পারফেল্লট- প্যাকেজ সহ চলমান সময় প্লট করা:
import perfplot
perfplot.show(
setup=lambda n: np.random.rand(n),
n_range=[2**k for k in range(0,24)],
kernels=[
f,
vf,
ne_f,
nb_vf, nb_par_jitf,
cy_f, cy_par_f,
],
logx=True,
logy=True,
xlabel='len(x)'
)