σμ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.1·~50 minAvançado

Pré-busca

Posição no roteiroMódulo 3 de 3 · Tópico 1 de 7
3.1 Pré-busca
—Nota do lab— / 100XP total—Sequência—

6 min de leitura3 exemplos de código

  1. 01Pedir antes de precisar
  2. 02Prever a próxima linha: os passos
  3. 03Quanto vale um pré-buscador
  4. 04Três jeitos de ler os fechamentos do SPY
  5. 05O que você vai construir

Uma falta só custa 80 ns se o programa tiver de esperar por ela. Um pré-buscador pede uma linha antes do programa, de modo que a espera já acabou quando a leitura chega. Esta aula constrói dois pré-buscadores, os avalia com cobertura e precisão, e mostra onde funcionam, em laços com passo regular, e onde não conseguem, em leituras em ordem imprevisível.

01

Pedir antes de precisar

Até agora o cache carregava uma linha quando uma leitura falhava. Uma pré-busca a carrega antes. Se o palpite estiver certo, a leitura que vem depois encontra a linha já no cache e acerta. Todo processador moderno tem pré-buscadores de hardware que vigiam os endereços das leituras e adivinham o que vem. O palpite mais simples: quem leu a linha L vai querer a linha L + 1 em seguida. Nosso cache fornecido tem um método fill que carrega uma linha sem contar acerto nem falta, que é exatamente uma pré-busca.

