From 5996a804d5baea012ba10c92c88ef3270e8432cc Mon Sep 17 00:00:00 2001 From: Matt McKay Date: Mon, 13 Apr 2026 01:10:51 +0100 Subject: [PATCH 1/6] Update translation: lectures/jax_intro.md --- lectures/jax_intro.md | 230 +++++++----------------------------------- 1 file changed, 34 insertions(+), 196 deletions(-) diff --git a/lectures/jax_intro.md b/lectures/jax_intro.md index 7aa18d7..9a04227 100644 --- a/lectures/jax_intro.md +++ b/lectures/jax_intro.md @@ -15,9 +15,9 @@ translation: JAX as a NumPy Replacement: JAX به عنوان جایگزین NumPy JAX as a NumPy Replacement::Similarities: شباهت‌ها JAX as a NumPy Replacement::Differences: تفاوت‌ها - JAX as a NumPy Replacement::Differences::Precision: دقت - JAX as a NumPy Replacement::Differences::Immutability: تغییرناپذیری - JAX as a NumPy Replacement::Differences::A workaround: راه‌حل جایگزین + JAX as a NumPy Replacement::Differences::Speed!: دقت + JAX as a NumPy Replacement::Differences::Precision: تغییرناپذیری + JAX as a NumPy Replacement::Differences::Immutability: راه‌حل جایگزین Functional Programming: برنامه‌نویسی تابعی Functional Programming::Pure functions: توابع خالص Functional Programming::Examples: مثال‌ها @@ -26,18 +26,12 @@ translation: Random numbers::Why explicit random state?: چرا وضعیت تصادفی صریح؟ Random numbers::Why explicit random state?::NumPy's approach: رویکرد NumPy Random numbers::Why explicit random state?::JAX's approach: رویکرد JAX - JIT compilation: کامپایل JIT - JIT compilation::A simple example: یک مثال ساده - JIT compilation::A simple example::With NumPy: با NumPy - JIT compilation::A simple example::With JAX: با JAX - JIT compilation::A simple example::Changing array sizes: تغییر اندازه آرایه‌ها - JIT compilation::Evaluating a more complicated function: ارزیابی یک تابع پیچیده‌تر - JIT compilation::Evaluating a more complicated function::With NumPy: با NumPy - JIT compilation::Evaluating a more complicated function::With JAX: با JAX - JIT compilation::How JIT compilation works: نحوه کار کامپایل JIT - JIT compilation::Compiling the whole function: کامپایل کل تابع - JIT compilation::Compiling non-pure functions: کامپایل توابع غیرخالص - JIT compilation::Summary: خلاصه + JIT Compilation: کامپایل JIT + JIT Compilation::With NumPy: با NumPy + JIT Compilation::With JAX: با JAX + JIT Compilation::Compiling the Whole Function: کامپایل کل تابع + JIT Compilation::How JIT compilation works: نحوه کار کامپایل JIT + JIT Compilation::Compiling non-pure functions: کامپایل توابع غیرخالص Vectorization with `vmap`: برداری‌سازی با `vmap` Vectorization with `vmap`::A simple example: یک مثال ساده Vectorization with `vmap`::Combining transformations: ترکیب تبدیل‌ها @@ -541,111 +535,9 @@ random_sum_jax(key) کامپایلر just-in-time (JIT) JAX اجرا را با تولید کد ماشین کارآمد که با هم اندازه وظیفه و هم سخت‌افزار متفاوت است، تسریع می‌کند. -### یک مثال ساده - -فرض کنید می‌خواهیم تابع کسینوس را در نقاط بسیاری ارزیابی کنیم. - -```{code-cell} -n = 50_000_000 -x = np.linspace(0, 10, n) -``` - -#### با NumPy - -بیایید ابتدا با NumPy امتحان کنیم - -```{code-cell} -with qe.Timer(): - y = np.cos(x) -``` - -و یک بار دیگر. - -```{code-cell} -with qe.Timer(): - y = np.cos(x) -``` - -در اینجا NumPy از یک فایل باینری از پیش ساخته شده، کامپایل شده از کد سطح پایین نوشته شده با دقت، برای اعمال کسینوس به یک آرایه از اعداد اعشاری استفاده می‌کند. - -این فایل باینری با NumPy ارسال می‌شود. - -#### با JAX - -اکنون بیایید با JAX امتحان کنیم. - -```{code-cell} -x = jnp.linspace(0, 10, n) -``` - -بیایید همان روش را زمان‌بندی کنیم. - -```{code-cell} -with qe.Timer(): - y = jnp.cos(x) - jax.block_until_ready(y); -``` - -```{note} -در اینجا، به منظور اندازه‌گیری سرعت واقعی، از متد `block_until_ready` استفاده می‌کنیم تا مفسر را تا زمانی که نتایج محاسبات برگردانده شوند، نگه دارد. - -این امر ضروری است زیرا JAX از ارسال ناهمزمان استفاده می‌کند که به مفسر Python اجازه می‌دهد از محاسبات عددی جلوتر برود. - -برای کدهای زمان‌بندی نشده، می‌توانید خط حاوی `block_until_ready` را حذف کنید. -``` - - -و بیایید دوباره آن را زمان‌بندی کنیم. - - -```{code-cell} -with qe.Timer(): - y = jnp.cos(x) - jax.block_until_ready(y); -``` - -روی GPU، این کد بسیار سریع‌تر از معادل NumPy آن اجرا می‌شود. - -همچنین، معمولاً، اجرای دوم سریع‌تر از اولین اجرا به دلیل کامپایل JIT است. - -این به این دلیل است که حتی توابع داخلی مانند `jnp.cos` نیز JIT-کامپایل می‌شوند --- و اجرای اول شامل زمان کامپایل است. - -چرا JAX می‌خواهد توابع داخلی مانند `jnp.cos` را به جای ارائه نسخه‌های از پیش کامپایل شده، مانند NumPy، JIT-کامپایل کند؟ - -دلیل این است که کامپایلر JIT می‌خواهد روی *اندازه* آرایه در حال استفاده (و همچنین نوع داده) تخصصی شود. - -اندازه برای تولید کد بهینه شده اهمیت دارد زیرا موازی‌سازی کارآمد نیاز به تطبیق اندازه وظیفه با سخت‌افزار موجود دارد. - -به همین دلیل است که JAX منتظر می‌ماند تا اندازه آرایه را قبل از کامپایل ببیند --- که نیاز به یک رویکرد JIT-کامپایل شده به جای ارائه باینری‌های از پیش کامپایل شده دارد. - -#### تغییر اندازه آرایه‌ها - -در اینجا اندازه ورودی را تغییر می‌دهیم و زمان‌های اجرا را مشاهده می‌کنیم. - -```{code-cell} -x = jnp.linspace(0, 10, n + 1) -``` - -```{code-cell} -with qe.Timer(): - y = jnp.cos(x) - jax.block_until_ready(y); -``` - - -```{code-cell} -with qe.Timer(): - y = jnp.cos(x) - jax.block_until_ready(y); -``` - -معمولاً، زمان اجرا افزایش می‌یابد و سپس دوباره کاهش می‌یابد (این روی GPU واضح‌تر خواهد بود). +قدرت کامپایلر JIT JAX در ترکیب با سخت‌افزار موازی را {ref}`بالاتر ` دیدیم، هنگامی که `cos` را روی یک آرایه بزرگ اعمال کردیم. -این به این دلیل است که کامپایلر JIT روی اندازه آرایه تخصصی می‌شود تا موازی‌سازی را بهره‌برداری کند --- و از این رو کد کامپایل شده جدیدی را هنگام تغییر اندازه آرایه تولید می‌کند. - -### ارزیابی یک تابع پیچیده‌تر - -بیایید همان کار را با یک تابع پیچیده‌تر امتحان کنیم. +بیایید همان کار را با یک تابع پیچیده‌تر امتحان کنیم: ```{code-cell} def f(x): @@ -653,7 +545,7 @@ def f(x): return y ``` -#### با NumPy +### با NumPy ابتدا با NumPy امتحان خواهیم کرد @@ -664,10 +556,11 @@ x = np.linspace(0, 10, n) ```{code-cell} with qe.Timer(): + # Time NumPy code y = f(x) ``` -#### با JAX +### با JAX اکنون بیایید دوباره با JAX امتحان کنیم. @@ -677,86 +570,36 @@ with qe.Timer(): def f(x): y = jnp.cos(2 * x**2) + jnp.sqrt(jnp.abs(x)) + 2 * jnp.sin(x**4) - x**2 return y -``` -اکنون بیایید آن را زمان‌بندی کنیم. -```{code-cell} x = jnp.linspace(0, 10, n) ``` +اکنون بیایید آن را زمان‌بندی کنیم. + ```{code-cell} with qe.Timer(): + # First call y = f(x) + # Hold interpreter jax.block_until_ready(y); ``` ```{code-cell} with qe.Timer(): + # Second call y = f(x) + # Hold interpreter jax.block_until_ready(y); ``` نتیجه مشابه مثال `cos` است --- JAX سریع‌تر است، به ویژه در اجرای دوم پس از کامپایل JIT. -علاوه بر این، با JAX، ترفند دیگری در آستین داریم --- می‌توانیم *کل* تابع را JIT-کامپایل کنیم، نه فقط عملیات‌های منفرد. - -### نحوه کار کامپایل JIT - -هنگامی که `jax.jit` را به یک تابع اعمال می‌کنیم، JAX آن را *ردیابی* می‌کند: به جای اجرای فوری عملیات‌ها، دنباله عملیات‌ها را به صورت یک گراف محاسباتی ثبت می‌کند و آن گراف را به کامپایلر [XLA](https://openxla.org/xla) تحویل می‌دهد. - -سپس XLA عملیات‌ها را در یک هسته کامپایل شده واحد بهینه‌سازی و ادغام می‌کند که متناسب با سخت‌افزار موجود (CPU، GPU، یا TPU) طراحی شده است. - -نمودار زیر این خط لوله را برای یک تابع ساده نشان می‌دهد: - -```{code-cell} ipython3 -:tags: [hide-input] - -fig, ax = plt.subplots(figsize=(7, 2)) -ax.set_xlim(-0.2, 7.2) -ax.set_ylim(0.2, 2.2) -ax.axis('off') - -# Boxes for pipeline stages -stages = [ - (0.7, 1.2, "Python\nfunction"), - (2.6, 1.2, "computational\ngraph"), - (4.5, 1.2, "optimized\nkernel"), - (6.4, 1.2, "fast\nexecution"), -] - -colors = ["#e3f2fd", "#fff9c4", "#f3e5f5", "#d4edda"] - -for (x, y, label), color in zip(stages, colors): - box = mpatches.FancyBboxPatch( - (x - 0.7, y - 0.5), 1.4, 1.0, - boxstyle="round,pad=0.15", - facecolor=color, edgecolor="black", linewidth=1.5) - ax.add_patch(box) - ax.text(x, y, label, ha='center', va='center', fontsize=9) - -# Arrows with labels -arrows = [ - (1.4, 1.9, "trace"), - (3.3, 3.8, "XLA"), - (5.2, 5.7, "run"), -] - -for x_start, x_end, label in arrows: - ax.annotate("", xy=(x_end, 1.2), xytext=(x_start, 1.2), - arrowprops=dict(arrowstyle="->", lw=1.5, color="gray")) - ax.text((x_start + x_end) / 2, 1.55, label, - ha='center', fontsize=8, color='gray') - -plt.tight_layout() -plt.show() -``` - -اولین فراخوانی به یک تابع JIT-کامپایل شده سربار کامپایل دارد، اما فراخوانی‌های بعدی با همان شکل‌ها و نوع‌های ورودی از کد کامپایل شده کش‌شده استفاده می‌کنند و با سرعت کامل اجرا می‌شوند. +علاوه بر این، با JAX، ترفند دیگری در آستین داریم --- می‌توانیم کل تابع را JIT-کامپایل کنیم، نه فقط عملیات‌های منفرد. ### کامپایل کل تابع -کامپایلر just-in-time (JIT) JAX می‌تواند اجرا را در درون توابع با ادغام عملیات جبر خطی در یک هسته بهینه شده واحد تسریع کند. +کامپایلر just-in-time (JIT) JAX می‌تواند اجرا را در درون توابع با ادغام عملیات آرایه‌ای در یک هسته بهینه شده واحد تسریع کند. بیایید این را با تابع `f` امتحان کنیم: @@ -766,13 +609,17 @@ f_jax = jax.jit(f) ```{code-cell} with qe.Timer(): + # First run y = f_jax(x) + # Hold interpreter jax.block_until_ready(y); ``` ```{code-cell} with qe.Timer(): + # Second run y = f_jax(x) + # Hold interpreter jax.block_until_ready(y); ``` @@ -780,7 +627,6 @@ with qe.Timer(): برای مثال، کامپایلر می‌تواند چندین فراخوانی به شتاب‌دهنده سخت‌افزاری و ایجاد تعدادی آرایه میانی را حذف کند. - اتفاقاً، نحو رایج‌تر هنگام هدف قرار دادن یک تابع برای کامپایلر JIT این است ```{code-cell} ipython3 @@ -789,6 +635,14 @@ def f(x): pass # put function body here ``` +### نحوه کار کامپایل JIT + +هنگامی که `jax.jit` را به یک تابع اعمال می‌کنیم، JAX آن را *ردیابی* می‌کند: به جای اجرای فوری عملیات‌ها، دنباله عملیات‌ها را به صورت یک گراف محاسباتی ثبت می‌کند و آن گراف را به کامپایلر [XLA](https://openxla.org/xla) تحویل می‌دهد. + +سپس XLA عملیات‌ها را در یک هسته کامپایل شده واحد بهینه‌سازی و ادغام می‌کند که متناسب با سخت‌افزار موجود (CPU، GPU، یا TPU) طراحی شده است. + +اولین فراخوانی به یک تابع JIT-کامپایل شده سربار کامپایل دارد، اما فراخوانی‌های بعدی با همان شکل‌ها و نوع‌های ورودی از کد کامپایل شده کش‌شده استفاده می‌کنند و با سرعت کامل اجرا می‌شوند. + ### کامپایل توابع غیرخالص اکنون که دیدیم کامپایل JIT چقدر قدرتمند می‌تواند باشد، درک رابطه آن با توابع خالص مهم است. @@ -837,22 +691,6 @@ f(x) درس اخلاقی داستان: هنگام استفاده از JAX، توابع خالص بنویسید! -### خلاصه - -اکنون می‌توانیم ببینیم که چرا هم توسعه‌دهندگان و هم کامپایلرها از توابع خالص بهره می‌برند. - -ما توابع خالص را دوست داریم زیرا آنها - -* به تست کمک می‌کنند: هر تابع می‌تواند به صورت جداگانه عمل کند -* رفتار قطعی و از این رو تکرارپذیری را ترویج می‌کنند -* از باگ‌هایی که از تغییر وضعیت مشترک ناشی می‌شوند، جلوگیری می‌کنند - -کامپایلر توابع خالص و برنامه‌نویسی تابعی را دوست دارد زیرا - -* وابستگی‌های داده صریح هستند، که به بهینه‌سازی محاسبات پیچیده کمک می‌کند -* توابع خالص راحت‌تر قابل تمایز هستند (autodiff) -* توابع خالص راحت‌تر موازی‌سازی و بهینه‌سازی می‌شوند (به وضعیت قابل تغییر مشترک وابسته نیستند) - ## برداری‌سازی با `vmap` یکی دیگر از تبدیل‌های قدرتمند JAX، `jax.vmap` است که به‌طور خودکار From 7992c86c4a2dff395d5540aa1009822596a3635e Mon Sep 17 00:00:00 2001 From: Matt McKay Date: Mon, 13 Apr 2026 01:10:52 +0100 Subject: [PATCH 2/6] Update translation: .translate/state/jax_intro.md.yml --- .translate/state/jax_intro.md.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.translate/state/jax_intro.md.yml b/.translate/state/jax_intro.md.yml index d5ece3d..e8f1756 100644 --- a/.translate/state/jax_intro.md.yml +++ b/.translate/state/jax_intro.md.yml @@ -1,6 +1,6 @@ -source-sha: 05ce95691fd97e48da39dd6d58fe032c03e8813d -synced-at: "2026-04-08" +source-sha: 95378b8382b4dbd1cd3e0ffe0e152811894c357f +synced-at: "2026-04-13" model: claude-sonnet-4-6 mode: UPDATE section-count: 7 -tool-version: 0.13.1 +tool-version: 0.14.1 From d185155ed66fff1ba15a07e29514e0dceadf9463 Mon Sep 17 00:00:00 2001 From: Matt McKay Date: Mon, 13 Apr 2026 01:10:52 +0100 Subject: [PATCH 3/6] Update translation: lectures/numba.md --- lectures/numba.md | 247 ++++++++++------------------------------------ 1 file changed, 50 insertions(+), 197 deletions(-) diff --git a/lectures/numba.md b/lectures/numba.md index 913ee7e..9dc5cd9 100644 --- a/lectures/numba.md +++ b/lectures/numba.md @@ -15,18 +15,14 @@ translation: Overview: مروری کلی Compiling Functions: کامپایل کردن توابع Compiling Functions::An Example: یک مثال - Compiling Functions::How and When it Works: چگونه و چه زمانی کار می‌کند - Decorator Notation: نماد Decorator - Type Inference: استنتاج نوع - Dangers and Limitations: خطرات و محدودیت‌ها - Dangers and Limitations::Limitations: محدودیت‌ها - 'Dangers and Limitations::A Gotcha: Global Variables': 'یک مشکل: متغیرهای سراسری' - Dangers and Limitations::Caching Compiled Code: ذخیره‌سازی کد کامپایل‌شده + Compiling Functions::An Example::Base Version: نسخه پایه + Compiling Functions::An Example::Acceleration via Numba: شتاب‌دهی از طریق Numba + Compiling Functions::How and When it Works: چگونگی و زمان عملکرد Multithreaded Loops in Numba: حلقه‌های چندنخی در Numba Exercises: تمرین‌ها --- -(speed)= +(numba_lecture)= ```{raw} jupyter
@@ -86,24 +82,22 @@ Numba توابع را در حین اجرا به دستورالعمل‌های ک (quad_map_eg)= ### یک مثال -بیایید مسئله‌ای را در نظر بگیریم که برداری‌سازی آن دشوار است: تولید مسیر یک معادله تفاضلی با توجه به یک شرط اولیه. +بیایید مسئله‌ای را در نظر بگیریم که برداری‌سازی آن دشوار است (یعنی، واگذار کردن آن به عملیات پردازش آرایه). -معادله تفاضلی را نگاشت درجه دوم در نظر می‌گیریم: +این مسئله شامل تولید مسیر از طریق نگاشت درجه دوم است: $$ x_{t+1} = \alpha x_t (1 - x_t) $$ -در ادامه مقدار زیر را تنظیم می‌کنیم: +در ادامه، مقدار $\alpha = 4$ را تنظیم می‌کنیم. -```{code-cell} ipython3 -α = 4.0 -``` +#### نسخه پایه -در اینجا نمودار یک مسیر معمول را مشاهده می‌کنید که از $x_0 = 0.1$ شروع می‌شود و $t$ روی محور x قرار دارد: +در اینجا نمودار یک مسیر معمولی آمده است که از $x_0 = 0.1$ شروع می‌شود و $t$ روی محور افقی است: ```{code-cell} ipython3 -def qm(x0, n): +def qm(x0, n, α=4.0): x = np.empty(n+1) x[0] = x0 for t in range(n): @@ -118,235 +112,94 @@ ax.set_ylabel('$x_{t}$', fontsize = 12) plt.show() ``` -برای تسریع تابع `qm` با استفاده از Numba، اولین گام ما این است: - -```{code-cell} ipython3 -from numba import jit - -qm_numba = jit(qm) -``` - -تابع `qm_numba` نسخه‌ای از `qm` است که برای کامپایل JIT «هدف‌گذاری» شده است. - -به زودی توضیح خواهیم داد که این به چه معناست. - -بیایید فراخوانی‌های یکسان تابع را در این دو نسخه زمان‌بندی و مقایسه کنیم، ابتدا با تابع اصلی `qm`: +بیایید ببینیم برای $n$ بزرگ، اجرای این تابع چقدر طول می‌کشد: ```{code-cell} ipython3 n = 10_000_000 with qe.Timer() as timer1: - qm(0.1, int(n)) -time1 = timer1.elapsed -``` - -حالا بیایید qm_numba را امتحان کنیم: + # Time Python base version + x = qm(0.1, n) -```{code-cell} ipython3 -with qe.Timer() as timer2: - qm_numba(0.1, int(n)) -time2 = timer2.elapsed ``` -این از قبل یک افزایش سرعت بسیار زیاد است. - -در واقع، دفعه بعد و تمام دفعات بعدی حتی سریع‌تر اجرا می‌شود زیرا تابع کامپایل شده و در حافظه قرار دارد: +#### شتاب‌دهی از طریق Numba -(qm_numba_result)= +برای افزایش سرعت تابع `qm` با استفاده از Numba، ابتدا تابع `jit` را وارد می‌کنیم: ```{code-cell} ipython3 -with qe.Timer() as timer3: - qm_numba(0.1, int(n)) -time3 = timer3.elapsed -``` - -```{code-cell} ipython3 -time1 / time3 # Calculate speed gain +from numba import jit ``` -### چگونه و چه زمانی کار می‌کند - -Numba تلاش می‌کند با استفاده از زیرساختی که [پروژه LLVM](https://llvm.org/) فراهم کرده، کد ماشین سریع تولید کند. - -این کار را با استنتاج اطلاعات نوع به صورت لحظه‌ای انجام می‌دهد. - -(برای بحث درباره انواع، {doc}`درس قبلی ` ما درباره محاسبات علمی را ببینید.) - -ایده اصلی این است: - -* پایتون بسیار انعطاف‌پذیر است و از این رو می‌توانیم تابع qm را با انواع مختلفی فراخوانی کنیم. - * مثلاً `x0` می‌تواند یک آرایه NumPy یا یک لیست باشد، `n` می‌تواند یک عدد صحیح یا یک عدد اعشاری باشد و غیره. -* این امر *پیش*-کامپایل کردن تابع (یعنی کامپایل پیش از زمان اجرا) را دشوار می‌سازد. -* اما وقتی واقعاً تابع را فراخوانی می‌کنیم، مثلاً با اجرای `qm(0.5, 10)`، انواع `x0` و `n` مشخص می‌شوند. -* علاوه بر این، انواع *سایر متغیرها* در `qm` *می‌توانند پس از مشخص شدن انواع ورودی استنتاج شوند*. -* بنابراین استراتژی Numba و سایر کامپایلرهای JIT این است که تا این لحظه صبر کنند و سپس تابع را کامپایل کنند. - -به همین دلیل است که به آن کامپایل «درست به موقع» (just-in-time) گفته می‌شود. - -توجه داشته باشید که اگر `qm(0.5, 10)` را فراخوانی کنید و سپس `qm(0.9, 20)` را دنبال آن بیاورید، کامپایل تنها در اولین فراخوانی انجام می‌شود. - -این به این دلیل است که کد کامپایل‌شده در حافظه نهان ذخیره می‌شود و در صورت نیاز مجدداً استفاده می‌شود. - -به همین دلیل است که در کد بالا، `time3` کوچک‌تر از `time2` است. - -## نماد Decorator - -در کد بالا، نسخه کامپایل شده JIT از `qm` را از طریق فراخوانی ایجاد کردیم +حالا آن را روی `qm` اعمال می‌کنیم و تابع جدیدی تولید می‌کنیم: ```{code-cell} ipython3 qm_numba = jit(qm) ``` -در عمل، این معمولاً با استفاده از یک سینتکس جایگزین *decorator* انجام می‌شود. - -(ما decoratorها را در یک {doc}`درس جداگانه ` بحث می‌کنیم اما می‌توانید جزئیات را در این مرحله رد کنید.) - -به طور مشخص، برای هدف‌گیری یک تابع برای کامپایل JIT، می‌توانیم `@jit` را قبل از تعریف تابع قرار دهیم. +تابع `qm_numba` نسخه‌ای از `qm` است که برای کامپایل JIT "هدف‌گذاری" شده است. -در اینجا نحوه انجام این کار برای `qm` آمده است - -```{code-cell} ipython3 -@jit -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 -``` - -این معادل افزودن `qm = jit(qm)` بعد از تعریف تابع است. - -موارد زیر اکنون از نسخه jitted استفاده می‌کنند: +به زودی توضیح خواهیم داد که این به چه معناست. -```{code-cell} ipython3 -with qe.Timer(precision=4): - qm(0.1, 100_000) -``` +بیایید این نسخه جدید را زمان‌سنجی کنیم: ```{code-cell} ipython3 -with qe.Timer(precision=4): - qm(0.1, 100_000) +with qe.Timer() as timer2: + # Time jitted version + x = qm_numba(0.1, n) ``` -Numba همچنین چندین آرگومان برای decoratorها جهت تسریع محاسبات و ذخیره‌سازی cache توابع ارائه می‌دهد -- [اینجا](https://numba.readthedocs.io/en/stable/user/performance-tips.html) را ببینید. - -## استنتاج نوع - -استنتاج موفقیت‌آمیز نوع، بخش کلیدی کامپایل JIT است. - -همانطور که می‌توانید تصور کنید، استنتاج انواع برای اشیاء ساده Python (مثلاً انواع داده اسکالر ساده مانند اعشاری و صحیح) آسان‌تر است. - -Numba همچنین با آرایه‌های NumPy که انواع به خوبی تعریف شده دارند، به خوبی کار می‌کند. - -در یک محیط ایده‌آل، Numba می‌تواند تمام اطلاعات نوع لازم را استنتاج کند. - -این به آن اجازه می‌دهد تا کد ماشین بومی تولید کند، بدون نیاز به فراخوانی محیط زمان اجرای Python. +این یک افزایش سرعت قابل توجه است. -وقتی Numba نمی‌تواند تمام اطلاعات نوع را استنتاج کند، خطا ایجاد می‌کند. +در واقع، دفعه بعد و تمام دفعات بعدی سریع‌تر اجرا می‌شود، چراکه تابع کامپایل شده و در حافظه قرار دارد: -```{note} -در نسخه‌های قدیمی‌تر Numba، دکوراتور `@jit` در صورت عدم توانایی در استنتاج همه انواع، به آرامی به «حالت شیء» بازمی‌گشت که سرعت چندانی به همراه نداشت. نسخه‌های فعلی Numba به طور پیش‌فرض از حالت `nopython` استفاده می‌کنند، به این معنا که کامپایلر بر استنتاج کامل نوع اصرار دارد و در صورت شکست، خطا ایجاد می‌کند. اغلب در کدهای دیگر `@njit` را می‌بینید که صرفاً یک نام مستعار برای `@jit(nopython=True)` است. از آنجا که حالت nopython اکنون پیش‌فرض است، `@jit` و `@njit` معادل یکدیگرند. -``` - -به عنوان مثال، در تنظیم (مصنوعی) زیر، Numba قادر به تعیین نوع تابع `mean` هنگام کامپایل تابع `bootstrap` نیست +(qm_numba_result)= ```{code-cell} ipython3 -@jit -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) - bootstrap_stat[i] = statistics(resample) - return bootstrap_stat - -# هیچ decorator اینجا نیست. -def mean(data): - return np.mean(data) - -data = np.array((2.3, 3.1, 4.3, 5.9, 2.1, 3.8, 2.2)) -n_resamples = 10 - -# این کد خطا ایجاد می‌کند -try: - bootstrap(data, mean, n_resamples) -except Exception as e: - print(e) +with qe.Timer() as timer3: + # Second run + x = qm_numba(0.1, n) ``` -می‌توانیم این خطا را در این مورد به راحتی با کامپایل کردن `mean` برطرف کنیم. +در اینجا میزان افزایش سرعت آمده است: ```{code-cell} ipython3 -@jit -def mean(data): - return np.mean(data) - -with qe.Timer(): - bootstrap(data, mean, n_resamples) +timer1.elapsed / timer3.elapsed ``` -## خطرات و محدودیت‌ها +این یک بهبود قابل توجه با تغییر اندکی در کد اصلی ماست. -بیایید برخی یادداشت‌های احتیاطی اضافه کنیم. +بیایید درباره نحوه عملکرد آن بحث کنیم. -### محدودیت‌ها +### چگونگی و زمان عملکرد -همانطور که دیدیم، Numba باید اطلاعات نوع را روی تمام متغیرها استنتاج کند تا دستورالعمل‌های سریع سطح ماشین تولید کند. +Numba تلاش می‌کند با استفاده از زیرساخت ارائه‌شده توسط [پروژه LLVM](https://llvm.org/)، کد ماشین سریعی تولید کند. -برای روال‌های ساده، Numba انواع را بسیار خوب استنتاج می‌کند. +این کار را از طریق استنتاج اطلاعات نوع به‌صورت پویا انجام می‌دهد. -برای روال‌های بزرگتر، یا برای روال‌هایی که از کتابخانه‌های خارجی استفاده می‌کنند، به راحتی می‌تواند شکست بخورد. +(برای بحث درباره انواع، به {doc}`درس ` قبلی ما درباره محاسبات علمی مراجعه کنید.) -از این رو، بهتر است روی تسریع قطعات کوچک و حیاتی کد تمرکز کنید. - -این به شما عملکرد بسیار بهتری نسبت به پوشاندن برنامه‌های Python خود با عبارت‌های `@jit` می‌دهد. - -### یک مشکل: متغیرهای سراسری - -چیز دیگری که هنگام استفاده از Numba باید مراقب آن باشید در اینجا است. - -مثال زیر را در نظر بگیرید - -```{code-cell} ipython3 -a = 1 - -@jit -def add_a(x): - return a + x - -print(add_a(10)) -``` - -```{code-cell} ipython3 -a = 2 - -print(add_a(10)) -``` +ایده اصلی این است: -توجه داشته باشید که تغییر global هیچ تأثیری بر مقدار برگشتی توسط تابع نداشت. +* پایتون بسیار انعطاف‌پذیر است و از این رو می‌توانیم تابع qm را با انواع مختلفی فراخوانی کنیم. + * به عنوان مثال، `x0` می‌تواند یک آرایه NumPy یا یک لیست باشد، `n` می‌تواند یک عدد صحیح یا اعشاری باشد، و غیره. +* این امر، تولید کد ماشین کارآمد *پیش از اجرا* (یعنی قبل از زمان اجرا) را بسیار دشوار می‌کند. +* اما وقتی واقعاً تابع را *فراخوانی می‌کنیم*، مثلاً با اجرای `qm(0.5, 10)`، نوع‌های `x0`، `α` و `n` مشخص می‌شوند. +* علاوه بر این، نوع‌های *سایر متغیرها* در `qm` *به محض مشخص شدن نوع‌های ورودی قابل استنتاج هستند*. +* بنابراین، استراتژی Numba و سایر کامپایلرهای JIT این است که *صبر کنند تا تابع فراخوانی شود*، و سپس کامپایل کنند. -وقتی Numba کد ماشین را برای توابع کامپایل می‌کند، متغیرهای سراسری را به عنوان ثابت برای اطمینان از پایداری نوع درمان می‌کند. +این روش "کامپایل درست-به-موقع" نامیده می‌شود. -### ذخیره‌سازی کد کامپایل‌شده +توجه داشته باشید که اگر `qm_numba(0.5, 10)` را فراخوانی کنید و سپس `qm_numba(0.9, 20)` را اجرا کنید، کامپایل تنها در اولین فراخوانی انجام می‌شود. -به طور پیش‌فرض، Numba در هر بار شروع یک نشست Python جدید، توابع را مجدداً کامپایل می‌کند. +دلیل این امر آن است که کد کامپایل‌شده در حافظه نهان ذخیره می‌شود و در صورت نیاز مجدداً استفاده می‌شود. -برای جلوگیری از این سربار، می‌توانید `cache=True` را به دکوراتور ارسال کنید: +به همین دلیل است که در کد بالا، اجرای دوم `qm_numba` سریع‌تر است. -```{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 +```{admonition} توضیح +در عمل، به جای نوشتن `qm_numba = jit(qm)`، معمولاً از نحو *دکوراتور* استفاده می‌کنیم و `@jit` را قبل از تعریف تابع قرار می‌دهیم. این معادل افزودن `qm = jit(qm)` پس از تعریف است. ``` -این کد کامپایل‌شده را روی دیسک ذخیره می‌کند تا نشست‌های بعدی بتوانند از مرحله کامپایل صرف‌نظر کنند. - -(multithreading)= ## حلقه‌های چندنخی در Numba علاوه بر کامپایل JIT، Numba پشتیبانی از محاسبات موازی در CPUها ارائه می‌دهد. From 7f1c42cf277b0db1609ae0467f9f1fd9a16d73d5 Mon Sep 17 00:00:00 2001 From: Matt McKay Date: Mon, 13 Apr 2026 01:10:53 +0100 Subject: [PATCH 4/6] Update translation: .translate/state/numba.md.yml --- .translate/state/numba.md.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.translate/state/numba.md.yml b/.translate/state/numba.md.yml index a0e2570..c7d5b0a 100644 --- a/.translate/state/numba.md.yml +++ b/.translate/state/numba.md.yml @@ -1,6 +1,6 @@ -source-sha: ab53ebc3d56705b836f0ca8207d70d1ff3ba0936 -synced-at: "2026-04-09" +source-sha: 95378b8382b4dbd1cd3e0ffe0e152811894c357f +synced-at: "2026-04-13" model: claude-sonnet-4-6 mode: UPDATE -section-count: 7 -tool-version: 0.14.0 +section-count: 5 +tool-version: 0.14.1 From 16ca96c2a4a9d3c5f60dda878258f2c84c867071 Mon Sep 17 00:00:00 2001 From: Matt McKay Date: Mon, 13 Apr 2026 01:10:53 +0100 Subject: [PATCH 5/6] Update translation: lectures/numpy_vs_numba_vs_jax.md --- lectures/numpy_vs_numba_vs_jax.md | 188 +++++++++++++----------------- 1 file changed, 82 insertions(+), 106 deletions(-) diff --git a/lectures/numpy_vs_numba_vs_jax.md b/lectures/numpy_vs_numba_vs_jax.md index 670a953..1e22a4c 100644 --- a/lectures/numpy_vs_numba_vs_jax.md +++ b/lectures/numpy_vs_numba_vs_jax.md @@ -17,8 +17,6 @@ translation: Vectorized operations::Parallelized Numba: Numba موازی شده Vectorized operations::Vectorized code with JAX: کد برداری شده با JAX Vectorized operations::JAX plus vmap: JAX به علاوه vmap - Vectorized operations::JAX plus vmap::Version 1: نسخه 1 - Vectorized operations::vmap version 2: نسخه 2 vmap Vectorized operations::Summary: خلاصه Sequential operations: عملیات ترتیبی Sequential operations::Numba Version: نسخه Numba @@ -27,7 +25,7 @@ translation: Overall recommendations: توصیه‌های کلی --- -(parallel)= +(numpy_numba_jax)= ```{raw} jupyter
@@ -156,7 +154,7 @@ for x in grid: grid = np.linspace(-3, 3, 3_000) x, y = np.meshgrid(grid, grid) -with qe.Timer(precision=8): +with qe.Timer(): z_max_numpy = np.max(f(x, y)) print(f"NumPy result: {z_max_numpy:.6f}") @@ -179,13 +177,17 @@ def compute_max_numba(grid): for x in grid: for y in grid: z = np.cos(x**2 + y**2) / (1 + x**2 + y**2) - if z > m: - m = z + m = max(m, z) return m +``` + +بیایید آن را آزمایش کنیم: +```{code-cell} ipython3 grid = np.linspace(-3, 3, 3_000) -with qe.Timer(precision=8): +with qe.Timer(): + # First run z_max_numba = compute_max_numba(grid) print(f"Numba result: {z_max_numba:.6f}") @@ -194,13 +196,16 @@ print(f"Numba result: {z_max_numba:.6f}") بیایید دوباره اجرا کنیم تا زمان کامپایل حذف شود. ```{code-cell} ipython3 -with qe.Timer(precision=8): +with qe.Timer(): + # Second run compute_max_numba(grid) ``` -بسته به دستگاه شما، نسخه Numba می‌تواند کمی کندتر یا کمی سریعتر از NumPy باشد. +بسته به دستگاه شما، نسخه Numba ممکن است کندتر یا سریعتر از NumPy باشد. -از یک طرف، NumPy محاسبات کارآمد (مانند Numba) را با مقداری چندنخی (برخلاف این کد Numba) ترکیب می‌کند که مزیتی فراهم می‌کند. +در اکثر موارد، Numba کمی بهتر است. + +از یک طرف، NumPy محاسبات کارآمد را با مقداری چندنخی ترکیب می‌کند که مزیتی فراهم می‌کند. از طرف دیگر، روال Numba از حافظه بسیار کمتری استفاده می‌کند، زیرا ما فقط با یک شبکه یک‌بعدی کار می‌کنیم. @@ -208,8 +213,6 @@ with qe.Timer(precision=8): حالا بیایید موازی‌سازی با Numba را با استفاده از `prange` امتحان کنیم: -در اینجا یک تلاش ساده و *نادرست* آمده است. - ```{code-cell} ipython3 @numba.jit(parallel=True) def compute_max_numba_parallel(grid): @@ -220,57 +223,25 @@ def compute_max_numba_parallel(grid): x = grid[i] y = grid[j] z = np.cos(x**2 + y**2) / (1 + x**2 + y**2) - if z > m: - m = z + m = max(m, z) return m - ``` -معمولاً این نتیجه نادرستی برمی‌گرداند: - -```{code-cell} ipython3 -z_max_parallel_incorrect = compute_max_numba_parallel(grid) -print(f"Numba result: {z_max_parallel_incorrect} 😱") -``` - -دلیل این است که متغیر `m` بین نخ‌ها مشترک است و به درستی کنترل نمی‌شود. - -وقتی چندین نخ سعی می‌کنند همزمان `m` را بخوانند و بنویسند، با یکدیگر تداخل می‌کنند. - -نخ‌ها مقادیر قدیمی `m` را می‌خوانند یا به‌روزرسانی‌های یکدیگر را بازنویسی می‌کنند --- یا `m` هرگز از مقدار اولیه خود به‌روزرسانی نمی‌شود. - -در اینجا یک نسخه با دقت بیشتری نوشته شده است. +در اینجا یک اجرای گرم‌کننده و آزمایش آمده است. ```{code-cell} ipython3 -@numba.jit(parallel=True) -def compute_max_numba_parallel(grid): - n = len(grid) - row_maxes = np.empty(n) - for i in numba.prange(n): - row_max = -np.inf - for j in range(n): - x = grid[i] - y = grid[j] - z = np.cos(x**2 + y**2) / (1 + x**2 + y**2) - if z > row_max: - row_max = z - row_maxes[i] = row_max - return np.max(row_maxes) -``` +with qe.Timer(): + # First run + z_max_parallel = compute_max_numba_parallel(grid) -اکنون بلوک کدی که `for i in numba.prange(n)` روی آن عمل می‌کند بین `i` ها مستقل است. - -هر نخ به یک عنصر جداگانه از آرایه `row_maxes` می‌نویسد و موازی‌سازی ایمن است. - -```{code-cell} ipython3 -z_max_parallel = compute_max_numba_parallel(grid) print(f"Numba result: {z_max_parallel:.6f}") ``` -در اینجا زمان‌بندی آمده است. +در اینجا زمان‌بندی برای نسخه از پیش کامپایل شده آمده است. ```{code-cell} ipython3 -with qe.Timer(precision=8): +with qe.Timer(): + # Second run compute_max_numba_parallel(grid) ``` @@ -284,8 +255,7 @@ with qe.Timer(precision=8): اما تفاوت‌هایی نیز وجود دارد که در اینجا آنها را برجسته می‌کنیم. -بیایید با تابع شروع کنیم. - +بیایید با تابع شروع کنیم که `np` را به `jnp` تغییر می‌دهد و `jax.jit` را اضافه می‌کند. ```{code-cell} ipython3 @jax.jit @@ -299,9 +269,15 @@ def f(x, y): ```{code-cell} ipython3 grid = jnp.linspace(-3, 3, 3_000) x_mesh, y_mesh = jnp.meshgrid(grid, grid) +``` + +حالا بیایید اجرا و زمان‌بندی کنیم -with qe.Timer(precision=8): +```{code-cell} ipython3 +with qe.Timer(): + # First run z_max = jnp.max(f(x_mesh, y_mesh)) + # Hold interpreter z_max.block_until_ready() print(f"Plain vanilla JAX result: {z_max:.6f}") @@ -310,8 +286,10 @@ print(f"Plain vanilla JAX result: {z_max:.6f}") بیایید دوباره اجرا کنیم تا زمان کامپایل حذف شود. ```{code-cell} ipython3 -with qe.Timer(precision=8): +with qe.Timer(): + # Second run z_max = jnp.max(f(x_mesh, y_mesh)) + # Hold interpreter z_max.block_until_ready() ``` @@ -339,14 +317,14 @@ x_mesh.nbytes + y_mesh.nbytes خوشبختانه، JAX رویکرد متفاوتی را با استفاده از [jax.vmap](https://docs.jax.dev/en/latest/_autosummary/jax.vmap.html) می‌پذیرد. -#### نسخه 1 +ایده `vmap` این است که برداری‌سازی را به مراحل تقسیم کند و تابعی که روی مقادیر منفرد عمل می‌کند را به تابعی تبدیل کند که روی آرایه‌ها عمل می‌کند. -در اینجا یک راه برای اعمال `vmap` آمده است. +در اینجا نحوه اعمال آن به مسئله ما آمده است. ```{code-cell} ipython3 -# f را تنظیم کنید تا f(x, y) را در هر x برای هر y داده شده محاسبه کند +# Set up f to compute f(x, y) at every x for any given y f_vec_x = lambda y: f(grid, y) -# یک تابع دوم ایجاد کنید که این عملیات را روی تمام y برداری کند +# Create a second function that vectorizes this operation over all y f_vec = jax.vmap(f_vec_x) ``` @@ -355,7 +333,7 @@ f_vec = jax.vmap(f_vec_x) بیایید زمان‌بندی را ببینیم: ```{code-cell} ipython3 -with qe.Timer(precision=8): +with qe.Timer(): z_max = jnp.max(f_vec(grid)) z_max.block_until_ready() @@ -363,41 +341,29 @@ print(f"JAX vmap v1 result: {z_max:.6f}") ``` ```{code-cell} ipython3 -with qe.Timer(precision=8): +with qe.Timer(): z_max = jnp.max(f_vec(grid)) z_max.block_until_ready() ``` -با اجتناب از آرایه‌های ورودی بزرگ `x_mesh` و `y_mesh`، این نسخه `vmap` از حافظه بسیار کمتری استفاده می‌کند. - -وقتی روی CPU اجرا می‌شود، زمان اجرای آن شبیه به نسخه meshgrid است. - -وقتی روی GPU اجرا می‌شود، معمولاً به طور قابل توجهی سریعتر است. - -در واقع، استفاده از `vmap` مزیت دیگری دارد: به ما اجازه می‌دهد برداری‌سازی را به مراحل تقسیم کنیم. - -این منجر به کدی می‌شود که اغلب راحت‌تر از کد برداری شده سنتی قابل درک است. +با اجتناب از آرایه‌های ورودی بزرگ `x_mesh` و `y_mesh`، این نسخه `vmap` از حافظه بسیار کمتری استفاده می‌کند و زمان اجرا نیز تغییر چندانی نمی‌کند. -ما این ایده‌ها را بیشتر هنگام حل مسائل بزرگتر بررسی خواهیم کرد. +این خوب است --- اما هنوز از دستاوردهای سرعت بهره نمی‌بریم! -### نسخه 2 vmap +اول توجه کنید که کد بالا آرایه دوبعدی کامل `f(x,y)` را محاسبه می‌کند که پیش از گرفتن حداکثر، سربارهایی ایجاد می‌کند. -می‌توانیم با استفاده از vmap همچنان کارآمدتر از نظر حافظه باشیم. +دوم، فراخوانی `jnp.max` خارج از تابع JIT-compiled شده `f` قرار دارد، بنابراین کامپایلر نمی‌تواند این عملیات را در یک kernel واحد ادغام کند. -در حالی که در نسخه قبلی از آرایه‌های ورودی بزرگ اجتناب می‌کنیم، هنوز آرایه خروجی بزرگ `f(x,y)` را قبل از محاسبه حداکثر ایجاد می‌کنیم. - -بیایید یک رویکرد کمی متفاوت را امتحان کنیم که max را به داخل می‌برد. - -به دلیل این تغییر، ما هرگز آرایه دوبعدی `f(x,y)` را محاسبه نمی‌کنیم. +می‌توانیم هر دو مشکل را با انتقال max به داخل و پوشش دادن همه چیز در یک `@jax.jit` واحد برطرف کنیم: ```{code-cell} ipython3 @jax.jit -def compute_max_vmap_v2(grid): - # یک تابع بسازید که حداکثر را در امتداد هر سطر بگیرد +def compute_max_vmap(grid): + # Construct a function that takes the max along each row f_vec_x_max = lambda y: jnp.max(f(grid, y)) - # تابع را برداری کنید تا بتوانیم روی تمام سطرها همزمان فراخوانی کنیم + # Vectorize the function so we can call on all rows simultaneously f_vec_max = jax.vmap(f_vec_x_max) - # تابع برداری شده را فراخوانی کنید و حداکثر را بگیرید + # Call the vectorized function and take the max return jnp.max(f_vec_max(grid)) ``` @@ -408,24 +374,32 @@ def compute_max_vmap_v2(grid): ما این تابع را روی تمام سطرها اعمال می‌کنیم و سپس حداکثر max های سطر را می‌گیریم. +از آنجایی که max را به داخل انتقال می‌دهیم، هرگز آرایه دوبعدی کامل `f(x,y)` را نمی‌سازیم و حافظه بیشتری ذخیره می‌کنیم. + +و از آنجایی که همه چیز زیر یک `@jax.jit` واحد است، کامپایلر می‌تواند تمام عملیات را در یک kernel بهینه ادغام کند. + بیایید آن را امتحان کنیم. ```{code-cell} ipython3 -with qe.Timer(precision=8): - z_max = compute_max_vmap_v2(grid).block_until_ready() +with qe.Timer(): + # First run + z_max = compute_max_vmap(grid) + # Hold interpreter + z_max.block_until_ready() -print(f"JAX vmap v2 result: {z_max:.6f}") +print(f"JAX vmap result: {z_max:.6f}") ``` بیایید دوباره اجرا کنیم تا زمان کامپایل حذف شود: ```{code-cell} ipython3 -with qe.Timer(precision=8): - z_max = compute_max_vmap_v2(grid).block_until_ready() +with qe.Timer(): + # Second run + z_max = compute_max_vmap(grid) + # Hold interpreter + z_max.block_until_ready() ``` -اگر این را روی GPU اجرا می‌کنید، همانطور که ما این کار را می‌کنیم، باید افزایش سرعت قابل توجه دیگری را ببینید. - ### خلاصه به نظر ما، JAX برنده برای عملیات برداری شده است. @@ -467,14 +441,16 @@ def qm(x0, n, α=4.0): ```{code-cell} ipython3 n = 10_000_000 -with qe.Timer(precision=8): +with qe.Timer(): + # First run x = qm(0.1, n) ``` بیایید دوباره اجرا کنیم تا زمان کامپایل حذف شود: ```{code-cell} ipython3 -with qe.Timer(precision=8): +with qe.Timer(): + # Second run x = qm(0.1, n) ``` @@ -493,7 +469,7 @@ Numba این عملیات ترتیبی را به طور بسیار کارآمد ```{code-cell} ipython3 cpu = jax.devices("cpu")[0] -@partial(jax.jit, static_argnums=(1,), device=cpu) +@partial(jax.jit, static_argnames=('n',), device=cpu) def qm_jax(x0, n, α=4.0): def update(x, t): x_new = α * x * (1 - x) @@ -506,32 +482,32 @@ def qm_jax(x0, n, α=4.0): این کد خواندن آسانی ندارد اما، در اصل، `lax.scan` به طور مکرر `update` را فراخوانی می‌کند و بازگشت‌های `x_new` را در یک آرایه جمع می‌کند. ```{note} -خوانندگان تیزبین متوجه خواهند شد که ما `device=cpu` را در decorator `jax.jit` مشخص می‌کنیم. - -محاسبه از بسیاری عملیات ترتیبی کوچک تشکیل شده است که فرصت کمی برای بهره‌برداری GPU از موازی‌سازی باقی می‌گذارد. - -در نتیجه، سربار راه‌اندازی kernel تمایل دارد روی GPU غالب شود و CPU را متناسب‌تر برای این بار کاری می‌کند. - -خوانندگان کنجکاو می‌توانند حذف این گزینه را امتحان کنند تا ببینند چگونه عملکرد تغییر می‌کند. +ما `device=cpu` را در decorator `jax.jit` مشخص می‌کنیم زیرا این محاسبه از بسیاری عملیات ترتیبی کوچک تشکیل شده است که فرصت کمی برای بهره‌برداری GPU از موازی‌سازی باقی می‌گذارد. در نتیجه، سربار راه‌اندازی kernel تمایل دارد روی GPU غالب شود و CPU را متناسب‌تر برای این بار کاری می‌کند. ``` بیایید آن را با همان پارامترها زمان‌بندی کنیم: ```{code-cell} ipython3 -with qe.Timer(precision=8): - x_jax = qm_jax(0.1, n).block_until_ready() +with qe.Timer(): + # First run + x_jax = qm_jax(0.1, n) + # Hold interpreter + x_jax.block_until_ready() ``` بیایید دوباره اجرا کنیم تا سربار کامپایل حذف شود: ```{code-cell} ipython3 -with qe.Timer(precision=8): - x_jax = qm_jax(0.1, n).block_until_ready() +with qe.Timer(): + # Second run + x_jax = qm_jax(0.1, n) + # Hold interpreter + x_jax.block_until_ready() ``` JAX نیز برای این عملیات ترتیبی کاملاً کارآمد است. -هم JAX و هم Numba عملکرد قوی پس از کامپایل ارائه می‌دهند، با این که Numba معمولاً (اما نه همیشه) سرعت‌های کمی بهتری در عملیات کاملاً ترتیبی ارائه می‌دهد. +هم JAX و هم Numba عملکرد قوی پس از کامپایل ارائه می‌دهند. ### خلاصه @@ -545,7 +521,7 @@ JAX نیز برای این عملیات ترتیبی کاملاً کارآمد علاوه بر این، آرایه‌های تغییرناپذیر JAX به این معنی است که نمی‌توانیم به سادگی عناصر آرایه را در جا به‌روزرسانی کنیم و تکرار مستقیم الگوریتم مورد استفاده توسط Numba را سخت می‌کند. -برای این نوع عملیات ترتیبی، Numba برنده واضح از نظر وضوح کد و سهولت پیاده‌سازی، و همچنین عملکرد بالا است. +برای این نوع عملیات ترتیبی، Numba برنده واضح از نظر وضوح کد و سهولت پیاده‌سازی است. ## توصیه‌های کلی From 085025f6b4d8c3dc3dfcda1e710a009f482c5853 Mon Sep 17 00:00:00 2001 From: Matt McKay Date: Mon, 13 Apr 2026 01:10:53 +0100 Subject: [PATCH 6/6] Update translation: .translate/state/numpy_vs_numba_vs_jax.md.yml --- .translate/state/numpy_vs_numba_vs_jax.md.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.translate/state/numpy_vs_numba_vs_jax.md.yml b/.translate/state/numpy_vs_numba_vs_jax.md.yml index 78c2946..34ec88f 100644 --- a/.translate/state/numpy_vs_numba_vs_jax.md.yml +++ b/.translate/state/numpy_vs_numba_vs_jax.md.yml @@ -1,6 +1,6 @@ -source-sha: 05ce95691fd97e48da39dd6d58fe032c03e8813d -synced-at: "2026-04-08" +source-sha: 95378b8382b4dbd1cd3e0ffe0e152811894c357f +synced-at: "2026-04-13" model: claude-sonnet-4-6 mode: UPDATE section-count: 3 -tool-version: 0.13.1 +tool-version: 0.14.1