σμ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 AcademyHojas de rutaRutas
Ctrl K
AO
+1,2 k estudiantes
Iniciar sesiónUnirse ↗
Hojas de rutaRutas
AO
+1,2 k estudiantes
Iniciar sesión
  • 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 AcademyHojas de rutaRutas
Ctrl K
AO
+1,2 k estudiantes
Iniciar sesiónUnirse ↗
Hojas de rutaRutas
AO
+1,2 k estudiantes
Iniciar sesión
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Ruta de Lock-free›Módulo 2 · Atómicas, orden y garantías›Tema 2.2·~1 h 15 minAvanzado

El modelo de memoria: acquire y release

Posición en la rutaMódulo 2 de 4 · Tema 2 de 3
2.2 El modelo de memoria: acquire y release
—Puntuación del lab— / 100XP total—Racha—

5 min de lectura4 ejemplos de código

  1. 01Los procesadores reordenan
  2. 02Ejecutar todos los órdenes
  3. 03Release y acquire van en pareja
  4. 04Lo que va a construir

Un procesador es libre de ejecutar dos lecturas y escrituras independientes en cualquier orden, y otro núcleo puede ver el resultado de esa elección. Publique un precio de SPY y luego levante una bandera: un lector que ve la bandera aún puede leer un precio de 0. De las cuatro maneras de ordenar al escritor y al lector, solo una es segura: release en el escritor emparejado con acquire en el lector.

01

Los procesadores reordenan

Los búferes de escritura, las cachés y el compilador dejan que un núcleo termine una escritura o lectura posterior antes que una anterior, mientras un solo hilo no pueda notarlo. Otro hilo sí puede. Nuestra máquina lo modela directamente: un hilo es una lista de operaciones y puede ejecutar, en cualquier orden, toda operación cuyas restricciones de orden se cumplen. Las operaciones sobre la misma dirección conservan siempre su orden, si no un hilo no vería sus propias escrituras. Una carga acquire bloquea todo lo que sigue; una escritura release espera todo lo anterior. Dos funciones lo dicen todo: must_wait lista lo que una operación espera y eligible lista lo que puede ejecutarse a continuación.

main.pyPython
def must_wait(ops, j):    waits = []    for i in range(j):        same = ops[i][1] == ops[j][1]        after_acquire = ops[i][0] == "load" and ops[i][3] == "acquire"        release = ops[j][0] == "store" and ops[j][3] == "release"        if same or after_acquire or release:            waits.append(i)    return waitsdef eligible(ops, done):    return [j for j in range(len(ops)) if j not in done and all(i in done for i in must_wait(ops, j))]writer = [("store", "data", 1, "relaxed"), ("store", "flag", 1, "release")]reader = [("load", "flag", "r1", "acquire"), ("load", "data", "r2", "relaxed")]print(must_wait(writer, 1), eligible(writer, set()), eligible(writer, {0}))print(must_wait(reader, 1), eligible(reader, set()))print(eligible([("load", "flag", "r1", "relaxed"), ("load", "data", "r2", "relaxed")], set()))
[0] [0] [1]
[0] [0]
[0, 1]

La escritura release de la bandera espera a la posición 0, la escritura de los datos, así que el escritor solo puede ejecutar sus operaciones en orden. La carga acquire de la bandera bloquea la carga de datos que le sigue, así que el lector también se ejecuta en orden. Con cargas simples, ambas son elegibles a la vez y el lector puede leer los datos primero.

02

Ejecutar todos los órdenes

outcomes es el explorador de los temas anteriores para esta nueva máquina. Un estado es qué operaciones ha hecho cada hilo, la memoria y los registros. Desde un estado prueba cada operación elegible de cada hilo y, cuando no queda nada por ejecutar, los registros son un resultado. Como muchos órdenes llegan al mismo estado, recuerda los estados ya vistos. La prueba de paso de mensajes publica con dos escrituras, los datos y luego la bandera, y consume con dos cargas, la bandera y luego los datos. El consumidor se equivoca cuando ve r1 == 1 (la bandera) y r2 == 0 (aún sin datos), lo que comprueba reads_stale.