main.pyPython
def baseline_misses(trace):    cache = Cache()    for address in trace:        cache.access(address)    return cache.missesdef next_line_misses(trace):    cache = Cache()    for address in trace:        cache.access(address)        cache.fill((address // LINE_BYTES + 1) * LINE_BYTES)1    return cache.missestrace = [8 * i for i in range(800)]print("no prefetch:", baseline_misses(trace), "- next line:", next_line_misses(trace))jumps = [i * 4096 for i in range(50)]print("jumps of 4096 bytes:", baseline_misses(jumps), "- next line:", next_line_misses(jumps))
no prefetch: 100 - next line: 1
jumps of 4096 bytes: 50 - next line: 50
  1. 1Pré-buscar a linha seguinte não custa nada aqui: fill não conta como acerto nem como falta.

Lendo 800 valores em ordem, um cache simples falha uma vez por linha, 100 vezes. Com o pré-buscador da linha seguinte ele falha uma vez no total: depois da primeiríssima leitura, a linha de que se precisa em seguida já está sempre lá. O segundo caso mostra o limite. Se o programa pula 4096 bytes de cada vez, a linha depois da atual é inútil e o pré-buscador não economiza nada: continuam 50 faltas em 50 leituras.

02

Prever a próxima linha: os passos

Os pré-buscadores reais procuram um padrão nas linhas que um programa lê. O mais comum é um passo constante: o programa lê as linhas 0, 3, 6, 9, pulando três linhas cada vez. Um detector de passos lembra a última linha e o último salto. Quando o novo salto é igual ao anterior, é um padrão e a próxima linha é a atual mais o salto. São precisos dois saltos iguais para ter certeza, então ele nunca adivinha nas duas primeiras leituras.

  1. 1
    Ignorar uma repetiçãoLer a mesma linha de novo não diz nada sobre um passo.
  2. 2
    Calcular o saltoA linha nova menos a última linha.
  3. 3
    Comparar com o último saltoSaltos iguais significam um padrão: preveja a linha nova mais o salto.
  4. 4
    LembrarGuarde o novo salto e a nova linha para a próxima leitura.
main.pyPython
class StridePrefetcher:    def __init__(self):        self.last_line = None        self.last_delta = None    def observe(self, line):        if line == self.last_line:            return None        prediction = None        if self.last_line is not None:            delta = line - self.last_line            if delta == self.last_delta and line + delta >= 0:1                prediction = line + delta            self.last_delta = delta        self.last_line = line        return predictionp = StridePrefetcher()print([p.observe(line) for line in [0, 3, 6, 9, 12]])p = StridePrefetcher()print([p.observe(line) for line in [5, 5, 5, 6, 7]])
[None, None, 9, 12, 15]
[None, None, None, None, 8]
  1. 1Dois saltos iguais seguidos: é um passo, então a próxima linha é a atual mais o salto.

A primeira sequência é um passo de três linhas. O detector não diz nada nas duas primeiras leituras e depois prevê 9, 12 e 15, sempre um passo à frente. Na segunda sequência as leituras repetidas da linha 5 são ignoradas e o passo de 1 só é confirmado na última leitura. Um bom detector é cauteloso: um palpite errado tem um preço, como veremos a seguir.

03

Quanto vale um pré-buscador

Dois números avaliam um pré-buscador. A cobertura diz quanto do problema ele resolveu. A precisão diz quanto do seu trabalho foi útil. Para medi-los passamos o rastro pelo cache, fazemos uma leitura, perguntamos ao pré-buscador o que carregar em seguida, lembramos quais linhas foram pré-buscadas e contamos quando uma leitura acerta numa delas.

coverage = 1 − misses with ÷ misses without accuracy = useful ÷ issued(1)a cobertura trata das faltas do programa, a precisão do trabalho do próprio pré-buscador
main.pyPython
def run_with_prefetcher(trace, prefetcher):    cache = Cache()    marked = set()    prefetches = 0    useful = 0    for address in trace:        line = address // LINE_BYTES        if cache.access(address):            if line in marked:                useful += 1                marked.discard(line)        else:            marked.discard(line)        target = prefetcher.observe(line)        if target is not None and not cache.contains(target * LINE_BYTES):            cache.fill(target * LINE_BYTES)            prefetches += 1            marked.add(target)    return {"demand_misses": cache.misses, "prefetches": prefetches, "useful": useful}def quality(trace):    baseline = baseline_misses(trace)    run = run_with_prefetcher(trace, StridePrefetcher())    coverage = 1 - run["demand_misses"] / baseline    accuracy = run["useful"] / run["prefetches"] if run["prefetches"] else 0.0    return baseline, run, round(coverage, 3), round(accuracy, 3)import randomordered = [8 * i for i in range(2000)]shuffled = [8 * p for p in random.Random(1).sample(range(2000), 2000)]print("in order:", quality(ordered))print("shuffled:", quality(shuffled))
in order: (250, {'demand_misses': 3, 'prefetches': 248, 'useful': 247}, 0.988, 0.996)
shuffled: (250, {'demand_misses': 250, 'prefetches': 1, 'useful': 0}, 0.0, 0.0)

Na leitura em ordem o pré-buscador elimina 98,8% das faltas e 99,6% das suas pré-buscas são usadas. Na leitura embaralhada ele não faz nada: sem um passo repetido quase nunca emite uma pré-busca (só uma), e assim também não faz mal. É o comportamento desejado. Um pré-buscador que adivinhasse o tempo todo encheria o cache de linhas que ninguém lê.

Um palpite errado tem um preço

Uma pré-busca inútil gasta largura de banda de memória e pode expulsar do cache uma linha de que o programa ainda precisa. Um pré-buscador só vale a pena quando sua precisão é alta. Nosso cache aqui é grande o bastante para esconder o dano; num cache pequeno ou num sistema de memória ocupado, um pré-buscador que erra deixa um programa mais lento do que sem pré-buscador.

04

Três jeitos de ler os fechamentos do SPY

O laboratório lê 2000 fechamentos de três jeitos: guardados um após o outro e lidos em ordem, guardados dentro de linhas de cinco valores (um passo de 40 bytes), e guardados um após o outro mas lidos em ordem de volume de negociação crescente. Os dois primeiros têm um padrão regular; o terceiro visita os dias numa ordem que parece aleatória, porque o volume de um dia não diz nada sobre o volume do seguinte.

O que dizem os dados reaisprimeiros 2000 fechamentos do SPY, cache de 32 KB de 8 vias, pré-buscador de passos
Em ordem: faltas250 → 3cobertura 99%, precisão 100%
Em linhas de 5: faltas1,250 → 3um passo de 40 bytes também é achado
Por volume crescente: faltas250 → 250cobertura 0%: nenhum padrão a achar
Pré-buscas na ordem do volume1fica quieto quando não consegue prever
Cobertura do pré-buscador de passos, em %
Em ordem98.8
Em linhas de 599.8
Por volume0

A pista está na pergunta que o pré-buscador responde: o que vem a seguir? Se a resposta está no padrão dos endereços, o hardware a acha de graça. Se a ordem depende dos dados, como ao visitar linhas ordenadas por volume, nada consegue adivinhá-la. A correção por software é mudar a disposição dos dados para que o acesso volte a ser regular, por exemplo ordenando as linhas uma vez e depois lendo-as em ordem.

05

O que você vai construir

O laboratório constrói os dois pré-buscadores, a simulação que os avalia e o estudo sobre os três jeitos de ler os fechamentos do SPY.

  • Função · perguntaBaseia-se em
  • baseline_misses(trace)Quantas faltas sem pré-busca?Cache
  • next_line_misses(trace)Quantas com um pré-buscador da linha seguinte?Cache.fill
  • StridePrefetcher().observe(line)Qual linha vem a seguir?A última linha e o último salto
  • run_with_prefetcher(trace, prefetcher)O que aconteceu: faltas, pré-buscas, úteis?Cache
  • quality(trace)Que cobertura e precisão ele atinge?baseline_missesrun_with_prefetcher
  • study(bars)Onde o pré-buscador ajuda nos fechamentos do SPY?qualitynext_line_misses

Pontos-chave

  • A pré-busca esconde uma falta. Ele pede uma linha antes da leitura, então a leitura a encontra no cache e acerta; a falta ainda é paga, mas mais cedo.
  • Precisa de um padrão. Um detector de passos prevê saltos constantes entre linhas; leituras numa ordem que depende dos dados não lhe dão nada a achar.
  • Avalie-o com dois números. A cobertura é a parte das faltas removidas; a precisão é a parte das pré-buscas usadas. Precisão baixa significa largura de banda e espaço de cache desperdiçados.
Verificação rápida
Um laço lê uma linha a cada três de um array (um passo de 3 linhas). O que um pré-buscador da linha seguinte faz por ele?
Escolha uma resposta.

Próximo tópico: até agora um endereço era uma linha e só importava um acerto de cache. Em TLB e páginas grandes todo endereço precisa antes ser traduzido de uma página virtual para uma física, e um pequeno cache de traduções, o TLB, também pode falhar, de um jeito que depende do tamanho da página.

Lab: o que é avaliado

6 tarefas · 100 pts
  • Sem pré-busca10 pts
  • Um pré-buscador da linha seguinte15 pts
  • Um pré-buscador de passos20 pts
  • Simular um pré-buscador25 pts
  • Cobertura e precisão15 pts
  • O estudo com a tabela do SPY15 pts
Explorador · lab-cl-7
▾ lab-cl-7
▾ data
…
bash · no navegador
learner@quantcraft:~/lab-cl-7$
Python ainda não carregadoPythonLn 1, Col 1Spaces: 4UTF-8

Redefinir este tópico?

Tudo em lab-cl-7 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