σμ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
  • 日本語
  • 中文
مسار Lock-free›الوحدة 4 · الاختبار وسلامة الذاكرة›الموضوع 4.1·~55 دمتقدم

اختبار الشيفرة المتزامنة: كاشفات التسابق والتحقق من النماذج

الموضع في المسارالوحدة 4 من 4 · الموضوع 1 من 3
4.1 اختبار الشيفرة المتزامنة: كاشفات التسابق والتحقق من النماذج
—درجة المختبر— / 100إجمالي XP—التتابع—

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

  1. 01أثر وسؤال
  2. 02مجموعة الأقفال: أي قفل يحمي هذا المتغير؟
  3. 03يحدث قبل: ما الذي يمكن أن يتداخل فعلًا
  4. 04ما حجم الفضاء؟
  5. 05ما ستبنيه

تشغيل كل الجداول يثبت برنامجًا صغيرًا لكنه يكلّف عدد التداخلات. وتسلك الأدوات الحقيقية اختصارين. كاشف التسابق يراقب تشغيلًا واحدًا ويبلغ عن المتغيرات التي قد تتسابق في جدول آخر. ومدقق النماذج يتذكر الحالات التي رآها. لخيطين من 10 خطوات يكون ذلك 184756 جدولًا مقابل 121 حالة.

01

أثر وسؤال

لا يشغّل كاشف التسابق خيوطًا. إنه يقرأ أثرًا: كل قراءة وكتابة للبيانات المشتركة وكل اكتساب وتحرير لقفل. تصبح برامجنا قوائم من هذه العمليات وتحوّل interleave برنامجًا وجدولًا إلى أثر ذلك التشغيل. خيطان يقرأ كل منهما x ويكتبه بلا قفل يعطيان الأحداث الأربعة أدناه. وفي هذا الجدول ينتهي الخيط 0 قبل أن يبدأ الخيط 1 فيرى الاختبار الجواب الصحيح. أما سؤال الكاشف فمختلف: هل كان يمكن أن يتداخل الخيطان؟

main.pyPython
def interleave(programs, schedule):    position = [0] * len(programs)    trace = []    for tid in schedule:        kind, name = programs[tid][position[tid]]        position[tid] += 1        trace.append((tid, kind, name))    return traceunlocked = [[("read", "x"), ("write", "x")], [("read", "x"), ("write", "x")]]trace = interleave(unlocked, [0, 0, 1, 1])print(trace)
[(0, 'read', 'x'), (0, 'write', 'x'), (1, 'read', 'x'), (1, 'write', 'x')]
02

مجموعة الأقفال: أي قفل يحمي هذا المتغير؟

يستعمل الكاشف الأول انضباطًا: ينبغي أن يحمي كل متغير مشترك قفل ما يكون ممسوكًا عند كل وصول. تتتبع قاعدة مجموعة الأقفال الأقفال التي يمسكها كل خيط وتقاطع لكل متغير مجموعات الأقفال الممسوكة عند وصولاته. فإن لمس المتغير خيطان على الأقل وكتبه أحدهما وكان التقاطع فارغًا فلا يحميه قفل واحد: تُبلَّغ حالة تسابق محتملة. يُعلَّم العدّاد بلا قفل ولا يُعلَّم العدّاد بقفل. القاعدة رخيصة وقد تعطي إنذارًا كاذبًا. في التسليم أدناه يكتب الخيط 0 المتغير x ثم يسلّمه إلى الخيط 1 عبر القفل. لم يكن أي قفل ممسوكًا عند الكتابة فتعترض القاعدة مع أن الوصولات في الواقع مرتبة.

main.pyPython
def lockset_races(trace):    held = {}    accesses = {}    for tid, kind, name in trace:        locks = held.setdefault(tid, set())        if kind == "acquire":            locks.add(name)        elif kind == "release":            locks.discard(name)        else:            accesses.setdefault(name, []).append((tid, kind == "write", frozenset(locks)))    racy = []    for name, history in accesses.items():        threads = {tid for tid, _, _ in history}        writes = any(is_write for _, is_write, _ in history)        common = frozenset.intersection(*(locks for _, _, locks in history))        if len(threads) >= 2 and writes and not common:            racy.append(name)    return sorted(racy)locked = [[("acquire", "m"), ("read", "x"), ("write", "x"), ("release", "m")]] * 2guarded = interleave(locked, [0, 0, 0, 0, 1, 1, 1, 1])handoff = [(0, "write", "x"), (0, "acquire", "m"), (0, "release", "m"), (1, "acquire", "m"), (1, "release", "m"), (1, "read", "x")]print("unlocked:", lockset_races(trace))print("locked:  ", lockset_races(guarded))print("handoff: ", lockset_races(handoff))
unlocked: ['x']
locked:   []
handoff:  ['x']
تعريفقاعدة مجموعة الأقفال لـEraser

