σμ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ᵀμ = μ*∫Σ∂∞λ
σμ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 AcademyRoteiros
Ctrl K
AO
+1,2 mil alunos
EntrarParticipar ↗
Roteiros
AO
+1,2 mil alunos
Entrar
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Roteiro de Python›Módulo 10 · Engenharia Python de alto desempenho›Tópico 10.7·~1 hAvançado

Cython

Posição no roteiroMódulo 10 de 11 · Tópico 7 de 13
10.7 Cython
—Nota do lab— / 100XP total—Sequência—

Python com tipos C, compilado

Cython é um compilador para uma linguagem que é Python mais declarações opcionais de tipos C. Você escreve um arquivo .pyx, o Cython o traduz para C, um compilador C o transforma em um módulo de extensão, e você o importa como qualquer outro módulo. Código Python simples compilado assim já roda um pouco mais rápido, mas o ganho real vem ao declarar tipos: cdef double total diz ao Cython que total é um número de máquina, não um objeto Python, de modo que o laço trabalha com números crus sem custo do interpretador. Laços numéricos pesados podem ficar de cinquenta a cem vezes mais rápidos, como com o Numba, mas você obtém um módulo compilado comum que pode distribuir.

# What a Cython file looks like: Python with cdef type declarations (shown here as text)
pyx = """\
cpdef double dot(double[:] a, double[:] b):
    cdef double total = 0.0
    cdef long i
    for i in range(a.shape[0]):
        total += a[i] * b[i]
    return total
"""
print(pyx.splitlines()[0])                           # cpdef double dot(double[:] a, double[:] b):
print(sum("cdef" in line for line in pyx.splitlines()))     # 2   two typed declarations inside the function

Declarar tipos: cdef, cpdef e views de memória

cdef declara uma variável C (cdef double total, cdef long i) ou uma função que só código C pode chamar. cpdef declara uma função chamável de Python e de C, o que serve aos pontos de entrada de um módulo. def continua sendo uma função Python comum. Arrays são passados como views de memória tipadas, como double[:] para um array unidimensional de doubles: aceitam arrays do NumPy sem cópia e são indexadas na velocidade do C. Uma assinatura como cpdef double mean(double[:] x, long n) troca assim os tipos dinâmicos do Python por tipos exatos, e um tipo omitido, ou declarado object, continua sendo um objeto Python lento.

# Building the declarations as text: one table from Python type names to C types
C_TYPES = {"int": "long", "float": "double", "bool": "bint", "array[float]": "double[:]"}

def c_type(py_type):
    return C_TYPES.get(py_type, "object")

def cdef_signature(name, params, returns):
    result = "void" if returns is None else c_type(returns)
    arguments = ", ".join(f"{c_type(t)} {p}" for p, t in params)
    return f"cpdef {result} {name}({arguments})"

print(cdef_signature("mean", [("x", "array[float]"), ("n", "int")], "float"))    # cpdef double mean(double[:] x, long n)
print(cdef_signature("log_event", [("message", "dict")], None))                  # cpdef void log_event(object message)

Construir o módulo e descobrir o que tipar

Um setup.py com cythonize(["pricing.pyx"]) diz ao setuptools para traduzir e compilar o arquivo; python setup.py build_ext --inplace produz uma biblioteca compartilhada ao lado do seu código, e import pricing a carrega. Para testes rápidos, o pyximport compila na importação. Para ver o que ainda é lento, rode cython -a pricing.pyx: ele escreve uma página HTML em que cada linha é sombreada de amarelo conforme a quantidade de maquinaria Python que usa, e as linhas brancas são C puro. Tipe as variáveis das linhas amarelas dentro dos laços críticos e depois desligue as verificações que você provou desnecessárias com @cython.boundscheck(False) e @cython.wraparound(False) para que indexar custe tão pouco quanto em C.

# The build script is a few lines of text; this generates it for a list of modules
def build_setup(modules):
    files = ", ".join(f'"{module}.pyx"' for module in modules)
    return "from setuptools import setup\nfrom Cython.Build import cythonize\n\n" + f"setup(ext_modules=cythonize([{files}]))\n"

print(build_setup(["pricing", "risk"]), end="")
# from setuptools import setup
# from Cython.Build import cythonize
#
# setup(ext_modules=cythonize(["pricing.pyx", "risk.pyx"]))
# The loop a .pyx file would hold, as plain Python: same result, compiled once types are added
def dot(a, b):
    total = 0.0
    for i in range(len(a)):
        total += a[i] * b[i]
    return total

prices = [184.25, 410.5, 99.0]
quantities = [100.0, 50.0, 200.0]
print(dot(prices, quantities))                       # 58750.0  the compiled version returns exactly this number
print(dot(prices, quantities) == sum(p * q for p, q in zip(prices, quantities)))     # True
Cython, Numba ou nenhum?O Numba é a escolha leve: um decorador, sem etapa de build, ideal para funções numéricas. O Cython custa uma etapa de build e um segundo tipo de arquivo, mas dá um módulo normal que se distribui sem a partida do JIT e permite chamar bibliotecas C e misturar livremente código C e Python. Tente primeiro a vetorização com NumPy, depois o Numba, depois o Cython; e em todos os casos compare a saída com a versão Python simples antes de confiar.
  • O Cython compila um arquivo .pyx (Python mais tipos C opcionais) para C e depois para um módulo de extensão que se importa normalmente.
  • cdef declara variáveis C e funções só de C, cpdef uma função chamável de Python e C; arrays são views de memória tipadas como double[:].
  • Construa com cythonize no setup.py, use cython -a para achar as linhas amarelas (lentas) em laços críticos e verifique com o resultado do Python simples.

A seguir: chamar C++ com pybind11, expor código C++ existente ao Python.

Lab: o que é avaliado

  • Mapear tipos Python para C com c_type25 pts
  • Escrever o cabeçalho de uma função com cdef_signature25 pts
  • Tipar as variáveis locais com declare_locals25 pts
  • Escrever o script de build com build_setup25 pts
Explorador · lab-123-1
▾ lab-123-1
…
bash · no navegador
learner@quantcraft:~/lab-123-1$
Python ainda não carregadoPythonLn 1, Col 1Spaces: 4UTF-8

Redefinir este tópico?

Tudo em lab-123-1 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

Entre para executar o seu código.

Todas as aulas são gratuitas para ler. Crie uma conta ou entre para executar código, rodar os testes e enviar este lab.

  • IDE no navegador com terminal bash — nada para instalar
  • Correção instantânea, XP e o seu lugar no roteiro
Criar uma contaEntrar
OU
Continuar com o GitHub