Skip to content

Commit d185155

Browse files
committed
Update translation: lectures/numba.md
1 parent 7992c86 commit d185155

1 file changed

Lines changed: 50 additions & 197 deletions

File tree

lectures/numba.md

Lines changed: 50 additions & 197 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,14 @@ translation:
1515
Overview: مروری کلی
1616
Compiling Functions: کامپایل کردن توابع
1717
Compiling Functions::An Example: یک مثال
18-
Compiling Functions::How and When it Works: چگونه و چه زمانی کار می‌کند
19-
Decorator Notation: نماد Decorator
20-
Type Inference: استنتاج نوع
21-
Dangers and Limitations: خطرات و محدودیت‌ها
22-
Dangers and Limitations::Limitations: محدودیت‌ها
23-
'Dangers and Limitations::A Gotcha: Global Variables': 'یک مشکل: متغیرهای سراسری'
24-
Dangers and Limitations::Caching Compiled Code: ذخیره‌سازی کد کامپایل‌شده
18+
Compiling Functions::An Example::Base Version: نسخه پایه
19+
Compiling Functions::An Example::Acceleration via Numba: شتاب‌دهی از طریق Numba
20+
Compiling Functions::How and When it Works: چگونگی و زمان عملکرد
2521
Multithreaded Loops in Numba: حلقه‌های چندنخی در Numba
2622
Exercises: تمرین‌ها
2723
---
2824

