σμ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 AcademyRoadmapsRoteiros
Ctrl K
AO
+1,2 mil alunos
EntrarParticipar ↗
RoadmapsRoteiros
AO
+1,2 mil alunos
Entrar
  • 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 AcademyRoadmapsRoteiros
Ctrl K
AO
+1,2 mil alunos
EntrarParticipar ↗
RoadmapsRoteiros
AO
+1,2 mil alunos
Entrar
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Roteiro de Cache locality›Módulo 3 · A máquina como um todo›Tópico 3.6·~55 minAvançado

Previsão de desvios

Posição no roteiroMódulo 3 de 3 · Tópico 6 de 7
3.6 Previsão de desvios
—Nota do lab— / 100XP total—Sequência—

6 min de leitura3 exemplos de código

  1. 01Por que o processador palpita
  2. 02Preditores que lembram
  3. 03O que custa um erro e por que ordenar ajuda
  4. 04O que você vai construir

Um processador não espera saber para onde vai um if: ele palpita e começa a trabalhar. Um palpite certo é de graça e um errado joga o trabalho fora, cerca de 15 ciclos perdidos. A frequência de acertos depende dos dados: o mesmo laço sobre os mesmos retornos do SPY custa 8,3 vezes menos ciclos depois de ordenar os retornos.

01

Por que o processador palpita

Um processador funciona como uma linha de montagem: enquanto uma instrução executa, as seguintes já estão sendo lidas e decodificadas. Um if quebra a linha, porque a próxima instrução depende da resposta dele, que ainda não está pronta. Então o processador faz uma previsão de desvio e avança nela. Se o palpite estiver certo, nada se perde. Se estiver errado, o trabalho feito no caminho errado é jogado fora e a linha recomeça, o que custa cerca de 15 ciclos num núcleo moderno, tanto quanto uma visita ao cache L2. Escrevemos o resultado de um desvio como True quando ele é tomado. Num laço de trading um desvio é, por exemplo, if daily_return > 0, e o fluxo de seus resultados é simplesmente um True ou False por dia.

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

De seis dias, quatro tiveram retorno positivo: o desvio é tomado quatro vezes e não tomado duas. O preditor mais simples, sempre tomado, erra exatamente nos dois resultados False. Ele acerta tantas vezes quanto o desvio é tomado e ignora o histórico.

02

Preditores que lembram

Os processadores reais usam o histórico. O preditor de 1 bit lembra o que o desvio fez da última vez e palpita o mesmo. É bom quando os resultados vêm em sequências, mas paga duas vezes cada resultado isolado: uma quando ele chega e outra quando a sequência retoma. O preditor de 2 bits resolve isso com um contador de 0 a 3 que para nas duas pontas. Ele palpita «tomado» em 2 e 3, anda um passo em direção a cada resultado, e por isso precisa de dois resultados seguidos contra o palpite para mudar de ideia. Começamos o palpite de 1 bit em «não tomado» e o contador em 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. 1O contador anda um passo em direção ao resultado e para em 0 e 3: é isso que o torna saturante.

Nas sequências de quatro, os dois preditores erram três vezes (o primeiro palpite e as duas viradas). Com um único False isolado entre resultados tomados, o de 1 bit erra três vezes e o de 2 bits só uma, porque o contador cai de 3 para 2 e ainda palpita «tomado». Por isso os processadores reais preferem contadores. Lembre do outro lado: quando os resultados não têm padrão, nenhum preditor consegue fazer melhor do que palpitar a resposta mais frequente.

A previsão depende dos dados, não do código

O mesmo if é barato com dados previsíveis e caro com dados aleatórios. Não reescreva um desvio para deixá-lo «mais rápido» antes de saber como são seus resultados nos dados reais; meça primeiro os erros.

03

O que custa um erro e por que ordenar ajuda

Para transformar erros em tempo, somamos: cada desvio custa um ciclo base e cada erro soma uma penalidade de 15. Agora o experimento que dá nome a esta aula: pegue os primeiros 4000 retornos diários do SPY e execute o desvio return > 0 duas vezes, sobre os retornos em ordem de data e sobre os mesmos retornos ordenados. Ordenar não muda nada nos números, só a ordem deles, mas transforma os resultados de um cara ou coroa em duas sequências longas: todos os False primeiro e todos os True depois.

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

