σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
uantCraft Academyخرائط الطريقالمسارات
Ctrl K
AO
+1.2k متعلّم
تسجيل الدخولانضم ↗
خرائط الطريقالمسارات
AO
+1.2k متعلّم
تسجيل الدخول
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
uantCraft Academyخرائط الطريقالمسارات
Ctrl K
AO
+1.2k متعلّم
تسجيل الدخولانضم ↗
خرائط الطريقالمسارات
AO
+1.2k متعلّم
تسجيل الدخول
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
مسار Cache locality›الوحدة 3 · الجهاز بصورة أوسع›الموضوع 3.6·~55 دمتقدم

التنبؤ بالتفرعات

الموضع في المسارالوحدة 3 من 3 · الموضوع 6 من 7
3.6 التنبؤ بالتفرعات
—درجة المختبر— / 100إجمالي XP—التتابع—

4 دقيقة قراءة3 أمثلة برمجية

  1. 01لماذا يخمّن المعالج
  2. 02متنبئات تتذكر
  3. 03كم يكلّف الخطأ ولماذا يفيد الفرز
  4. 04ما ستبنيه

لا ينتظر المعالج حتى يعرف أي اتجاه سيسلكه if: إنه يخمّن ويبدأ العمل. التخمين الصحيح مجاني والخاطئ يرمي العمل، أي ضياع نحو 15 دورة. وتتوقف نسبة الصواب على البيانات: الحلقة نفسها على عوائد SPY نفسها تكلّف 8.3 مرة أقل من الدورات بعد فرز العوائد.

01

لماذا يخمّن المعالج

يعمل المعالج كخط تجميع: بينما تنفَّذ تعليمة تكون التي تليها قد جُلبت وفُكَّ ترميزها. يكسر if الخط لأن التعليمة التالية تعتمد على جوابه وهو لم يجهز بعد. لذلك يجري المعالج تنبؤًا بالتفرع ويتقدم بناءً عليه. إن صح التخمين لا يضيع شيء. وإن أخطأ يُرمى العمل المنجز على المسار الخاطئ ويعاد تشغيل الخط، وهذا يكلّف نحو 15 دورة في نواة حديثة أي بقدر زيارة الذاكرة المخبئية من المستوى الثاني. نكتب نتيجة التفرع True حين يُؤخذ. وفي حلقة تداول يكون التفرع مثلًا if daily_return > 0 وتدفق نتائجه هو ببساطة True أو False لكل يوم.

main.pyPython
def taken_sequence(values, threshold):    return [bool(value > threshold) for value in values]def always_taken_mispredicts(outcomes):    return sum(1 for outcome in outcomes if not outcome)returns = [0.004, -0.012, 0.007, 0.001, -0.003, 0.009]outcomes = taken_sequence(returns, 0)print(outcomes)print(always_taken_mispredicts(outcomes), "misses out of", len(outcomes))
[True, False, True, True, False, True]
2 misses out of 6

من ستة أيام كان لأربعة منها عائد موجب، فيُؤخذ التفرع أربع مرات ولا يُؤخذ مرتين. أبسط متنبئ، مأخوذ دائمًا، يخطئ بالضبط في النتيجتين False. إنه يصيب بقدر ما يُؤخذ التفرع ويتجاهل التاريخ.

02

متنبئات تتذكر

تستخدم المعالجات الحقيقية التاريخ. يتذكر متنبئ البت الواحد ما فعله التفرع آخر مرة ويخمّن الشيء نفسه. وهو جيد حين تأتي النتائج في سلاسل لكنه يدفع مرتين عن كل نتيجة شاذة: مرة حين تصل ومرة حين تُستأنف السلسلة. ويعالج متنبئ البتين ذلك بعدّاد من 0 إلى 3 يقف عند الطرفين. يخمّن «مأخوذ» عند 2 و3 ويتحرك خطوة نحو كل نتيجة، ولذلك يحتاج نتيجتين متتاليتين ضد تخمينه ليغيّر رأيه. نبدأ تخمين البت الواحد عند «غير مأخوذ» والعدّاد عند 1.

