diff --git a/.translate/state/numba.md.yml b/.translate/state/numba.md.yml index 5d1dfab..a0e2570 100644 --- a/.translate/state/numba.md.yml +++ b/.translate/state/numba.md.yml @@ -1,6 +1,6 @@ -source-sha: cc9c3256dc35bd277cb25d0089f0a0452c0fa94e -synced-at: "2026-03-20" -model: unknown -mode: RESYNC -section-count: 8 -tool-version: 0.13.1 +source-sha: ab53ebc3d56705b836f0ca8207d70d1ff3ba0936 +synced-at: "2026-04-09" +model: claude-sonnet-4-6 +mode: UPDATE +section-count: 7 +tool-version: 0.14.0 diff --git a/lectures/numba.md b/lectures/numba.md index 30af0d8..913ee7e 100644 --- a/lectures/numba.md +++ b/lectures/numba.md @@ -13,15 +13,15 @@ translation: title: Numba headings: Overview: مروری کلی - Compiling Functions: کامپایل توابع + Compiling Functions: کامپایل کردن توابع Compiling Functions::An Example: یک مثال Compiling Functions::How and When it Works: چگونه و چه زمانی کار می‌کند Decorator Notation: نماد Decorator Type Inference: استنتاج نوع - Compiling Classes: کامپایل کلاس‌ها Dangers and Limitations: خطرات و محدودیت‌ها Dangers and Limitations::Limitations: محدودیت‌ها 'Dangers and Limitations::A Gotcha: Global Variables': 'یک مشکل: متغیرهای سراسری' + Dangers and Limitations::Caching Compiled Code: ذخیره‌سازی کد کامپایل‌شده Multithreaded Loops in Numba: حلقه‌های چندنخی در Numba Exercises: تمرین‌ها --- @@ -55,61 +55,52 @@ import quantecon as qe import matplotlib.pyplot as plt ``` - - ## مروری کلی -در یک {doc}`درس قبلی ` درباره برداری‌سازی یاد گرفتیم، که یک روش برای بهبود سرعت و کارایی در کار عددی است. - -برداری‌سازی شامل ارسال عملیات پردازش آرایه به صورت دسته‌ای به کد کارآمد سطح پایین است. - -با این حال، همانطور که {ref}`قبلاً بحث شد `، برداری‌سازی چندین نقطه ضعف دارد. - -یکی این است که هنگام کار با حجم زیادی از داده‌ها، بسیار حافظه‌بر است. +در یک {doc}`درس قبلی ` درباره برداری‌سازی بحث کردیم، +که می‌تواند سرعت اجرا را با ارسال دسته‌ای عملیات پردازش آرایه به کد کارآمد سطح پایین بهبود بخشد. -دیگری این است که مجموعه الگوریتم‌هایی که می‌توانند به طور کامل برداری شوند، جهانی نیست. +با این حال، همانطور که {ref}`قبلاً بحث شد `، طرح‌های سنتی برداری‌سازی، مانند آنچه در MATLAB، Julia و NumPy یافت می‌شود، چندین نقطه ضعف دارند. -در واقع، برای برخی الگوریتم‌ها، برداری‌سازی ناکارآمد است. +برای مثال، می‌توانند بسیار حافظه‌بر باشند و برای برخی الگوریتم‌ها، برداری‌سازی ناکارآمد یا غیرممکن است. -خوشبختانه، یک کتابخانه جدید Python به نام [Numba](https://numba.pydata.org/) -بسیاری از این مشکلات را حل می‌کند. +یک راه برای دور زدن این مشکلات، استفاده از [Numba](https://numba.pydata.org/) است، یک +**کامپایلر در زمان اجرا (JIT)** برای Python که به سمت کار عددی جهت‌گیری شده است. -این کار را از طریق چیزی به نام **کامپایل در زمان اجرا (JIT compilation)** انجام می‌دهد. +Numba توابع را در حین اجرا به دستورالعمل‌های کد ماشین بومی کامپایل می‌کند. -ایده کلیدی، کامپایل کردن توابع به دستورالعمل‌های کد ماشین بومی در حین اجرا است. +وقتی موفق می‌شود، Numba با کد ماشین زبان‌های سطح پایین برابری خواهد کرد. -وقتی موفق می‌شود، کد کامپایل شده بسیار سریع است. - -فراتر از افزایش سرعت ناشی از کامپایل، Numba به طور خاص برای کار عددی طراحی شده است و می‌تواند ترفندهای دیگری مانند {ref}`چندنخی ` نیز انجام دهد. +علاوه بر این، Numba می‌تواند ترفندهای مفید دیگری نیز انجام دهد، مانند {ref}`چندنخی ` یا +ارتباط با GPU‌ها (از طریق `numba.cuda`). این درس ایده‌های اصلی را معرفی می‌کند. (numba_link)= -## {index}`کامپایل توابع ` +## {index}`کامپایل کردن توابع ` ```{index} single: Python; Numba ``` -همانطور که در بالا گفته شد، استفاده اصلی Numba کامپایل کردن توابع به کد ماشین بومی سریع در طول زمان اجرا است. (quad_map_eg)= ### یک مثال بیایید مسئله‌ای را در نظر بگیریم که برداری‌سازی آن دشوار است: تولید مسیر یک معادله تفاضلی با توجه به یک شرط اولیه. -معادله تفاضلی را نگاشت درجه دو در نظر خواهیم گرفت +معادله تفاضلی را نگاشت درجه دوم در نظر می‌گیریم: $$ -x_{t+1} = \alpha x_t (1 - x_t) + x_{t+1} = \alpha x_t (1 - x_t) $$ -در آنچه در ادامه می‌آید، تنظیم می‌کنیم +در ادامه مقدار زیر را تنظیم می‌کنیم: ```{code-cell} ipython3 α = 4.0 ``` -در اینجا نمودار یک مسیر معمولی است که از $x_0 = 0.1$ شروع می‌شود، با $t$ روی محور x +در اینجا نمودار یک مسیر معمول را مشاهده می‌کنید که از $x_0 = 0.1$ شروع می‌شود و $t$ روی محور x قرار دارد: ```{code-cell} ipython3 def qm(x0, n): @@ -127,7 +118,7 @@ ax.set_ylabel('$x_{t}$', fontsize = 12) plt.show() ``` -برای تسریع تابع `qm` با استفاده از Numba، اولین قدم ما این است +برای تسریع تابع `qm` با استفاده از Numba، اولین گام ما این است: ```{code-cell} ipython3 from numba import jit @@ -135,11 +126,11 @@ from numba import jit qm_numba = jit(qm) ``` -تابع `qm_numba` نسخه‌ای از `qm` است که برای کامپایل JIT "هدف‌گیری" شده است. +تابع `qm_numba` نسخه‌ای از `qm` است که برای کامپایل JIT «هدف‌گذاری» شده است. به زودی توضیح خواهیم داد که این به چه معناست. -بیایید زمان‌سنجی کرده و فراخوانی‌های تابع یکسان را در این دو نسخه مقایسه کنیم، با شروع از تابع اصلی `qm`: +بیایید فراخوانی‌های یکسان تابع را در این دو نسخه زمان‌بندی و مقایسه کنیم، ابتدا با تابع اصلی `qm`: ```{code-cell} ipython3 n = 10_000_000 @@ -149,7 +140,7 @@ with qe.Timer() as timer1: time1 = timer1.elapsed ``` -حالا بیایید qm_numba را امتحان کنیم +حالا بیایید qm_numba را امتحان کنیم: ```{code-cell} ipython3 with qe.Timer() as timer2: @@ -157,9 +148,9 @@ with qe.Timer() as timer2: time2 = timer2.elapsed ``` -این از قبل یک افزایش سرعت بسیار بزرگ است. +این از قبل یک افزایش سرعت بسیار زیاد است. -در واقع، دفعه بعد و تمام دفعات بعدی حتی سریعتر اجرا می‌شود زیرا تابع کامپایل شده و در حافظه است: +در واقع، دفعه بعد و تمام دفعات بعدی حتی سریع‌تر اجرا می‌شود زیرا تابع کامپایل شده و در حافظه قرار دارد: (qm_numba_result)= @@ -170,35 +161,33 @@ time3 = timer3.elapsed ``` ```{code-cell} ipython3 -time1 / time3 # محاسبه افزایش سرعت +time1 / time3 # Calculate speed gain ``` -این نوع افزایش سرعت نسبت به سادگی و وضوح تغییر، چشمگیر است. - ### چگونه و چه زمانی کار می‌کند -Numba تلاش می‌کند کد ماشین سریع با استفاده از زیرساخت ارائه شده توسط [پروژه LLVM](https://llvm.org/) تولید کند. +Numba تلاش می‌کند با استفاده از زیرساختی که [پروژه LLVM](https://llvm.org/) فراهم کرده، کد ماشین سریع تولید کند. -این کار را با استنتاج اطلاعات نوع در حین اجرا انجام می‌دهد. +این کار را با استنتاج اطلاعات نوع به صورت لحظه‌ای انجام می‌دهد. (برای بحث درباره انواع، {doc}`درس قبلی ` ما درباره محاسبات علمی را ببینید.) ایده اصلی این است: -* Python بسیار انعطاف‌پذیر است و از این رو می‌توانیم تابع qm را با انواع مختلف فراخوانی کنیم. - * مثلاً، `x0` می‌تواند یک آرایه NumPy یا یک لیست باشد، `n` می‌تواند یک عدد صحیح یا یک اعشاری باشد و غیره. -* این باعث می‌شود *پیش*-کامپایل کردن تابع (یعنی کامپایل قبل از زمان اجرا) دشوار باشد. -* با این حال، وقتی واقعاً تابع را فراخوانی می‌کنیم، مثلاً با اجرای `qm(0.5, 10)`، انواع `x0` و `n` مشخص می‌شوند. -* علاوه بر این، انواع سایر متغیرها در `qm` می‌توانند پس از مشخص شدن انواع ورودی استنتاج شوند. -* بنابراین استراتژی Numba و سایر کامپایلرهای JIT این است که تا این لحظه صبر کنند و *سپس* تابع را کامپایل کنند. +* پایتون بسیار انعطاف‌پذیر است و از این رو می‌توانیم تابع qm را با انواع مختلفی فراخوانی کنیم. + * مثلاً `x0` می‌تواند یک آرایه NumPy یا یک لیست باشد، `n` می‌تواند یک عدد صحیح یا یک عدد اعشاری باشد و غیره. +* این امر *پیش*-کامپایل کردن تابع (یعنی کامپایل پیش از زمان اجرا) را دشوار می‌سازد. +* اما وقتی واقعاً تابع را فراخوانی می‌کنیم، مثلاً با اجرای `qm(0.5, 10)`، انواع `x0` و `n` مشخص می‌شوند. +* علاوه بر این، انواع *سایر متغیرها* در `qm` *می‌توانند پس از مشخص شدن انواع ورودی استنتاج شوند*. +* بنابراین استراتژی Numba و سایر کامپایلرهای JIT این است که تا این لحظه صبر کنند و سپس تابع را کامپایل کنند. -به همین دلیل است که به آن کامپایل "درست در زمان" می‌گویند. +به همین دلیل است که به آن کامپایل «درست به موقع» (just-in-time) گفته می‌شود. -توجه داشته باشید که اگر فراخوانی `qm(0.5, 10)` را انجام دهید و سپس آن را با `qm(0.9, 20)` دنبال کنید، کامپایل فقط در فراخوانی اول اتفاق می‌افتد. +توجه داشته باشید که اگر `qm(0.5, 10)` را فراخوانی کنید و سپس `qm(0.9, 20)` را دنبال آن بیاورید، کامپایل تنها در اولین فراخوانی انجام می‌شود. -کد کامپایل شده سپس ذخیره و در صورت نیاز بازیافت می‌شود. +این به این دلیل است که کد کامپایل‌شده در حافظه نهان ذخیره می‌شود و در صورت نیاز مجدداً استفاده می‌شود. -به همین دلیل است که در کد بالا، `time3` کوچکتر از `time2` است. +به همین دلیل است که در کد بالا، `time3` کوچک‌تر از `time2` است. ## نماد Decorator @@ -212,9 +201,7 @@ qm_numba = jit(qm) (ما decoratorها را در یک {doc}`درس جداگانه ` بحث می‌کنیم اما می‌توانید جزئیات را در این مرحله رد کنید.) -بیایید ببینیم چگونه این کار انجام می‌شود. - -برای هدف‌گیری یک تابع برای کامپایل JIT، می‌توانیم `@jit` را قبل از تعریف تابع قرار دهیم. +به طور مشخص، برای هدف‌گیری یک تابع برای کامپایل JIT، می‌توانیم `@jit` را قبل از تعریف تابع قرار دهیم. در اینجا نحوه انجام این کار برای `qm` آمده است @@ -256,16 +243,18 @@ Numba همچنین با آرایه‌های NumPy که انواع به خوبی این به آن اجازه می‌دهد تا کد ماشین بومی تولید کند، بدون نیاز به فراخوانی محیط زمان اجرای Python. -در چنین محیطی، Numba با کد ماشین از زبان‌های سطح پایین برابری می‌کند. - وقتی Numba نمی‌تواند تمام اطلاعات نوع را استنتاج کند، خطا ایجاد می‌کند. +```{note} +در نسخه‌های قدیمی‌تر Numba، دکوراتور `@jit` در صورت عدم توانایی در استنتاج همه انواع، به آرامی به «حالت شیء» بازمی‌گشت که سرعت چندانی به همراه نداشت. نسخه‌های فعلی Numba به طور پیش‌فرض از حالت `nopython` استفاده می‌کنند، به این معنا که کامپایلر بر استنتاج کامل نوع اصرار دارد و در صورت شکست، خطا ایجاد می‌کند. اغلب در کدهای دیگر `@njit` را می‌بینید که صرفاً یک نام مستعار برای `@jit(nopython=True)` است. از آنجا که حالت nopython اکنون پیش‌فرض است، `@jit` و `@njit` معادل یکدیگرند. +``` + به عنوان مثال، در تنظیم (مصنوعی) زیر، Numba قادر به تعیین نوع تابع `mean` هنگام کامپایل تابع `bootstrap` نیست ```{code-cell} ipython3 @jit -def bootstrap(data, statistics, n): - bootstrap_stat = np.empty(n) +def bootstrap(data, statistics, n_resamples): + bootstrap_stat = np.empty(n_resamples) n = len(data) for i in range(n_resamples): resample = np.random.choice(data, size=n, replace=True) @@ -297,117 +286,9 @@ with qe.Timer(): bootstrap(data, mean, n_resamples) ``` -## کامپایل کلاس‌ها - -همانطور که در بالا ذکر شد، در حال حاضر Numba فقط می‌تواند زیرمجموعه‌ای از Python را کامپایل کند. - -با این حال، آن زیرمجموعه همیشه در حال گسترش است. - -به ویژه، Numba اکنون در کامپایل کلاس‌ها بسیار موثر است. - -اگر یک کلاس با موفقیت کامپایل شود، متدهای آن به عنوان توابع کامپایل شده JIT عمل می‌کنند. - -برای ارائه یک مثال، بیایید کلاس تحلیل مدل رشد Solow که در {doc}`این درس ` ایجاد کردیم را در نظر بگیریم. - -برای کامپایل این کلاس از decorator `@jitclass` استفاده می‌کنیم: - -```{code-cell} ipython3 -from numba import float64 -from numba.experimental import jitclass -``` - -توجه داشته باشید که چیزی به نام `float64` نیز import کردیم. - -این یک نوع داده است که نشان‌دهنده اعداد اعشاری استاندارد است. - -ما اینجا آن را import می‌کنیم زیرا Numba هنگام تلاش برای برخورد با کلاس‌ها به کمک بیشتری با انواع نیاز دارد. - -در اینجا کد ما است: - -```{code-cell} ipython3 -solow_data = [ - ('n', float64), - ('s', float64), - ('δ', float64), - ('α', float64), - ('z', float64), - ('k', float64) -] - -@jitclass(solow_data) -class Solow: - r""" - مدل رشد Solow را با قانون به‌روزرسانی پیاده‌سازی می‌کند - - k_{t+1} = [(s z k^α_t) + (1 - δ)k_t] /(1 + n) - - """ - def __init__(self, n=0.05, # نرخ رشد جمعیت - s=0.25, # نرخ پس‌انداز - δ=0.1, # نرخ استهلاک - α=0.3, # سهم نیروی کار - z=2.0, # بهره‌وری - k=1.0): # موجودی سرمایه فعلی - - self.n, self.s, self.δ, self.α, self.z = n, s, δ, α, z - self.k = k - - def h(self): - "تابع h را ارزیابی کن" - # پارامترها را باز کن (self را حذف کن تا نماد ساده شود) - n, s, δ, α, z = self.n, self.s, self.δ, self.α, self.z - # قانون به‌روزرسانی را اعمال کن - return (s * z * self.k**α + (1 - δ) * self.k) / (1 + n) - - def update(self): - "وضعیت فعلی (یعنی موجودی سرمایه) را به‌روزرسانی کن." - self.k = self.h() - - def steady_state(self): - "مقدار حالت پایدار سرمایه را محاسبه کن." - # پارامترها را باز کن (self را حذف کن تا نماد ساده شود) - n, s, δ, α, z = self.n, self.s, self.δ, self.α, self.z - # حالت پایدار را محاسبه و برگردان - return ((s * z) / (n + δ))**(1 / (1 - α)) - - def generate_sequence(self, t): - "یک سری زمانی با طول t تولید و برگردان" - path = [] - for i in range(t): - path.append(self.k) - self.update() - return path -``` - -ابتدا انواع داده‌های instance برای کلاس را در `solow_data` مشخص کردیم. - -پس از آن، هدف‌گیری کلاس برای کامپایل JIT فقط نیاز به افزودن `@jitclass(solow_data)` قبل از تعریف کلاس دارد. - -وقتی متدها در کلاس را فراخوانی می‌کنیم، متدها دقیقاً مانند توابع کامپایل می‌شوند. - -```{code-cell} ipython3 -s1 = Solow() -s2 = Solow(k=8.0) - -T = 60 -fig, ax = plt.subplots() - -# مقدار حالت پایدار مشترک سرمایه را رسم کن -ax.plot([s1.steady_state()]*T, 'k-', label='steady state') - -# سری زمانی را برای هر اقتصاد رسم کن -for s in s1, s2: - lb = f'capital series from initial state {s.k}' - ax.plot(s.generate_sequence(T), 'o-', lw=2, alpha=0.6, label=lb) -ax.set_ylabel('$k_{t}$', fontsize=12) -ax.set_xlabel('$t$', fontsize=12) -ax.legend() -plt.show() -``` - ## خطرات و محدودیت‌ها -بیایید موارد بالا را مرور کنیم و برخی یادداشت‌های احتیاطی اضافه کنیم. +بیایید برخی یادداشت‌های احتیاطی اضافه کنیم. ### محدودیت‌ها @@ -417,9 +298,9 @@ plt.show() برای روال‌های بزرگتر، یا برای روال‌هایی که از کتابخانه‌های خارجی استفاده می‌کنند، به راحتی می‌تواند شکست بخورد. -از این رو، هنگام استفاده از Numba، محتاطانه است که روی تسریع قطعات کوچک و حیاتی کد تمرکز کنید. +از این رو، بهتر است روی تسریع قطعات کوچک و حیاتی کد تمرکز کنید. -این به شما عملکرد بسیار بهتری نسبت به پوشاندن برنامه‌های Python خود با عبارت‌های `@njit` می‌دهد. +این به شما عملکرد بسیار بهتری نسبت به پوشاندن برنامه‌های Python خود با عبارت‌های `@jit` می‌دهد. ### یک مشکل: متغیرهای سراسری @@ -447,17 +328,31 @@ print(add_a(10)) وقتی Numba کد ماشین را برای توابع کامپایل می‌کند، متغیرهای سراسری را به عنوان ثابت برای اطمینان از پایداری نوع درمان می‌کند. +### ذخیره‌سازی کد کامپایل‌شده + +به طور پیش‌فرض، Numba در هر بار شروع یک نشست Python جدید، توابع را مجدداً کامپایل می‌کند. + +برای جلوگیری از این سربار، می‌توانید `cache=True` را به دکوراتور ارسال کنید: + +```{code-cell} ipython3 +@jit(cache=True) +def qm(x0, n): + x = np.empty(n+1) + x[0] = x0 + for t in range(n): + x[t+1] = α * x[t] * (1 - x[t]) + return x +``` + +این کد کامپایل‌شده را روی دیسک ذخیره می‌کند تا نشست‌های بعدی بتوانند از مرحله کامپایل صرف‌نظر کنند. + (multithreading)= ## حلقه‌های چندنخی در Numba -علاوه بر کامپایل JIT، Numba پشتیبانی قدرتمندی برای محاسبات موازی در CPUها ارائه می‌دهد. - -با توزیع محاسبات در چندین هسته CPU، می‌توانیم افزایش سرعت قابل توجهی برای بسیاری از الگوریتم‌های عددی به دست آوریم. +علاوه بر کامپایل JIT، Numba پشتیبانی از محاسبات موازی در CPUها ارائه می‌دهد. ابزار کلیدی برای موازی‌سازی در Numba تابع `prange` است که به Numba می‌گوید تا تکرارهای حلقه را به صورت موازی در هسته‌های CPU موجود اجرا کند. -این رویکرد چندنخی برای طیف گسترده‌ای از مسائل در محاسبات علمی و اقتصاد کمی به خوبی کار می‌کند. - برای نمایش، ابتدا به یک قطعه کد ساده تک‌نخی (یعنی غیرموازی) نگاه می‌کنیم. کد، به‌روزرسانی ثروت $w_t$ یک خانوار را از طریق قانون شبیه‌سازی می‌کند @@ -477,18 +372,17 @@ $$ در اینجا کد است: ```{code-cell} ipython3 -from numpy.random import randn -from numba import njit +from numba import jit -@njit +@jit def h(w, r=0.1, s=0.3, v1=0.1, v2=1.0): """ ثروت خانوار را به‌روزرسانی می‌کند. """ # شوک‌ها را بکش - R = np.exp(v1 * randn()) * (1 + r) - y = np.exp(v2 * randn()) + R = np.exp(v1 * np.random.randn()) * (1 + r) + y = np.exp(v2 * np.random.randn()) # ثروت را به‌روزرسانی کن w = R * s * w + y @@ -518,11 +412,13 @@ plt.show() به طور خاص، تعداد زیادی از خانوارها را شبیه‌سازی می‌کنیم و سپس میانه ثروت را برای این گروه محاسبه می‌کنیم. -معلوم می‌شود که برای مشخصاتی که در بالا انتخاب کرده‌ایم، می‌توانیم این را با گرفتن یک عکس یک دوره‌ای از آنچه برای میانه ثروت گروه در پایان یک شبیه‌سازی طولانی اتفاق افتاده محاسبه کنیم. +فرض کنیم به میانگین بلندمدت این میانه در طول زمان علاقه‌مند هستیم. + +برای مشخصاتی که در بالا انتخاب کرده‌ایم، می‌توانیم این را با گرفتن یک مقطع عرضی یک دوره‌ای از میانه ثروت گروه در پایان یک شبیه‌سازی طولانی محاسبه کنیم. علاوه بر این، به شرطی که دوره شبیه‌سازی به اندازه کافی طولانی باشد، شرایط اولیه اهمیتی ندارند. -* این به دلیل چیزی به نام ارگودیسیتی است که [بعداً](https://python.quantecon.org/finite_markov.html#id15) آن را بحث خواهیم کرد. +(این به دلیل [ارگودیسیته](https://python.quantecon.org/finite_markov.html#id15) است.) بنابراین، به طور خلاصه، قصد داریم 50,000 خانوار را شبیه‌سازی کنیم با @@ -534,7 +430,7 @@ plt.show() در اینجا کد است: ```{code-cell} ipython3 -@njit +@jit def compute_long_run_median(w0=1, T=1000, num_reps=50_000): obs = np.empty(num_reps) @@ -561,7 +457,7 @@ with qe.Timer(): ```{code-cell} ipython3 from numba import prange -@njit(parallel=True) +@jit(parallel=True) def compute_long_run_median_parallel(w0=1, T=1000, num_reps=50_000): obs = np.empty(num_reps) @@ -583,7 +479,6 @@ with qe.Timer(): افزایش سرعت قابل توجه است. - ## تمرین‌ها ```{exercise} @@ -603,13 +498,11 @@ with qe.Timer(): در اینجا یک راه‌حل است: ```{code-cell} ipython3 -from random import uniform - @jit def calculate_pi(n=1_000_000): count = 0 for i in range(n): - u, v = uniform(0, 1), uniform(0, 1) + u, v = np.random.uniform(0, 1), np.random.uniform(0, 1) d = np.sqrt((u - 0.5)**2 + (v - 0.5)**2) if d < 0.5: count += 1 @@ -630,9 +523,9 @@ with qe.Timer(): calculate_pi() ``` -اگر کامپایل JIT را با حذف `@njit` خاموش کنیم، کد حدود 150 برابر بیشتر در دستگاه ما طول می‌کشد. +اگر کامپایل JIT را با حذف `@jit` خاموش کنیم، کد حدود 150 برابر بیشتر در دستگاه ما طول می‌کشد. -بنابراین با افزودن چهار کاراکتر، افزایش سرعت 2 مرتبه بزرگی -- که بسیار زیاد است -- به دست می‌آوریم. +بنابراین با افزودن چهار کاراکتر، افزایش سرعت 2 مرتبه بزرگی به دست می‌آوریم. ```{solution-end} ``` @@ -675,7 +568,7 @@ with qe.Timer(): :class: dropdown * حالت پایین را به عنوان 0 و حالت بالا را به عنوان 1 نمایش دهید. -* اگر می‌خواهید اعداد صحیح را در یک آرایه NumPy ذخیره کنید و سپس کامپایل JIT اعمال کنید، از `x = np.empty(n, dtype=np.int_)` استفاده کنید. +* اگر می‌خواهید اعداد صحیح را در یک آرایه NumPy ذخیره کنید و سپس کامپایل JIT اعمال کنید، از `x = np.empty(n, dtype=np.int64)` استفاده کنید. ``` @@ -699,7 +592,7 @@ p, q = 0.1, 0.2 # احتمال خروج از حالت پایین و بالا ب ```{code-cell} ipython3 def compute_series(n): - x = np.empty(n, dtype=np.int_) + x = np.empty(n, dtype=np.int64) x[0] = 1 # در حالت 1 شروع کن U = np.random.uniform(0, 1, size=n) for t in range(1, n): @@ -778,13 +671,11 @@ with qe.Timer(): در اینجا یک راه‌حل است: ```{code-cell} ipython3 -from random import uniform - -@njit(parallel=True) +@jit(parallel=True) def calculate_pi(n=1_000_000): count = 0 for i in prange(n): - u, v = uniform(0, 1), uniform(0, 1) + u, v = np.random.uniform(0, 1), np.random.uniform(0, 1) d = np.sqrt((u - 0.5)**2 + (v - 0.5)**2) if d < 0.5: count += 1 @@ -805,7 +696,7 @@ with qe.Timer(): calculate_pi() ``` -با روشن و خاموش کردن موازی‌سازی (انتخاب `True` یا `False` در annotation `@njit`)، می‌توانیم افزایش سرعتی که چندنخی علاوه بر کامپایل JIT فراهم می‌کند را آزمایش کنیم. +با روشن و خاموش کردن موازی‌سازی (انتخاب `True` یا `False` در annotation `@jit`)، می‌توانیم افزایش سرعتی که چندنخی علاوه بر کامپایل JIT فراهم می‌کند را آزمایش کنیم. در ایستگاه کاری ما، می‌بینیم که موازی‌سازی سرعت اجرا را با ضریب 2 یا 3 افزایش می‌دهد. @@ -889,13 +780,12 @@ $$ ```{code-cell} ipython3 -from numpy.random import randn M = 10_000_000 n, β, K = 20, 0.99, 100 μ, ρ, ν, S0, h0 = 0.0001, 0.1, 0.001, 10, 0 -@njit(parallel=True) +@jit(parallel=True) def compute_call_price_parallel(β=β, μ=μ, S0=S0, @@ -912,10 +802,10 @@ def compute_call_price_parallel(β=β, h = h0 # شبیه‌سازی رو به جلو در زمان for t in range(n): - s = s + μ + np.exp(h) * randn() - h = ρ * h + ν * randn() + s = s + μ + np.exp(h) * np.random.randn() + h = ρ * h + ν * np.random.randn() # و مقدار max{S_n - K, 0} را به current_sum اضافه کن - current_sum += np.maximum(np.exp(s) - K, 0) + current_sum += max(np.exp(s) - K, 0) return β**n * current_sum / M ``` @@ -925,4 +815,4 @@ def compute_call_price_parallel(β=β, اگر روی دستگاهی با CPUهای زیاد هستید، تفاوت باید قابل توجه باشد. ```{solution-end} -``` \ No newline at end of file +```