σμ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.3·~2 سخبير

مشروع ختامي: توزيع بيانات سوق بلا أقفال مع إثبات ثوابته

الموضع في المسارالوحدة 4 من 4 · الموضوع 3 من 3
4.3 مشروع ختامي: توزيع بيانات سوق بلا أقفال مع إثبات ثوابته
—درجة المختبر— / 100إجمالي XP—التتابع—

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

  1. 01التصميم: سجل ومؤشر لكل مستهلك
  2. 02المستهلك: اقرأ الذيل ثم مؤشرك
  3. 03ثلاثة ثوابت ثم كل جدول
  4. 04ما ستبنيه

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

01

التصميم: سجل ومؤشر لكل مستهلك

الخلاصة سجل لا ينمو إلا. الموضع p يحمل tick<p> ويقول tail كم موضعًا نُشر. لا يكتب النقرات والذيل إلا المنتج فلا شيء يُتنازع عليه من جهته. ولكل مستهلك مؤشر هو الموضع التالي الذي سيقرؤه ولا يكتبه أحد غيره. هذه كل حيلة التوزيع: لا يكتب المستهلكون شيئًا يقرؤه الآخرون فإضافة مستهلك لا تكلّف غيره شيئًا. الحقيقة المشتركة الوحيدة هي الذيل والسؤال الوحيد هو بأي ترتيب يكتب المنتج النقرة ويحرّك الذيل. إن حرّكه بعد البيانات وجد القارئ الذي يرى الذيل الجديد البيانات. وإن حرّكه قبلها قد يرى القارئ موضعًا لم يُكتب بعد.

main.pyPython
def publish(shared, tick, safe=True):    if safe:        yield        pos = shared["tail"]        shared["tick%d" % pos] = tick        shared["put%d" % pos] = tick        yield        shared["tail"] = pos + 1    else:        yield        pos = shared["tail"]        shared["tail"] = pos + 1        yield        shared["tick%d" % pos] = tick        shared["put%d" % pos] = tickfor safe in (True, False):    shared, _ = run_prefix(lambda sh: [publish(sh, 9369, safe)], [0], make_feed(1))    print("safe  " if safe else "unsafe", "after one turn:", {k: v for k, v in shared.items() if k in ("tail", "tick0")})
safe   after one turn: {'tail': 0, 'tick0': 9369}
unsafe after one turn: {'tail': 1}
02

المستهلك: اقرأ الذيل ثم مؤشرك

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

main.pyPython
def poll(shared, c):    yield    tail = shared["tail"]    yield    pos = shared["cur%d" % c]    if pos >= tail:        return None    value = shared.get("tick%d" % pos)    shared["cur%d" % c] = pos + 1    shared["got%d_%d" % (c, pos)] = value    return valueready = dict(make_feed(1), tail=1)shared, alive = run_prefix(lambda sh: [poll(sh, 0)], [0, 0], ready)print("tail is 1 but tick0 was never written:", {k: v for k, v in shared.items() if k.startswith(("cur", "got"))})
tail is 1 but tick0 was never written: {'cur0': 1, 'got0_0': None}
لماذا لا يستطيع المستهلك الانتظار ببساطة

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

03

ثلاثة ثوابت ثم كل جدول

ماذا يعني «صحيح» للتدفق؟ ثلاث عبارات عن الحالة النهائية. ممزق: كل نقرة استلمها مستهلك تساوي النقرة الموضوعة في ذلك الموضع (نقرة غائبة استُلمت None ممزقة). فجوة: استلم كل مستهلك المواضع 0 و1 و2 ... بلا ثقب. تجاوز: لا مؤشر يتجاوز الذيل. يستكشف verify كل جدول لمنتج ومستهلكين ويعدّ الجداول التي تكسر كلًّا منها. المستكشف هو الدليل: لا يستطيع أخذ عينة من التشغيل المحظوظ الواحد بل يزورها كلها.

main.pyPython
def violations(final):    puts = {int(key[3:]): value for key, value in final.items() if key.startswith("put")}    gots = {}    for key, value in final.items():        if key.startswith("got"):            c, p = key[3:].split("_")            gots[(int(c), int(p))] = value    broken = []    if any(value != puts.get(p) for (c, p), value in gots.items()):        broken.append("torn")    for c in {c for c, _ in gots}:        if sorted(p for cc, p in gots if cc == c) != list(range(sum(1 for cc, _ in gots if cc == c))):            broken.append("gap")            break    if any(key.startswith("cur") and value > final["tail"] for key, value in final.items()):        broken.append("overrun")    return sorted(broken)def verify(ticks, polls, safe=True):    outcomes = explore_all(feed_threads(ticks, polls, safe), make_feed(len(polls)))    broken = {}    delivered = 0    for key, count in outcomes.items():        final = dict(key)        delivered = max(delivered, sum(1 for k in final if k.startswith("got")))        for name in violations(final):            broken[name] = broken.get(name, 0) + count    return {"schedules": sum(outcomes.values()), "broken": dict(sorted(broken.items())), "delivered": delivered}ticks = [9369, 9305]print("safe  ", verify(ticks, (2, 1), True))print("unsafe", verify(ticks, (2, 1), False))
safe   {'schedules': 3150, 'broken': {}, 'delivered': 3}
unsafe {'schedules': 3150, 'broken': {'torn': 869}, 'delivered': 3}
ما تقوله البيانات الحقيقيةمنتج بأول إغلاقين لـSPY (9369 و9305 سنتًا) ومستهلك يستطلع مرتين وآخر مرة واحدة
الجداول3,150مستكشفة لكل ترتيب
البيانات أولًا0جداول تكسر ثابتًا
الذيل أولًا869جداول تسلّم نقرة غير موجودة
تشغيل هادئ2نقرات استلمها كل مستهلك بالترتيب

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

04

ما ستبنيه

يتبع المختبر الدرس: التدفق الفارغ والمنتج بترتيبيه واستطلاع المستهلك ومدقق الثوابت الثلاثة والمدقق الذي يستكشف كل الجداول وإعادة تشغيل هادئ يستلم فيه كل مستهلك النقرات بالترتيب والدراسة على إغلاقات SPY.

  • الدالة · السؤالتعتمد على
  • make_feed(consumers)ما المشترك؟حساب بسيط
  • publish(shared, tick, safe)البيانات أولًا أو الذيل أولًاحساب بسيط
  • poll(shared, c)النقرة التالية للمستهلك cحساب بسيط
  • violations(final)أي الثوابت انكسرت؟حساب بسيط
  • verify(ticks, polls, safe)هل التدفق صحيح تحت كل الجداول؟explore_allviolations
  • drain(ticks, polls)ماذا يستلم كل مستهلك؟run_prefix
  • study(bars)آمن وغير آمن وهادئ على SPYverifydrain

أهم الخلاصات

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

بهذا تكتمل مهارة البرمجة بلا أقفال. صرت تستطيع بناء القطع (الذرّيات وcompare-and-swap والمكدسات والطوابير) وتسمية الأخطار (التسابقات وABA والاسترجاع) وقبل كل شيء فحص تصميم على كل جدول بدل الوثوق بتشغيل نجح مصادفة. والخطوة التالية هي الانضباط نفسه على عتاد حقيقي في مواضيع ذرّيات C++.

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

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

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

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

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