σμ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›الوحدة 2 · التخطيط والعبور›الموضوع 2.1·~55 دمتوسط

مصفوفة هياكل مقابل هيكل مصفوفات

الموضع في المسارالوحدة 2 من 3 · الموضوع 1 من 2
2.1 مصفوفة هياكل مقابل هيكل مصفوفات
—درجة المختبر— / 100إجمالي XP—التتابع—

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

  1. 01جدول واحد وتخطيطان
  2. 02مسح عمود واحد
  3. 03قراءة صفوف كاملة تعكس النتيجة
  4. 04دع الأعداد تختار
  5. 05ما ستبنيه

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

01

جدول واحد وتخطيطان

الذاكرة صف طويل من البايتات، فيجب تسطيح الجدول ثنائي الأبعاد. هناك طريقتان طبيعيتان. في مصفوفة هياكل تُخزَّن القيم الخمس ليوم (الافتتاح والأعلى والأدنى والإغلاق والحجم) معًا ثم القيم الخمس لليوم التالي وهكذا. وفي هيكل مصفوفات تأتي كل قيم الافتتاح أولًا ثم كل قيم الأعلى وهكذا. القيم والبرنامج نفسهما لكن العناوين تختلف.

يُستنتج عنوان القيمة من صفها وعمودها. في AoS رقم القيمة هو الصف × عدد الأعمدة + العمود. وفي SoA هو العمود × عدد الصفوف + الصف. اضرب في 8 بايت للنوع float64. يطبع الكود أدناه عناوين إغلاقات الأيام الأربعة الأولى في جدول من 100 يوم و5 أعمدة.

main.pyPython
def address(layout, row, col, n_rows, n_cols, elem_bytes=8):    if layout == "aos":        return (row * n_cols + col) * elem_bytes1    return (col * n_rows + row) * elem_bytesclose = 3print("aos:", [address("aos", row, close, 100, 5) for row in range(4)])print("soa:", [address("soa", row, close, 100, 5) for row in range(4)])
aos: [24, 64, 104, 144]
soa: [2400, 2408, 2416, 2424]
  1. 1في AoS يبعد الصف التالي n_cols قيمة فيتباعد إغلاقان متتاليان 5 قيم (40 بايت).

في AoS تتباعد إغلاقات الأيام المتتالية 40 بايت. وفي SoA تتباعد 8 بايت أي واحد بعد الآخر مباشرة وهذه هي بالضبط القراءة بالترتيب في أول موضوع من هذه المهارة.

02

مسح عمود واحد

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

main.pyPython
def scan_column(layout, col, n_rows, n_cols):    return [address(layout, row, col, n_rows, n_cols) for row in range(n_rows)]for layout in ("aos", "soa"):    misses = count_misses(scan_column(layout, 3, 1000, 5))    print(layout, "misses:", misses, "- useful share:", round(1000 * 8 / (misses * 64), 3))
aos misses: 625 - useful share: 0.2
soa misses: 125 - useful share: 1.0
useful share = value bytes ÷ row bytes (while a row fits in one line)(1)في AoS يحمّل مسح العمود صفوفًا كاملة: 8 ÷ 40 = 20%؛ وفي SoA كل بايت محمّل هو إغلاق: نحو 100%

يخفق مسح SoA 125 مرة لكل 1000 قيمة أي إخفاق واحد لكل ثماني قيم ولا شيء مهدور. ويخفق مسح AoS 625 مرة أي خمسة أضعاف و20% فقط من البايتات المحمّلة إغلاقات. أما الـ80% الباقية فهي افتتاحات وأعلى وأدنى وأحجام لا ينظر إليها المتوسط أبدًا وتطرد أسطرًا مفيدة من الذاكرة المخبئية.

03

قراءة صفوف كاملة تعكس النتيجة

تطرح برامج تيكات كثيرة سؤالًا مختلفًا: لهذا التيك أعطني السعر والحجم والجانب معًا. وهذا يقرأ كل قيم الصف. في AoS الصف كله كتلة من 40 بايت تقع في سطر أو سطرين. وفي SoA القيم الخمس في خمس مصفوفات مختلفة فيفتح كل صف خمسة أسطر. يقرأ الكود صفًّا من كل سبعة ويعدّ الإخفاقات.

main.pyPython
def scan_rows(layout, rows, n_rows, n_cols):    return [address(layout, row, col, n_rows, n_cols) for row in rows for col in range(n_cols)]rows = list(range(0, 1000, 7))for layout in ("aos", "soa"):    print(layout, "misses:", count_misses(scan_rows(layout, rows, 1000, 5)))
aos misses: 214
soa misses: 625

الآن يفوز AoS بعامل ثلاثة: 214 إخفاقًا مقابل 625. التخطيط الذي كان أسوأ بخمسة أضعاف لمسح العمود صار أفضل بثلاثة أضعاف لقراءة الصفوف. لا يوجد تخطيط هو الأفضل: لكل منهما طريقة قراءة هو الأفضل لها.

أنت تستعمل التخطيطين بالفعل