main.pyPython
def one_bit_mispredicts(outcomes):    guess = False    wrong = 0    for outcome in outcomes:        if guess != outcome:            wrong += 1        guess = outcome    return wrongdef two_bit_mispredicts(outcomes):    counter = 1    wrong = 0    for outcome in outcomes:        if (counter >= 2) != outcome:            wrong += 1        counter = min(3, counter + 1) if outcome else max(0, counter - 1)1    return wrongruns = [True] * 4 + [False] * 4 + [True] * 4stray = [True] * 6 + [False] + [True] * 6for name, stream in (("runs", runs), ("one stray", stray)):    print(name, "| 1-bit", one_bit_mispredicts(stream), "| 2-bit", two_bit_mispredicts(stream))
runs | 1-bit 3 | 2-bit 5
one stray | 1-bit 3 | 2-bit 2
  1. 1يتحرك العدّاد خطوة نحو النتيجة ويقف عند 0 و3: هذا ما يجعله مشبعًا.

في سلاسل الأربعة يخطئ المتنبئان ثلاث مرات (التخمين الأول والتحولان). ومع False شاذ واحد بين نتائج مأخوذة يخطئ متنبئ البت الواحد ثلاث مرات ومتنبئ البتين مرة واحدة فقط لأن العدّاد ينزل من 3 إلى 2 ويظل يخمّن «مأخوذ». ولهذا تفضّل المعالجات الحقيقية العدّادات. وتذكّر الجانب الآخر: حين لا يكون للنتائج نمط لا يستطيع أي متنبئ أن يتفوق على تخمين الجواب الأكثر تكرارًا.

التنبؤ يخص البيانات لا الشيفرة

نفس if رخيص على بيانات يمكن التنبؤ بها ومكلف على بيانات عشوائية. لا تعِد كتابة تفرع ليصبح «أسرع» قبل أن تعرف شكل نتائجه في البيانات الحقيقية؛ قِس الإخفاقات أولًا.

03

كم يكلّف الخطأ ولماذا يفيد الفرز

لتحويل الإخفاقات إلى زمن نجمعها: كل تفرع يكلّف دورة أساسية واحدة وكل خطأ يضيف عقوبة 15. والآن التجربة التي سُمّي بها هذا الدرس: خذ أول 4000 عائد يومي لـSPY ونفّذ التفرع return > 0 مرتين، على العوائد بترتيب التواريخ وعلى العوائد نفسها بعد الفرز. الفرز لا يغيّر الأرقام بل ترتيبها فقط، لكنه يحوّل النتائج من رمي عملة إلى سلسلتين طويلتين: كل الـFalse أولًا وكل الـTrue بعدها.

main.pyPython
def cycles(n_branches, mispredicts, penalty=PENALTY, base=BASE_CYCLES):    return base * n_branches + penalty * mispredictsdef sorted_stream(values, threshold):    return taken_sequence(sorted(values), threshold)returns = [float(r) for r in load_bars()["close"].pct_change().dropna()][:4000]for name, stream in (("unsorted", taken_sequence(returns, 0)), ("sorted", sorted_stream(returns, 0))):    wrong = two_bit_mispredicts(stream)    print(name, "| always", always_taken_mispredicts(stream), "| 1-bit", one_bit_mispredicts(stream), "| 2-bit", wrong, "|", cycles(len(stream), wrong), "cycles")
unsorted | always 1800 | 1-bit 2035 | 2-bit 1950 | 33250 cycles
sorted | always 1800 | 1-bit 1 | 2-bit 2 | 4030 cycles

بترتيب التواريخ 2200 من 4000 عائد موجبة، فيُؤخذ التفرع 55% من الوقت ويقفز من جواب إلى آخر بلا نمط تقريبًا: «المأخوذ دائمًا» يخطئ 1800 مرة ومتنبئ البت الواحد 2035 ومتنبئ البتين 1950. المتنبئات ذات الذاكرة أسوأ حتى من التخمين البسيط لأنها تتبع كل انقلاب. وبعد الفرز تكون النتائج 1800 False يتبعها 2200 True ويخطئ متنبئ البتين مرتين فقط. تنخفض الكلفة من 33250 إلى 4030 دورة أي أقل بـ8.3 مرة بالبيانات نفسها والحلقة نفسها.

تعريفعقوبة سوء التنبؤ

الدورات الضائعة حين يخطئ المعالج في تخمين تفرع: يتخلص من العمل المنجز على المسار الخاطئ ويعيد ملء الخط، نحو 15 دورة في نواة حديثة.

الحيلة نفسها في NumPy وC++

