地标是OCAML的简单分析库。它提供了原始图,以划定代码的部分并在运行时测量仪器代码的性能。可用的措施是通过汇总CPU周期(使用CPU的时间邮票计数器),应用时间(使用Sys.time
)和分配的字节(使用Gc.allocated_bytes
)来获得的。代码的仪器可以使用PPX扩展程序自动或半自动手工完成。
在执行程序期间,控制流的仪器代码遍历被记录为带有收集措施的“呼叫图”。结果可以直接浏览在控制台上,也可以导出到JSON。
该工具旨在将其用作查找在程序中花费的时间(而不是基准的独立代码(例如core_bench))的方式,而提供的结果仅与OCAML代码的仪器相对应(与工具相反)直接与二进制可执行文件(如gprof或perf)一起使用。
有关更多信息,您可以浏览API。
该库分为两个软件包:运行时库的landmarks
和实施自动仪器的预处理器的landmarks-ppx
。
opam install landmarks
或者
opam install landmarks-ppx
将安装运行时库或运行时库和预处理器。
git clone https://github.com/LexiFi/landmarks.git
cd landmarks
dune build @install
只需使用图书馆landmarks
和预处理区landmarks-ppx
来基准您的可执行文件和库即可。例如,以下dune
文件使用landmarks
库及其PPX构建可执行test
。可选的--auto
标志打开自动仪器(见下文)。
(executable
(name test)
(libraries landmarks)
(preprocess (pps landmarks-ppx --auto))
)
可以使用沙丘自动触发项目的仪器。请查看Lexifi/Landmarks-Starter的基本示例,并查看《沙丘手册》以获取更多信息。
ocamlfind ocamlopt -c -package landmarks prog.ml
ocamlfind ocamlopt -o prog -package landmarks -linkpkg prog.cmx
您可以用“ Ocamlc”替换“ Ocamlopt”,以在字节码中编译程序。
ocamlfind ocamlopt -c -package landmarks -package landmarks-ppx -ppxopt landmarks-ppx,--auto prog.ml
ocamlfind ocamlopt -o prog -package landmarks -linkpkg prog.cmx
请注意,“ -ppxopt Landmarks-PPX, - 自动”是可选的,并且可以打开自动仪器。
有三个主要原则:
val register : string -> landmark
val enter : landmark -> unit
val exit : landmark -> unit
register
功能声明了新的地标,应在Toplevel上使用。 enter
和exit
功能用于界定附属于地标的代码部分。在分析结束时,我们检索每个地标的总汇总时间信息,用于执行相应的代码。在执行过程中,还记录了每个访问的地标的痕迹,以构建“呼叫图”。
例如:
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
(可以使用ocamlfind ocamlc -o prog -package landmarks -package unix -linkpkg prog.ml
编译此文件)
诱导的呼叫图是:
- 100.00% : main
| - 90.00% : loop
| | - 100.00% : sleep
| - 10.00% : sleep
可以解释为:
clock()
功能该库为X86 32和64位体系结构提供了对高性能周期计数器的绑定(请注意,您可以使用landmarks-noc.cm(x)a
档案库来提供您自己的实现)。它用于测量仪器代码中花费的时间。
为了避免编写样板代码,您可以使用此软件包分布式的PPX扩展名。它允许程序员使用注释来仪器表达式并自动仪器顶级功能。
值expr [@landmark "name"]
扩展到
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
和声明
let __generated_landmark_1 = Landmark. register " name "
附加在顶级。
应该指出的是,这种转变不能保留尾部回调的呼叫(并且也防止了某些多态性概括)。为了解决let rec ... in
问题,建议使用其他提供的扩展名let ... in
let [ @ landmark] f = body
扩展了:
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
当通过计算有趣的浅水出现来获得f
的ARITH n
时fun ... ->
和function ... ->
在body
中。请注意,将此注释与LET-REC绑定一起使用时,仅记录入口点调用。例如,在以下代码中
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 )
与“偶数”相关的地标将精确地穿越一次(不是三遍!),而控制流将不会穿过与“奇数”相关的地标。
The structure annotations [@@@landmark "auto"]
and [@@@landmark "auto-off"]
activate or deactivate the automatic instrumentation of top-level functions in a module.在自动模式下,所有函数声明都是隐式注释的。如下所述,可以通过OCAML_LANDMARKS
中的OPAML_LANDMARKS中的Option auto
启用/禁用所有文件的自动仪器。
环境变量OCAML_LANDMARKS
在两个不同的阶段读取:执行PPX重写器以及Landmarks模块被仪器程序加载时。该变量被解析为表单option=argument
或option
的逗号分隔列表,其中option
是:
在PPX重写阶段(在编译时):
auto
(无参数):默认情况下打开自动仪器(表现得好像每个模块以注释开头[@@@landmark "auto"]
)。
threads
(无参数):告诉PPX扩展程序使用Landmark_threads
模块而不是模块Landmark
。
加载仪器程序(在运行时)时:
format
具有可能的参数: textual
(默认)或json
。它控制分析的输出格式,该格式是控制台友好表示形式,也可以是callgraph的JSON编码。
threshold
在0.0到100.0之间为参数(默认值:1.0)。如果阈值不为零,则文本输出将在此阈值以下的呼叫图中隐藏节点(以其父级的百分比为单位)。对于其他格式,此选项毫无意义。
output
可能参数: stderr
(默认), stdout
, temporary
, <file>
(其中<file>
是文件的路径)。它讲述了在哪里输出分析结果。使用temporary
它将将其打印在临时文件中(该文件的名称将在标准错误上打印)。您也可以使用temporary:<directory>
指定文件生成的目录。
没有争论的debug
。激活详细模式,每次调用地标原始图。
没有争论的time
。还可以在分析过程中收集Sys.time
时间戳。
没有off
。禁用分析。
on
争论。启用分析(默认;可能会省略)。
allocation
没有参数。还收集Gc.allocated_byte
数据。
您可以在计算机上编译网络查看器,也可以在线浏览。您需要使用FilePicker加载JSON文件,然后单击以浏览呼叫图。
Landmark
模块不是线程安全。如果您有多个线程,则必须确保最多有一个线程是执行仪器代码。为此,您可以使用Landmark_threads
模块(包含在Landmarks-threads.cm(x)A档案中),该模块可以防止在所有线程中执行的非线程安全函数,但开始进行分析。
对表达的注释可能会随着多态性的速度(释放结束注释不是这种情况)。例如,以下代码将无法编译:
let test = ( fun x -> x)[ @ landmark " test " ]
in test " string " , test 1
此“地标”包由Lexifi根据MIT许可的条款许可。