Marcos é uma biblioteca simples de perfil para o OCAML. Ele fornece primitivas para delimitar partes do código e medir o desempenho do código instrumentado em tempo de execução. As medidas disponíveis são obtidas pela agregação de ciclos da CPU (usando o contador de carimbos de data e hora da CPU), tempo de aplicação (usando Sys.time
) e bytes alocados (com Gc.allocated_bytes
). A instrumentação do código pode ser feita manualmente, automaticamente ou semi-automaticamente usando uma extensão PPX.
Durante a execução do seu programa, a travessia do código instrumentada pelo fluxo de controle é registrada como um "callgraph" que carrega as medidas coletadas. Os resultados podem ser navegados diretamente no console ou exportados para o JSON.
Essa ferramenta deve ser usada como uma maneira de descobrir onde o tempo é gasto em seus programas (e não em partes independentes de código como Core_bench), fornecendo resultados que correspondem apenas à parte instrumentada do seu código OCAML (ao contrário das ferramentas que Trabalhe diretamente com o executável binário como GProf ou Perf).
Para mais informações, você pode navegar na API.
A biblioteca é dividida em dois pacotes: landmarks
para a biblioteca de tempo de execução e landmarks-ppx
para o pré-processador que implementa instrumentação automática.
opam install landmarks
ou
opam install landmarks-ppx
instalará a biblioteca de tempo de execução ou a biblioteca de tempo de execução e o pré -processador.
git clone https://github.com/LexiFi/landmarks.git
cd landmarks
dune build @install
Basta usar os landmarks
da biblioteca e os landmarks-ppx
para comparar seus executáveis e bibliotecas. Por exemplo, o arquivo dune
a seguir cria o test
executável usando a biblioteca landmarks
e seu PPX. A bandeira opcional --auto
liga a instrumentação automática (veja abaixo).
(executable
(name test)
(libraries landmarks)
(preprocess (pps landmarks-ppx --auto))
)
É possível usar o Dune para acionar automaticamente a instrumentação de um projeto. Dê uma olhada no Lexifi/Markmarks-Starter para um exemplo básico e consulte o Manual da Dune para obter mais informações.
ocamlfind ocamlopt -c -package landmarks prog.ml
ocamlfind ocamlopt -o prog -package landmarks -linkpkg prog.cmx
Você pode substituir "OCAMLOT" por "OCAMLC" para compilar o programa em ByteCode.
ocamlfind ocamlopt -c -package landmarks -package landmarks-ppx -ppxopt landmarks-ppx,--auto prog.ml
ocamlfind ocamlopt -o prog -package landmarks -linkpkg prog.cmx
Observe que "-ppxopt markmarks-ppx,-auto" é opcional e liga a instrumentação automática.
Existem três primitivas principais:
val register : string -> landmark
val enter : landmark -> unit
val exit : landmark -> unit
A função register
declara novos marcos e deve ser usada no Toplevel. As funções enter
e exit
são usadas para delimitar a parte do código anexado a um marco. No final do perfil, recuperamos para cada marco as informações de tempo agregadas gastas executando a peça de código correspondente. Durante a execução, também é registrado um traço de cada marco visitado para construir um "callgraph".
Por exemplo:
open Landmark
let loop = register " loop "
let sleep = register " sleep "
let main = register " main "
let zzz () =
enter sleep;
Unix. sleep 1 ;
exit sleep
let () =
begin
start_profiling () ;
enter main;
enter loop;
for _ = 1 to 9 do
zzz ()
done ;
exit loop;
zzz () ;
exit main;
end
(Este arquivo pode ser compilado com ocamlfind ocamlc -o prog -package landmarks -package unix -linkpkg prog.ml
)
O CallGraph Induzido é:
- 100.00% : main
| - 90.00% : loop
| | - 100.00% : sleep
| - 10.00% : sleep
que pode ser parafraseado como:
clock()
A biblioteca fornece uma ligação ao contador de ciclos de alto desempenho para arquiteturas x86 32 e 64 bits (observe que você pode usar os landmarks-noc.cm(x)a
arquivo para fornecer sua própria implementação). É usado para medir o tempo gasto dentro do código instrumentado.
Para evitar a gravação de código de caldeira, você pode usar a extensão PPX distribuída com este pacote. Ele permite que o programador instrumente expressões usando a anotação e instrumente automaticamente as funções de nível superior.
O valor expr [@landmark "name"]
é expandido para
Landmark. enter __generated_landmark_1;
let r =
try expr with e -> Landmark. exit __generated_landmark_1; raise e
in
Landmark. exit __generated_landmark_1;
r
e a declaração
let __generated_landmark_1 = Landmark. register " name "
é anexado no nível superior.
Deve-se ressaltar que essa transformação não preserva chamadas recursivas à cauda (e também impede alguma generalização do polimorfismo). Para contornar esses problemas, é recomendável usar a outra extensão fornecida em torno de let ... in
e let rec ... in
:
let [ @ landmark] f = body
que é expandido em:
let __generated_landmark_2 = Landmark. register " f "
let f = body
let f x1 ... xn =
Landmark. enter __generated_landmark_2;
let r =
try f x1 ... xn with e -> Landmark. exit __generated_landmark_2; raise e
in
Landmark. exit __generated_landmark_2;
r
Quando a arity n
de f
é obtida contando as ocorrências rasas de fun ... ->
e function ... ->
no body
. Observe que, ao usar esta anotação com as ligações LET-REC, apenas as chamadas de entrada serão gravadas. Por exemplo, na seguinte parte do código
let () =
let [ @ landmark] rec even n = (n = 0 ) || odd (n - 1 )
and [ @ landmark] odd n = (n = 1 ) || n > 0 && even (n - 1 )
in Printf. printf " 'six is even' is %b n " (even 6 )
O marco associado a "par" será atravessado exatamente uma vez (e não três vezes!), Enquanto o fluxo de controle não passará pelo marco associado a "ímpares".
As anotações da estrutura [@@@landmark "auto"]
e [@@@landmark "auto-off"]
ativam ou desativam a instrumentação automática de funções de nível superior em um módulo. No modo automático, todas as declarações de funções são anotadas implicitamente. A instrumentação automática pode ser ativada/desativada para todos os arquivos via opção auto
em OCAML_LANDMARKS
, conforme detalhado abaixo.
A variável de ambiente OCAML_LANDMARKS
é lida em dois estágios diferentes: quando o reescrita do PPX é executado e quando o módulo marcos é carregado por um programa instrumentado. Essa variável é analisada como uma lista separada por vírgula de itens da option=argument
ou option
, onde option
é:
Durante o estágio de reescrita do PPX (no momento da compilação):
auto
(sem argumentos): atende a instrumentação automática por padrão (se comporta como se cada módulo comece com anotação [@@@landmark "auto"]
).
threads
(sem argumentos): informa à extensão PPX para usar o módulo Landmark_threads
em vez do Landmark
do módulo.
Ao carregar um programa instrumentado (em tempo de execução):
format
com possíveis argumentos: textual
(padrão) ou json
. Ele controla o formato de saída do perfil, que é uma representação amigável ao console ou codificação JSON do callgraph.
threshold
com um número entre 0,0 e 100,0 como argumento (padrão: 1.0). Se o limite não for zero, a saída textual ocultará nós no gráfico abaixo deste limite (em porcentagem do tempo de seus pais). Esta opção não tem sentido para outros formatos.
output
com possível argumento: stderr
(padrão), stdout
, temporary
, <file>
(onde <file>
é o caminho de um arquivo). Ele diz onde emitir os resultados do perfil. Com temporary
, ele o imprimirá em um arquivo temporário (o nome deste arquivo será impresso no erro padrão). Você também pode usar temporary:<directory>
para especificar o diretório onde os arquivos são gerados.
debug
sem argumento. Ativa um modo detalhado que produz traços no stderr cada vez que os primitivos de referência são chamados.
time
sem argumento. Colete também os registros de data e hora Sys.time
durante o perfil.
off
sem argumento. Desativar o perfil.
on
argumento. Ativar perfil (padrão; pode ser omitido).
allocation
sem argumento. Colete também dados Gc.allocated_byte
.
Você pode compilar o visualizador da web no seu computador ou navegar online. Você precisa carregar os arquivos JSON usando o FilePicker e, em seguida, clique para procurar o CallGraph.
O módulo Landmark
não é seguro para threads. Se você possui vários threads, precisará garantir que, no máximo, um thread esteja executando o código instrumentado. Para isso, você pode usar o módulo Landmark_threads
(incluído nos marcos-threads.cm (x) um arquivo) que impede que funções não seguras de threads sejam executadas em todos os threads, mas o que iniciou o perfil.
A anotação nas expressões pode temperamento com o polimorfismo (esse não é o caso da anotação de ligação). Por exemplo, a seguinte parte do código falhará em compilar:
let test = ( fun x -> x)[ @ landmark " test " ]
in test " string " , test 1
Este pacote de 'marcos' é licenciado pela Lexifi nos termos da licença do MIT.
Contato: [email protected]