المقارنة المتجهية مثل returns > 0 ليس فيها تفرع لكل عنصر فلا تعاني من هذا إطلاقًا. وفي C++ حلقة فيها if على بيانات عشوائية هي الحالة الكلاسيكية التي يجعلها فيها فرز البيانات أولًا أو كتابة الشرط كحساب أسرع عدة مرات.

ما تقوله البيانات الحقيقية4000 عائد يومي لـSPY، التفرع return > 0، عقوبة 15 دورة
مأخوذ دائمًا بترتيب التواريخ1,800خطأ من 4000
البتان بترتيب التواريخ1,950خطأ، أسوأ من المأخوذ دائمًا
البتان بعد الفرز2خطآن: سلسلة ثم الأخرى
كسب الفرز8.3×دورات أقل (33250 ← 4030)
دورات متنبئ البتين
ترتيب التواريخ33250
مفروزة4030
04

ما ستبنيه

يتبع المختبر الدرس: نتائج تفرع والمتنبئات الثلاثة والكلفة بالدورات والتدفق المفروز ودراسة لعوائد SPY بترتيب التواريخ وبعد الفرز.

  • الدالة · السؤالتعتمد على
  • taken_sequence(values, threshold)ماذا يفعل التفرع لكل قيمة؟حساب بسيط
  • always_taken_mispredicts(outcomes)كم مرة يخطئ «المأخوذ دائمًا»؟حساب بسيط
  • one_bit_mispredicts(outcomes)وماذا عن متنبئ يتذكر آخر نتيجة؟حساب بسيط
  • two_bit_mispredicts(outcomes)وماذا عن عدّاد مشبع؟حساب بسيط
  • cycles(n_branches, mispredicts)كم يكلّف كل ذلك؟حساب بسيط
  • sorted_stream(values, threshold)وماذا لو فُرزت القيم أولًا؟taken_sequence
  • study(bars)كم يوفّر الفرز في SPY؟sorted_streamtwo_bit_mispredictscycles

أهم الخلاصات

  • المعالج يخمّن كل `if`. التخمين الصحيح مجاني والخاطئ يكلّف نحو 15 دورة، ولا يتفوق متنبئ ذو ذاكرة على التخمين البسيط إلا حين تأتي النتائج في سلاسل.
  • العدّاد يتفوق على بت واحد. يتجاهل عدّاد البتين نتيجة شاذة واحدة بينما يدفع متنبئ البت الواحد مرتين.
  • الترتيب يغيّر الكلفة. تكلّف العوائد الـ4000 نفسها 33250 دورة بترتيب التواريخ و4030 بعد الفرز لأن الفرز يحوّل رمي العملة إلى سلسلتين.
تحقق سريع
لتفرع النتائج True ×4 ثم False ×4 ثم True ×4. يبدأ متنبئ البت الواحد عند «غير مأخوذ». كم مرة يخطئ؟
اختر إجابة.

الموضوع التالي: يجمع المشروع الختامي المهارة كلها. تُعطى جدول تيكات وحملًا يقرؤه فتعيد تخطيطه صفوفًا أو أعمدة أو كتلًا وتعدّ إخفاقات الذاكرة المخبئية لكل تخطيط بالذاكرة التي بنيتها وتقدّر كلفتها بنموذج الكمون من الموضوع الأول وتثبت أي تخطيط يفوز.

المختبر: ما الذي يُقيَّم

7 مهام · 100 نقطة
  • نتائج التفرع5 نقطة
  • مأخوذ دائمًا10 نقطة
  • متنبئ بت واحد15 نقطة
  • عدّاد مشبع من بتين20 نقطة
  • الكلفة بالدورات10 نقطة
  • قيم مفروزة10 نقطة
  • الدراسة على جدول SPY30 نقطة
المستكشف · lab-cl-12
▾ lab-cl-12
▾ data
…
bash · داخل المتصفح
learner@quantcraft:~/lab-cl-12$
لم يتم تحميل Python بعدPythonLn 1, Col 1Spaces: 4UTF-8

هل تريد إعادة ضبط هذا الموضوع؟

سيعود كل ما في lab-cl-12 إلى الملفات الابتدائية وستُمسح الطرفية. ستفقد حلّك الحالي ولا يمكن التراجع عن ذلك.

سيُعاد ضبطه
  • main.py ← الشيفرة الابتدائية
  • سجل الطرفية ومخرجات الاختبارات
  • التغييرات غير المُرسلة
سيُحتفظ به
  • أفضل درجة في المختبر
  • نقاط XP والتتابع
  • تقدّم المحاضرة والملاحظات