مصفوفة NumPy ثنائية الأبعاد بالترتيب الافتراضي هي تخطيط AoS: كل صف متجاور. وDataFrame في pandas يخزّن أعمدة النوع الواحد معًا وهذا قريب من SoA وهو أحد أسباب سرعة عمليات الأعمدة فيه.

04

دع الأعداد تختار

يقوم برنامج حقيقي بنوعي القراءة معًا، فيعتمد الاختيار على المزيج. لكل تخطيط اجمع إخفاقات حمل العمل: مسوح الأعمدة × إخفاقات العمود + قراءات الصفوف × إخفاقات الصفوف. يفوز التخطيط ذو المجموع الأصغر ويذهب التعادل إلى AoS. يجرّب الكود أدناه أربعة مزائج على الجدول نفسه.

main.pyPython
def compare(n_rows, n_cols, col, rows):    out = {}    for layout in ("aos", "soa"):        out[layout] = {            "column": count_misses(scan_column(layout, col, n_rows, n_cols)),            "rows": count_misses(scan_rows(layout, rows, n_rows, n_cols)),        }    return outdef best_layout(n_column_scans, n_row_reads, n_rows, n_cols, col, rows):    counts = compare(n_rows, n_cols, col, rows)    totals = {layout: n_column_scans * counts[layout]["column"] + n_row_reads * counts[layout]["rows"] for layout in counts}    return "soa" if totals["soa"] < totals["aos"] else "aos"rows = list(range(0, 1000, 7))for scans, reads in ((1, 4), (1, 2), (1, 1), (4, 1)):    print(scans, "column scans per", reads, "row reads:", best_layout(scans, reads, 1000, 5, 3, rows))
1 column scans per 4 row reads: aos
1 column scans per 2 row reads: aos
1 column scans per 1 row reads: soa
4 column scans per 1 row reads: soa
اختر بحسب نمط الوصول لا بالعادة

مع مسح عمود واحد لكل قراءتي صف يفوز AoS؛ ومع مسح لكل قراءة صف يفوز SoA بالفعل. تقع نقطة التعادل قرب نصف مسح لكل قراءة صف ويحدد حمل عملك الفعلي في أي جانب أنت. عُدّ القراءات التي يقوم بها برنامجك فعلًا قبل إعادة تنظيم جدول.

ما تقوله البيانات الحقيقيةأشرطة SPY اليومية، 5405 صفوف × 5 أعمدة، ذاكرة 32 كيلوبايت بـ8 طرق
مسح الإغلاقات: SoA67799.8% من البايتات المحمّلة مستعملة
مسح الإغلاقات: AoS3,37820% من البايتات المحمّلة مستعملة
قراءة 772 صفًّا: AoS1,159صف من كل سبعة
قراءة 772 صفًّا: SoA3,383خمسة أسطر مفتوحة لكل صف
إخفاقات مسح الإغلاقات وقراءات الصفوف حسب التخطيط
عمود، SoA677
عمود، AoS3378
صفوف، AoS1159
صفوف، SoA3383
05

ما ستبنيه

يتبع المختبر الدرس: التخطيطان ونوعا القراءة والمقارنة واختيار التخطيط لحمل عمل والحصة المفيدة والدراسة على جدول SPY الحقيقي.

  • الدالة · السؤالتعتمد على
  • address(layout, row, col, n_rows, n_cols)أين توجد هذه القيمة؟حساب بسيط
  • scan_column(layout, col, n_rows, n_cols)ما العناوين التي يقرؤها مسح عمود؟address
  • scan_rows(layout, rows, n_rows, n_cols)ما العناوين التي تمسّها قراءة صفوف؟address
  • compare(n_rows, n_cols, col, rows)كم إخفاقًا تكلّف كل قراءة في كل تخطيط؟scan_columnscan_rows
  • best_layout(...)أي تخطيط يناسب هذا الحمل؟compare
  • useful_fraction(layout, col, n_rows, n_cols)كم مما يُحمَّل يُستعمل؟scan_column
  • study(bars)أي تخطيط يناسب جدول SPY؟comparebest_layout

أهم الخلاصات

  • التخطيط مجموعة من الخطوات. يجعل AoS مسح العمود بخطوة تساوي عرض الصف ويجعله SoA بخطوة قيمة واحدة.
  • عدّ البايتات المفيدة. مسح عمود في جدول AoS من 5 أعمدة يستعمل 20% فقط مما يحمّله والباقي يطرد أسطرًا مفيدة.
  • اختر بحسب حمل العمل. اضرب كل نوع قراءة في إخفاقاته في كل تخطيط واختر المجموع الأصغر؛ كان التعادل عند نحو نصف مسح لكل قراءة صف.
تحقق سريع
جدول فيه 4 أعمدة من قيم 8 بايت مخزَّن بنمط AoS. مسح عمود واحد يحمّل صفوفًا من 32 بايت، اثنين في كل سطر من 64 بايت. ما نسبة البايتات المحمّلة المستعملة؟
اختر إجابة.

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

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

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

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

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

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