main.pyPython
def outcomes(programs, memory=None):    found = set()    seen = set()    def visit(done, mem, regs):        state = (tuple(frozenset(d) for d in done), tuple(sorted(mem.items())), tuple(sorted(regs.items())))        if state in seen:            return        seen.add(state)        moved = False        for tid, ops in enumerate(programs):            for j in eligible(ops, done[tid]):                moved = True                kind, address, x, _ = ops[j]                mem2, regs2 = dict(mem), dict(regs)                if kind == "store":                    mem2[address] = x                else:                    regs2[x] = mem.get(address, 0)                done2 = [set(d) for d in done]                done2[tid].add(j)                visit(done2, mem2, regs2)        if not moved:            found.add(tuple(sorted(regs.items())))    visit([set() for _ in programs], dict(memory or {}), {})    return founddef mp_programs(write_order, read_order):    return [        [("store", "data", 1, "relaxed"), ("store", "flag", 1, write_order)],        [("load", "flag", "r1", read_order), ("load", "data", "r2", "relaxed")],    ]def reads_stale(results):    return any(dict(outcome).get("r1") == 1 and dict(outcome).get("r2") == 0 for outcome in results)for pair in (("relaxed", "relaxed"), ("release", "acquire")):    results = outcomes(mp_programs(*pair))    print(pair, sorted(results), "stale:", reads_stale(results))
('relaxed', 'relaxed') [(('r1', 0), ('r2', 0)), (('r1', 0), ('r2', 1)), (('r1', 1), ('r2', 0)), (('r1', 1), ('r2', 1))] stale: True
('release', 'acquire') [(('r1', 0), ('r2', 0)), (('r1', 0), ('r2', 1)), (('r1', 1), ('r2', 1))] stale: False

Con órdenes relaxed el consumidor puede terminar con los cuatro pares (r1, r2), incluido (1, 0), el obsoleto. Con release en la escritura de la bandera y acquire en su carga solo quedan tres: el lector que ve la bandera ve también los datos, porque ningún lado puede reordenarse a través de la bandera.

03

Release y acquire van en pareja

¿Basta un solo lado? Probemos las cuatro parejas. Release solo en el escritor falla: el lector puede cargar los datos antes que la bandera. Acquire solo en el lector falla: el escritor puede guardar la bandera antes que los datos. Solo funciona la pareja, como muestra la tabla siguiente. Es el contrato release-acquire de std::atomic de C++ y de Rust, y la razón por la que una cola sin cerrojos publica una casilla con una escritura release de un índice y la lee con una carga acquire.

main.pyPython
PAIRS = [("relaxed", "relaxed"), ("release", "relaxed"), ("relaxed", "acquire"), ("release", "acquire")]def ordering_table():    return {pair: reads_stale(outcomes(mp_programs(*pair))) for pair in PAIRS}for pair, stale in ordering_table().items():    print("write", pair[0], "| read", pair[1], "->", "can read stale data" if stale else "safe")
write relaxed | read relaxed -> can read stale data
write release | read relaxed -> can read stale data
write relaxed | read acquire -> can read stale data
write release | read acquire -> safe
Acquire y release son una pareja, no una palabra mágica

Un release sin el acquire correspondiente sobre la misma variable no protege nada. Un orden de memoria real de C++ como seq_cst es más fuerte y cuesta más; relaxed es el más barato y no promete ningún orden. Elija el orden más débil que haga pasar su prueba anterior y demuéstrelo con un explorador.

main.pyPython
price = int(round(float(load_bars()["close"].iloc[0]) * 100))programs = mp_programs("release", "acquire")programs[0][0] = ("store", "data", price, "relaxed")print(price, sorted(outcomes(programs)))
9369 [(('r1', 0), ('r2', 0)), (('r1', 0), ('r2', 9369)), (('r1', 1), ('r2', 9369))]
Qué dicen los datos realesel primer cierre de SPY publicado como 9369 centavos mediante una bandera
Relaxed + relaxed4resultados, precio obsoleto posible
Solo release4resultados, precio obsoleto posible
Solo acquire4resultados, precio obsoleto posible
Release + acquire3resultados, nunca obsoleto