Em ordem de data, 2200 dos 4000 retornos são positivos: o desvio é tomado 55% das vezes e pula de uma resposta para a outra quase sem padrão. Sempre tomado erra 1800 vezes, o de 1 bit 2035 e o de 2 bits 1950. Os preditores com memória são até piores que o palpite simples, porque seguem cada virada. Ordenados, os resultados são 1800 False seguidos de 2200 True, e o de 2 bits erra só 2 vezes. O custo cai de 33 250 para 4030 ciclos, 8,3 vezes menos, com exatamente os mesmos dados e o mesmo laço.

DefiniçãoPenalidade de erro de previsão

Os ciclos perdidos quando o processador erra um desvio: ele descarta o trabalho feito no caminho errado e reenche a linha, cerca de 15 ciclos num núcleo moderno.

O mesmo truque em NumPy e em C++

Uma comparação vetorizada como returns > 0 não tem um desvio por elemento, então não sofre disso de forma alguma. Em C++ um laço com um if sobre dados aleatórios é o caso clássico em que ordenar os dados primeiro, ou escrever a condição como aritmética, o torna várias vezes mais rápido.

O que dizem os dados reais4000 retornos diários do SPY, desvio return > 0, penalidade de 15 ciclos
Sempre tomado, ordem de data1,800erros de 4000
2 bits, ordem de data1,950erros, pior que sempre tomado
2 bits, ordenado2erros: uma sequência, depois a outra
Ganho de ordenar8.3×menos ciclos (33 250 → 4030)
Ciclos do preditor de 2 bits
Ordem de data33250
Ordenado4030
04

O que você vai construir

O laboratório segue a aula: os resultados de um desvio, os três preditores, o custo em ciclos, o fluxo ordenado e um estudo dos retornos do SPY em ordem de data e ordenados.

  • Função · perguntaBaseia-se em
  • taken_sequence(values, threshold)O que o desvio faz com cada valor?Aritmética simples
  • always_taken_mispredicts(outcomes)Quantas vezes «sempre tomado» erra?Aritmética simples
  • one_bit_mispredicts(outcomes)E um preditor que lembra o último resultado?Aritmética simples
  • two_bit_mispredicts(outcomes)E um contador saturante?Aritmética simples
  • cycles(n_branches, mispredicts)Quanto custa tudo isso?Aritmética simples
  • sorted_stream(values, threshold)E se os valores fossem ordenados antes?taken_sequence
  • study(bars)Quanto ordenar economiza no SPY?sorted_streamtwo_bit_mispredictscycles

Pontos-chave

  • O processador palpita cada `if`. Um palpite certo é de graça; um errado custa cerca de 15 ciclos, e um preditor com memória só vence o palpite simples quando os resultados vêm em sequências.
  • Um contador vence um único bit. O contador de 2 bits ignora um resultado isolado onde o de 1 bit paga duas vezes.
  • A ordem muda o custo. Os mesmos 4000 retornos custam 33 250 ciclos em ordem de data e 4030 ordenados, porque ordenar transforma um cara ou coroa em duas sequências.
Verificação rápida
Um desvio tem os resultados True ×4, False ×4, True ×4. O preditor de 1 bit começa em «não tomado». Quantas vezes ele erra?
Escolha uma resposta.

Próximo tópico: o projeto final reúne toda a habilidade. Você recebe uma tabela de ticks e uma carga que a lê; reorganiza em linhas, colunas ou blocos, conta as faltas de cache de cada disposição com o cache que construiu, as precifica com o modelo de latência do primeiro tópico e prova qual disposição vence.

Lab: o que é avaliado

7 tarefas · 100 pts
  • Resultados de um desvio5 pts
  • Sempre tomado10 pts
  • Um preditor de 1 bit15 pts
  • Um contador saturante de 2 bits20 pts
  • O custo em ciclos10 pts
  • Valores ordenados10 pts
  • O estudo com a tabela do SPY30 pts
Explorador · lab-cl-12
▾ lab-cl-12
▾ data
…
bash · no navegador
learner@quantcraft:~/lab-cl-12$
Python ainda não carregadoPythonLn 1, Col 1Spaces: 4UTF-8

Redefinir este tópico?

Tudo em lab-cl-12 volta aos arquivos iniciais e o terminal é limpo. Sua solução atual será perdida — isso não pode ser desfeito.

Será redefinido
  • main.py → código inicial
  • Histórico do terminal e saída dos testes
  • Alterações não enviadas
Será mantido
  • Melhor nota do lab
  • Seu XP e sua sequência
  • Progresso da aula e anotações