تُبلَّغ عن متغير حين يصل إليه خيطان ويكتب أحدهما ولا يكون أي قفل ممسوكًا عند كل وصولاته. تجد غياب انضباط الأقفال من تشغيل واحد مع إنذارات كاذبة حيث يأتي الترتيب من شيء غير قفل مشترك.

03

يحدث قبل: ما الذي يمكن أن يتداخل فعلًا

يطرح الكاشف الثاني السؤال الدقيق: هل توجد سلسلة ترتيبات من الوصول الأول إلى الثاني؟ هناك نوعان من الأضلاع: الترتيب داخل خيط واحد والزوج تحرير ثم اكتساب للقفل نفسه. وعلاقة يحدث قبل هي الإغلاق لكليهما. ويسجّل ساعة متجهية لكل خيط ما رآه الخيط؛ يخزّن التحرير الساعة في القفل ويأخذ الاكتساب الأعظم عنصرًا بعنصر معها. والوصول تسابق إن كان لوصول سابق متعارض من خيط آخر عصر لم تره الساعة الحالية. في التسليم يصل التحرير والاكتساب الكتابة بالقراءة فلا تسابق. لا يبلّغ إلا عن التسابقات الحقيقية لكن في الوصولات التي رآها فقط.

main.pyPython
def hb_races(trace):    clocks = {}    lock_clocks = {}    history = {}    racy = set()    for tid, kind, name in trace:        clock = clocks.setdefault(tid, {tid: 1})        if kind == "acquire":            for u, value in lock_clocks.get(name, {}).items():                clock[u] = max(clock.get(u, 0), value)        elif kind == "release":            lock_clocks[name] = dict(clock)            clock[tid] += 1        else:            is_write = kind == "write"            for u, was_write, epoch in history.get(name, []):                if u != tid and (is_write or was_write) and clock.get(u, 0) < epoch:                    racy.add(name)            history.setdefault(name, []).append((tid, is_write, clock[tid]))    return sorted(racy)print("unlocked:", hb_races(trace))print("locked:  ", hb_races(guarded))print("handoff: ", hb_races(handoff))
unlocked: ['x']
locked:   []
handoff:  []
استعمل الاثنين بهذا الترتيب

تستعمل الأدوات الحقيقية مثل ThreadSanitizer علاقة يحدث قبل لأنها لا تعطي إنذارات كاذبة على الأثر الذي ترى. وقاعدة مجموعة الأقفال هي الطريقة الأرخص لإيجاد متغير لا يحميه أحد قط وفحص أول جيد. ولا تغني أيٌّ منهما عن مدقق النماذج في بنية بلا أقفال لا تأتي صحتها من الأقفال أصلًا.

04

ما حجم الفضاء؟

يزور مدقق النماذج البرنامج نفسه لكنه يتذكر الحالات. في برامجنا الحالة هي مدى تقدم كل خيط فقط فمع خيوط من n خطوة تكون هناك (n1 + 1) × (n2 + 1) × ... حالة مهما بلغ عدد الجداول المؤدية إليها. تعطي خيوط العدّ أدناه الأرقام للبيانات الحقيقية مع الجداول والحالات جنبًا إلى جنب.