Con el primer cierre de SPY, 9369 centavos, el lector puede ver la bandera y un precio de 0 en tres de las cuatro configuraciones. Un error de este tipo pasa todas las pruebas en una máquina que conserva el orden y aparece en otra. Por eso la pareja segura debe demostrarse sobre todos los órdenes permitidos, como hace el explorador.

DefiniciónPareja release-acquire

Una escritura release de una bandera y una carga acquire que la lee. Todo lo que el escritor hizo antes del release es visible para el lector después del acquire. Es el orden más barato que hace correcta la publicación de datos mediante una bandera.

04

Lo que va a construir

El laboratorio sigue la lección: lo que una operación debe esperar, qué operaciones pueden ejecutarse a continuación, todos los resultados de un conjunto de hilos, la prueba de paso de mensajes, la comprobación de datos obsoletos, la tabla de los cuatro órdenes y el estudio que publica un precio real de SPY.

  • Función · preguntaSe apoya en
  • must_wait(ops, j)¿Qué debe esperar esta operación?Aritmética simple
  • eligible(ops, done)¿Qué operaciones pueden ir a continuación?Aritmética simple
  • outcomes(programs, memory)¿Con qué pueden terminar los hilos?eligible
  • mp_programs(write_order, read_order)¿Cómo es el paso de mensajes?Aritmética simple
  • reads_stale(results)¿Puede el lector ver la bandera sin los datos?Aritmética simple
  • ordering_table()¿Qué parejas de órdenes son seguras?outcomesmp_programs
  • study(bars)¿Qué parejas protegen un precio real de SPY?outcomesmp_programs

Ideas clave

  • El reordenamiento está permitido salvo que una regla lo prohíba. Las operaciones sobre la misma dirección conservan su orden; las cargas acquire y las escrituras release añaden las barreras.
  • Publicar necesita una pareja. Release en la escritura de la bandera más acquire en su carga: 3 resultados y ningún precio obsoleto; cualquier otra pareja tiene 4 y puede leer 0.
  • Demuéstrelo sobre todos los órdenes. Una prueba en una máquina puede pasar por suerte; el explorador comprueba todos los órdenes que permiten las reglas.
Comprobación rápida
El escritor guarda los datos y luego hace una escritura release de la bandera. El lector hace una carga simple (relaxed) de la bandera y luego una carga simple de los datos. ¿Puede el lector ver la bandera puesta y los datos ausentes?
Elige una respuesta.

Siguiente tema: ya tiene las herramientas para construir una estructura sin cerrojos y un vocabulario para describir lo que garantiza. Las garantías de progreso nombran los tres niveles, obstruction-free, lock-free y wait-free, y prueban cada contador que escribió contra un hilo pausado para siempre en el peor momento.

Lab: qué se califica

7 tareas · 100 pts
  • Lo que espera una operación15 pts
  • Cuáles pueden ir a continuación10 pts
  • Cada resultado25 pts
  • La prueba de paso de mensajes10 pts
  • Datos obsoletos5 pts
  • Los cuatro órdenes10 pts
  • El estudio con la tabla de SPY25 pts
Explorador · lab-lf-04
▾ lab-lf-04
▾ data
…
bash · en el navegador
learner@quantcraft:~/lab-lf-04$
Python aún no cargadoPythonLn 1, Col 1Spaces: 4UTF-8

¿Restablecer este tema?

Todo lo que hay en lab-lf-04 vuelve a los archivos iniciales y se limpia el terminal. Tu solución actual se perderá: no se puede deshacer.

Se restablecerá
  • main.py → código inicial
  • Historial del terminal y salida de pruebas
  • Cambios sin enviar
Se conservará
  • Mejor puntuación del lab
  • Tu XP y tu racha
  • Progreso de la lección y notas