29-
(speed)=
25+
(numba_lecture)=
3026
```{raw} jupyter
3127
<div id="qe-notebook-header" align="right" style="text-align:right;">
3228
<a href="https://quantecon.org/" title="quantecon.org">
@@ -86,24 +82,22 @@ Numba توابع را در حین اجرا به دستورالعمل‌های ک
8682
(quad_map_eg)=
8783
### یک مثال
8884

89-
بیایید مسئله‌ای را در نظر بگیریم که برداری‌سازی آن دشوار است: تولید مسیر یک معادله تفاضلی با توجه به یک شرط اولیه.
85+
بیایید مسئله‌ای را در نظر بگیریم که برداری‌سازی آن دشوار است (یعنی، واگذار کردن آن به عملیات پردازش آرایه).
9086

91-
معادله تفاضلی را نگاشت درجه دوم در نظر می‌گیریم:
87+
این مسئله شامل تولید مسیر از طریق نگاشت درجه دوم است:
9288

9389
$$
9490
x_{t+1} = \alpha x_t (1 - x_t)
9591
$$
9692

97-
در ادامه مقدار زیر را تنظیم می‌کنیم:
93+
در ادامه، مقدار $\alpha = 4$ را تنظیم می‌کنیم.
9894

99-
```{code-cell} ipython3
100-
α = 4.0
101-
```
95+
#### نسخه پایه
10296

103-
در اینجا نمودار یک مسیر معمول را مشاهده می‌کنید که از $x_0 = 0.1$ شروع می‌شود و $t$ روی محور x قرار دارد:
97+
در اینجا نمودار یک مسیر معمولی آمده است که از $x_0 = 0.1$ شروع می‌شود و $t$ روی محور افقی است:
10498

10599
```{code-cell} ipython3
106-
def qm(x0, n):
100+
def qm(x0, n, α=4.0):
107101
x = np.empty(n+1)
108102
x[0] = x0
109103
for t in range(n):
@@ -118,235 +112,94 @@ ax.set_ylabel('$x_{t}$', fontsize = 12)
118112
plt.show()
119113
```
120114

121-
برای تسریع تابع `qm` با استفاده از Numba، اولین گام ما این است:
122-
123-
```{code-cell} ipython3
124-
from numba import jit
125-
126-
qm_numba = jit(qm)
127-
```
128-
129-
تابع `qm_numba` نسخه‌ای از `qm` است که برای کامپایل JIT «هدف‌گذاری» شده است.
130-
131-
به زودی توضیح خواهیم داد که این به چه معناست.
132-
133-
بیایید فراخوانی‌های یکسان تابع را در این دو نسخه زمان‌بندی و مقایسه کنیم، ابتدا با تابع اصلی `qm`:
115+
بیایید ببینیم برای $n$ بزرگ، اجرای این تابع چقدر طول می‌کشد:
134116

135117
```{code-cell} ipython3
136118
n = 10_000_000
137119
138120
with qe.Timer() as timer1:
139-
qm(0.1, int(n))
140-
time1 = timer1.elapsed
141-
```
142-
143-
حالا بیایید qm_numba را امتحان کنیم:
121+
# Time Python base version
122+
x = qm(0.1, n)
144123
145-
```{code-cell} ipython3
146-
with qe.Timer() as timer2:
147-
qm_numba(0.1, int(n))
148-
time2 = timer2.elapsed
149124
```
150125

151-
این از قبل یک افزایش سرعت بسیار زیاد است.
152-
153-
در واقع، دفعه بعد و تمام دفعات بعدی حتی سریع‌تر اجرا می‌شود زیرا تابع کامپایل شده و در حافظه قرار دارد:
126+
#### شتاب‌دهی از طریق Numba
154127

155-
(qm_numba_result)=
128+
برای افزایش سرعت تابع `qm` با استفاده از Numba، ابتدا تابع `jit` را وارد می‌کنیم:
156129

157130
```{code-cell} ipython3
158-
with qe.Timer() as timer3:
159-
qm_numba(0.1, int(n))
160-
time3 = timer3.elapsed
161-
```
162-
163-
```{code-cell} ipython3
164-
time1 / time3 # Calculate speed gain
131+
from numba import jit
165132
```
166133

167-
### چگونه و چه زمانی کار می‌کند
168-
169-
Numba تلاش می‌کند با استفاده از زیرساختی که [پروژه LLVM](https://llvm.org/) فراهم کرده، کد ماشین سریع تولید کند.
170-
171-
این کار را با استنتاج اطلاعات نوع به صورت لحظه‌ای انجام می‌دهد.
172-
173-
(برای بحث درباره انواع، {doc}`درس قبلی <need_for_speed>` ما درباره محاسبات علمی را ببینید.)
174-
175-
ایده اصلی این است:
176-
177-
* پایتون بسیار انعطاف‌پذیر است و از این رو می‌توانیم تابع qm را با انواع مختلفی فراخوانی کنیم.
178-
* مثلاً `x0` می‌تواند یک آرایه NumPy یا یک لیست باشد، `n` می‌تواند یک عدد صحیح یا یک عدد اعشاری باشد و غیره.
179-
* این امر *پیش*-کامپایل کردن تابع (یعنی کامپایل پیش از زمان اجرا) را دشوار می‌سازد.
180-
* اما وقتی واقعاً تابع را فراخوانی می‌کنیم، مثلاً با اجرای `qm(0.5, 10)`، انواع `x0` و `n` مشخص می‌شوند.
181-
* علاوه بر این، انواع *سایر متغیرها* در `qm` *می‌توانند پس از مشخص شدن انواع ورودی استنتاج شوند*.
182-
* بنابراین استراتژی Numba و سایر کامپایلرهای JIT این است که تا این لحظه صبر کنند و سپس تابع را کامپایل کنند.
183-
184-
به همین دلیل است که به آن کامپایل «درست به موقع» (just-in-time) گفته می‌شود.
185-
186-
توجه داشته باشید که اگر `qm(0.5, 10)` را فراخوانی کنید و سپس `qm(0.9, 20)` را دنبال آن بیاورید، کامپایل تنها در اولین فراخوانی انجام می‌شود.
187-
188-
این به این دلیل است که کد کامپایل‌شده در حافظه نهان ذخیره می‌شود و در صورت نیاز مجدداً استفاده می‌شود.
189-
190-
به همین دلیل است که در کد بالا، `time3` کوچک‌تر از `time2` است.
191-
192-
## نماد Decorator
193-
194-
در کد بالا، نسخه کامپایل شده JIT از `qm` را از طریق فراخوانی ایجاد کردیم
134+
حالا آن را روی `qm` اعمال می‌کنیم و تابع جدیدی تولید می‌کنیم:
195135

196136
```{code-cell} ipython3
197137
qm_numba = jit(qm)
198138
```
199139

200-
در عمل، این معمولاً با استفاده از یک سینتکس جایگزین *decorator* انجام می‌شود.
201-
202-
(ما decoratorها را در یک {doc}`درس جداگانه <python_advanced_features>` بحث می‌کنیم اما می‌توانید جزئیات را در این مرحله رد کنید.)
203-
204-
به طور مشخص، برای هدف‌گیری یک تابع برای کامپایل JIT، می‌توانیم `@jit` را قبل از تعریف تابع قرار دهیم.
140+
تابع `qm_numba` نسخه‌ای از `qm` است که برای کامپایل JIT "هدف‌گذاری" شده است.
205141

206-
در اینجا نحوه انجام این کار برای `qm` آمده است
207-
208-
```{code-cell} ipython3
209-
@jit
210-
def qm(x0, n):
211-
x = np.empty(n+1)
212-
x[0] = x0
213-
for t in range(n):
214-
x[t+1] = α * x[t] * (1 - x[t])
215-
return x
216-
```
217-
218-
این معادل افزودن `qm = jit(qm)` بعد از تعریف تابع است.
219-
220-
موارد زیر اکنون از نسخه jitted استفاده می‌کنند:
142+
به زودی توضیح خواهیم داد که این به چه معناست.
221143

222-
```{code-cell} ipython3
223-
with qe.Timer(precision=4):
224-
qm(0.1, 100_000)
225-
```
144+
بیایید این نسخه جدید را زمان‌سنجی کنیم:
226145

227146
```{code-cell} ipython3
228-
with qe.Timer(precision=4):
229-
qm(0.1, 100_000)
147+
with qe.Timer() as timer2:
148+
# Time jitted version
149+
x = qm_numba(0.1, n)
230150
```
231151

232-
Numba همچنین چندین آرگومان برای decoratorها جهت تسریع محاسبات و ذخیره‌سازی cache توابع ارائه می‌دهد -- [اینجا](https://numba.readthedocs.io/en/stable/user/performance-tips.html) را ببینید.
233-
234-
## استنتاج نوع
235-
236-
استنتاج موفقیت‌آمیز نوع، بخش کلیدی کامپایل JIT است.
237-
238-
همانطور که می‌توانید تصور کنید، استنتاج انواع برای اشیاء ساده Python (مثلاً انواع داده اسکالر ساده مانند اعشاری و صحیح) آسان‌تر است.
239-
240-
Numba همچنین با آرایه‌های NumPy که انواع به خوبی تعریف شده دارند، به خوبی کار می‌کند.
241-
242-
در یک محیط ایده‌آل، Numba می‌تواند تمام اطلاعات نوع لازم را استنتاج کند.
243-
244-
این به آن اجازه می‌دهد تا کد ماشین بومی تولید کند، بدون نیاز به فراخوانی محیط زمان اجرای Python.
152+
این یک افزایش سرعت قابل توجه است.
245153

246-
وقتی Numba نمی‌تواند تمام اطلاعات نوع را استنتاج کند، خطا ایجاد می‌کند.
154+
در واقع، دفعه بعد و تمام دفعات بعدی سریع‌تر اجرا می‌شود، چراکه تابع کامپایل شده و در حافظه قرار دارد:
247155

248-
```{note}
249-
در نسخه‌های قدیمی‌تر Numba، دکوراتور `@jit` در صورت عدم توانایی در استنتاج همه انواع، به آرامی به «حالت شیء» بازمی‌گشت که سرعت چندانی به همراه نداشت. نسخه‌های فعلی Numba به طور پیش‌فرض از حالت `nopython` استفاده می‌کنند، به این معنا که کامپایلر بر استنتاج کامل نوع اصرار دارد و در صورت شکست، خطا ایجاد می‌کند. اغلب در کدهای دیگر `@njit` را می‌بینید که صرفاً یک نام مستعار برای `@jit(nopython=True)` است. از آنجا که حالت nopython اکنون پیش‌فرض است، `@jit` و `@njit` معادل یکدیگرند.
250-
```
251-
252-
به عنوان مثال، در تنظیم (مصنوعی) زیر، Numba قادر به تعیین نوع تابع `mean` هنگام کامپایل تابع `bootstrap` نیست
156+
(qm_numba_result)=
253157

254158
```{code-cell} ipython3
255-
@jit
256-
def bootstrap(data, statistics, n_resamples):
257-
bootstrap_stat = np.empty(n_resamples)
258-
n = len(data)
259-
for i in range(n_resamples):
260-
resample = np.random.choice(data, size=n, replace=True)
261-
bootstrap_stat[i] = statistics(resample)
262-
return bootstrap_stat
263-
264-
# هیچ decorator اینجا نیست.
265-
def mean(data):
266-
return np.mean(data)
267-
268-
data = np.array((2.3, 3.1, 4.3, 5.9, 2.1, 3.8, 2.2))
269-
n_resamples = 10
270-
271-
# این کد خطا ایجاد می‌کند
272-
try:
273-
bootstrap(data, mean, n_resamples)
274-
except Exception as e:
275-
print(e)
159+
with qe.Timer() as timer3:
160+
# Second run
161+
x = qm_numba(0.1, n)
276162
```
277163

278-
می‌توانیم این خطا را در این مورد به راحتی با کامپایل کردن `mean` برطرف کنیم.
164+
در اینجا میزان افزایش سرعت آمده است:
279165

280166
```{code-cell} ipython3
281-
@jit
282-
def mean(data):
283-
return np.mean(data)
284-
285-
with qe.Timer():
286-
bootstrap(data, mean, n_resamples)
167+
timer1.elapsed / timer3.elapsed
287168
```
288169

289-
## خطرات و محدودیت‌ها
170+
این یک بهبود قابل توجه با تغییر اندکی در کد اصلی ماست.
290171

291-
بیایید برخی یادداشت‌های احتیاطی اضافه کنیم.
172+
بیایید درباره نحوه عملکرد آن بحث کنیم.
292173

293-
### محدودیت‌ها
174+
### چگونگی و زمان عملکرد
294175

295-
همانطور که دیدیم، Numba باید اطلاعات نوع را روی تمام متغیرها استنتاج کند تا دستورالعمل‌های سریع سطح ماشین تولید کند.
176+
Numba تلاش می‌کند با استفاده از زیرساخت ارائه‌شده توسط [پروژه LLVM](https://llvm.org/)، کد ماشین سریعی تولید کند.
296177

297-
برای روال‌های ساده، Numba انواع را بسیار خوب استنتاج می‌کند.
178+
این کار را از طریق استنتاج اطلاعات نوع به‌صورت پویا انجام می‌دهد.
298179

299-
برای روال‌های بزرگتر، یا برای روال‌هایی که از کتابخانه‌های خارجی استفاده می‌کنند، به راحتی می‌تواند شکست بخورد.
180+
(برای بحث درباره انواع، به {doc}`درس <need_for_speed>` قبلی ما درباره محاسبات علمی مراجعه کنید.)
300181

301-
از این رو، بهتر است روی تسریع قطعات کوچک و حیاتی کد تمرکز کنید.
302-
303-
این به شما عملکرد بسیار بهتری نسبت به پوشاندن برنامه‌های Python خود با عبارت‌های `@jit` می‌دهد.
304-
305-
### یک مشکل: متغیرهای سراسری
306-
307-
چیز دیگری که هنگام استفاده از Numba باید مراقب آن باشید در اینجا است.
308-
309-
مثال زیر را در نظر بگیرید
310-
311-
```{code-cell} ipython3
312-
a = 1
313-
314-
@jit
315-
def add_a(x):
316-
return a + x
317-
318-
print(add_a(10))
319-
```
320-
321-
```{code-cell} ipython3
322-
a = 2
323-
324-
print(add_a(10))
325-
```
182+
ایده اصلی این است:
326183

327-
توجه داشته باشید که تغییر global هیچ تأثیری بر مقدار برگشتی توسط تابع نداشت.
184+
* پایتون بسیار انعطاف‌پذیر است و از این رو می‌توانیم تابع qm را با انواع مختلفی فراخوانی کنیم.
185+
* به عنوان مثال، `x0` می‌تواند یک آرایه NumPy یا یک لیست باشد، `n` می‌تواند یک عدد صحیح یا اعشاری باشد، و غیره.
186+
* این امر، تولید کد ماشین کارآمد *پیش از اجرا* (یعنی قبل از زمان اجرا) را بسیار دشوار می‌کند.
187+
* اما وقتی واقعاً تابع را *فراخوانی می‌کنیم*، مثلاً با اجرای `qm(0.5, 10)`، نوع‌های `x0`، `α` و `n` مشخص می‌شوند.
188+
* علاوه بر این، نوع‌های *سایر متغیرها* در `qm` *به محض مشخص شدن نوع‌های ورودی قابل استنتاج هستند*.
189+
* بنابراین، استراتژی Numba و سایر کامپایلرهای JIT این است که *صبر کنند تا تابع فراخوانی شود*، و سپس کامپایل کنند.
328190

329-
وقتی Numba کد ماشین را برای توابع کامپایل می‌کند، متغیرهای سراسری را به عنوان ثابت برای اطمینان از پایداری نوع درمان می‌کند.
191+
این روش "کامپایل درست-به-موقع" نامیده می‌شود.
330192

331-
### ذخیره‌سازی کد کامپایل‌شده
193+
توجه داشته باشید که اگر `qm_numba(0.5, 10)` را فراخوانی کنید و سپس `qm_numba(0.9, 20)` را اجرا کنید، کامپایل تنها در اولین فراخوانی انجام می‌شود.
332194

333-
به طور پیش‌فرض، Numba در هر بار شروع یک نشست Python جدید، توابع را مجدداً کامپایل می‌کند.
195+
دلیل این امر آن است که کد کامپایل‌شده در حافظه نهان ذخیره می‌شود و در صورت نیاز مجدداً استفاده می‌شود.
334196

335-
برای جلوگیری از این سربار، می‌توانید `cache=True` را به دکوراتور ارسال کنید:
197+
به همین دلیل است که در کد بالا، اجرای دوم `qm_numba` سریع‌تر است.
336198

337-
```{code-cell} ipython3
338-
@jit(cache=True)
339-
def qm(x0, n):
340-
x = np.empty(n+1)
341-
x[0] = x0
342-
for t in range(n):
343-
x[t+1] = α * x[t] * (1 - x[t])
344-
return x
199+
```{admonition} توضیح
200+
در عمل، به جای نوشتن `qm_numba = jit(qm)`، معمولاً از نحو *دکوراتور* استفاده می‌کنیم و `@jit` را قبل از تعریف تابع قرار می‌دهیم. این معادل افزودن `qm = jit(qm)` پس از تعریف است.
345201
```
346202

347-
این کد کامپایل‌شده را روی دیسک ذخیره می‌کند تا نشست‌های بعدی بتوانند از مرحله کامپایل صرف‌نظر کنند.
348-
349-
(multithreading)=
350203
## حلقه‌های چندنخی در Numba
351204

352205
علاوه بر کامپایل JIT، Numba پشتیبانی از محاسبات موازی در CPUها ارائه می‌دهد.

0 commit comments

Comments
 (0)