main.pyPython
def count_states(lengths):    total = 1    for n in lengths:        total *= n + 1    return totaldef counter_programs(ups, locked):    programs = []    for count in ups:        program = []        for _ in range(count):            program += ([("acquire", "m")] if locked else []) + [("read", "x"), ("write", "x")] + ([("release", "m")] if locked else [])        programs.append(program)    return programsreturns = [float(r) for r in load_bars()["close"].pct_change().dropna()][:6]ups = [sum(1 for r in returns[start::2] if r > 0) for start in (0, 1)]for name, lock in (("unlocked", False), ("locked", True)):    programs = counter_programs(ups, lock)    lengths = [len(p) for p in programs]    trace = interleave(programs, serial_schedule(lengths))    print(name, len(trace), "events", lockset_races(trace), hb_races(trace), count_schedules(lengths), "schedules,", count_states(lengths), "states")
unlocked 8 events ['x'] ['x'] 28 schedules, 21 states
locked 16 events [] [] 1820 schedules, 65 states
ما تقوله البيانات الحقيقيةخيطان يعدّان أيام صعود أول 6 عوائد لـSPY (1 و3)
عدّاد بلا قفلxيُعلَّم من الكاشفين
عدّاد بقفلnoneلا يعلّمه أيٌّ منهما
جداول البرنامج بقفل1,820للفحص واحدًا واحدًا
حالات البرنامج بقفل65للتذكر كلها

يكفي أثر تسلسلي واحد للكاشفين لإيجاد القفل الناقص والبرنامج المقفل نظيف. وميزة مدقق النماذج على القوة الغاشمة هي النسبة بين 1820 و65 وتنمو بلا حدّ: لـ10 و10 خطوات تكون 184756 مقابل 121. ولهذا يستطيع التحقق من النماذج تغطية برامج لا يبلغها التعداد البسيط أبدًا بينما يكلّف كاشف التسابق مرورًا واحدًا على تشغيل.

تعريفاختزال فضاء الحالات

تذكّر حالة في أول مرة تُبلَغ ولا توسّعها أبدًا مرتين. تصل جداول كثيرة إلى الحالة نفسها فتنخفض الكلفة من عدد الجداول إلى عدد الحالات.

05

ما ستبنيه

يتبع المختبر الدرس: أثر تشغيل وقاعدة مجموعة الأقفال وقاعدة يحدث قبل بالساعات المتجهية وعدّ الحالات وبرامج خيوط العدّ ودراسة الكاشفين وحجمي الفضاء على أيام صعود SPY.

  • الدالة · السؤالتعتمد على
  • interleave(programs, schedule)ماذا حدث وبأي ترتيب؟حساب بسيط
  • lockset_races(trace)أي المتغيرات بلا قفل مشترك؟حساب بسيط
  • hb_races(trace)أي المتغيرات لها وصولات غير مرتبة؟حساب بسيط
  • count_states(lengths)كم حالة يجب تذكرها؟حساب بسيط
  • counter_programs(ups, locked)ماذا تفعل خيوط العدّ؟حساب بسيط
  • study(bars)هل يتفق الكاشفان على SPY؟counter_programsinterleavelockset_raceshb_racescount_states

أهم الخلاصات

  • الكاشف يقرأ تشغيلًا واحدًا. أثر تسلسلي لم يسوء فيه شيء يكشف مع ذلك القفل الناقص.
  • مجموعة الأقفال رخيصة وصاخبة ويحدث قبل دقيق. التسليم عبر قفل إنذار كاذب لمجموعة الأقفال ومرتب للساعات المتجهية.
  • تذكّر الحالات لا الجداول. 1820 جدولًا مقابل 65 حالة و184756 مقابل 121 لخيطين من 10 خطوات.
تحقق سريع
يكتب الخيط 0 المتغير x ثم يحرّر القفل m؛ ويكتسب الخيط 1 القفل m ثم يقرأ x. الكتابة قبل التحرير. هل يبلّغ كاشف الساعات المتجهية عن تسابق على x؟
اختر إجابة.

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

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

6 مهام · 100 نقطة
  • أثر تشغيل10 نقطة
  • قاعدة مجموعة الأقفال25 نقطة
  • يحدث قبل بالساعات المتجهية30 نقطة
  • حالات مدقق النماذج5 نقطة
  • خيوط العدّ5 نقطة
  • الدراسة على جدول SPY25 نقطة
المستكشف · lab-lf-09
▾ lab-lf-09
▾ data
…
bash · داخل المتصفح
learner@quantcraft:~/lab-lf-09$
لم يتم تحميل Python بعدPythonLn 1, Col 1Spaces: 4UTF-8

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

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

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