Spec-Zone.ru › OCaml
☰Инструменты OCaml
  • Пакетная компиляция (ocamlc)
  • Система верхнего уровня или REPL (ocaml)
  • Система времени выполнения (ocamlrun)
  • Компиляция в машинный код (ocamlopt)
  • Генераторы лексических анализаторов и парсеров (ocamllex, ocamlyacc)
  • Генератор зависимостей (ocamldep)
  • Генератор документации (ocamldoc)
  • Отладчик (ocamldebug)
  • Отладка машинного кода (gdb, lldb)
  • Профилирование (ocamlprof, perf)
  • Взаимодействие C с OCaml
  • Оптимизация с помощью Flambda
  • Фаззинг с помощью afl-fuzz
  • Трассировка времени выполнения с помощью событий runtime
  • Преобразование программы «Tail Modulo Constructor»
  • Обнаружение гонок данных во время выполнения с помощью ThreadSanitizer

Глава 22. Профилирование (ocamlprof, perf)

В этой главе описаны различные способы профилирования выполнения программ OCaml.

1 Профилирование количества выполнений с помощью ocamlprof

В этом разделе описывается, как профилировать выполнение программ OCaml, записывая количество вызовов функций и выполнений ветвей условных конструкций. Этот подход применим к программам OCaml, скомпилированным с помощью бэкендов байт-кода и машинного кода.

1.1 Компиляция для профилирования

Перед профилированием выполнения программу необходимо скомпилировать в режиме профилирования с помощью интерфейсной программы ocamlcp для компилятора ocamlc (см. главу 13) или интерфейсной программы ocamloptp для компилятора ocamlopt (см. главу 16). При компиляции модулей по отдельности для их компиляции (создания файлов .cmo или .cmx) необходимо использовать ocamlcp или ocamloptp. Эти программы можно использовать и при их последующей компоновке (хотя это и не является строго необходимым).

Примечание

Если для модуля (файла .ml) нет соответствующего интерфейса (файла .mli), компиляция с помощью ocamlcp создаст объектные файлы (.cmi и .cmo), несовместимые с файлами, созданными с помощью ocamlc. Это может привести к проблемам (если файлы .cmi или .cmo остались) при переключении между компиляцией с профилированием и без него. Чтобы избежать этой проблемы, для каждого файла .ml всегда создавайте файл .mli. Такая же проблема существует и с ocamloptp.

Примечание

Чтобы гарантировать возможность компиляции программ в режиме профилирования, не используйте идентификаторы, начинающиеся с __ocaml_prof.

Объём собираемой информации о профилировании можно задать с помощью параметра -P для ocamlcp или ocamloptp, указав после него одну или несколько букв, обозначающих части программы, которые нужно профилировать:

a
все параметры
f
вызовы функций: точка подсчёта устанавливается в начале тела каждой функции
i
if …then …else …: точки подсчёта устанавливаются в ветвях then и else
l
циклы while, for: точка подсчёта устанавливается в начале тела цикла
m
ветви match: точка подсчёта устанавливается в начале тела каждой ветви
t
ветви try …with …: точка подсчёта устанавливается в начале тела каждой ветви

Например, компиляция с помощью ocamlcp -P film позволяет профилировать вызовы функций, конструкции if…then…else…, циклы и сопоставление с образцом.

Если вызвать ocamlcp или ocamloptp без параметра -P, по умолчанию используется -P fm, то есть профилируются только вызовы функций и сопоставление с образцом.

Примечание

Для совместимости с предыдущими выпусками ocamlcp также принимает параметр -p с теми же аргументами и поведением, что и у -P.

Команды ocamlcp и ocamloptp также принимают все параметры соответствующего компилятора ocamlc или ocamlopt, кроме параметра -pp (предварительная обработка).

1.2 Профилирование выполнения

При запуске исполняемого файла, скомпилированного с помощью ocamlcp или ocamloptp, записывается число выполнений указанных частей программы. Эти данные сохраняются в файле ocamlprof.dump в текущем каталоге.

Если при завершении программы задана переменная окружения OCAMLPROF_DUMP, её значение используется в качестве имени файла вместо ocamlprof.dump.

Файл дампа записывается только при нормальном завершении программы (вызовом exit или при достижении конца программы). Он не записывается, если программа завершилась из-за необработанного исключения.

Если в текущем каталоге уже существует совместимый файл дампа, информация о профилировании добавляется в него. Это позволяет, например, профилировать несколько запусков программы с разными входными данными. Обратите внимание, что файлы дампа, созданные исполняемыми файлами с байт-кодом (скомпилированными с помощью ocamlcp), совместимы с файлами дампа, созданными исполняемыми файлами с машинным кодом (скомпилированными с помощью ocamloptp).

1.3 Вывод информации о профилировании

Команда ocamlprof выводит исходный код модулей программы, вставляя в него комментарии с количеством выполнений. Например,

        ocamlprof foo.ml

выводит исходный код модуля foo с комментариями, указывающими, сколько раз вызывались функции этого модуля. Естественно, эти сведения точны только в том случае, если после компиляции исходный файл не изменялся.

Команда ocamlprof распознаёт следующие параметры:

-args filename
Прочитать из файла filename дополнительные аргументы командной строки, разделённые символами перевода строки.
-args0 filename
Прочитать из файла filename дополнительные аргументы командной строки, разделённые нулевыми символами.
-f dumpfile
Указать альтернативный файл дампа с информацией о профилировании, который нужно прочитать.
-F string
Указать дополнительную строку, выводимую вместе с информацией о профилировании. По умолчанию ocamlprof добавляет в программы комментарии вида (* n *), где n — значение счётчика в точке профилирования. С параметром -F s аннотация будет иметь вид (* sn *).
-impl filename
Обработать файл filename как файл реализации, даже если его расширение не .ml.
-intf filename
Обработать файл filename как файл интерфейса, даже если его расширение не .mli.
-keywords version+list
Задать ключевые слова согласно спецификации version+list.
-version
Вывести строку с версией и завершить работу.
-vnum
Вывести краткий номер версии и завершить работу.
-help или --help
Вывести краткую справку по использованию и завершить работу.

Профилирование с помощью ocamlprof записывает только количество выполнений, но не фактическое время, затраченное на выполнение каждой функции. В настоящее время измерять время выполнения программ с байт-кодом, созданных с помощью ocamlc, нельзя.

2 Профилирование времени с помощью Linux perf

В этом разделе описано, как использовать perf для профилирования программ OCaml. Linux Performance Events (также известный как perf) — набор инструментов для наблюдения за производительностью, входящий в состав ядра Linux. Здесь рассматриваются основные возможности: запись событий с помощью perf-record(1) и визуализация записанных событий с помощью perf-report(1). У perf есть множество дополнительных возможностей профилирования и визуализации; более подробную документацию см. в man-странице perf(1), вики perf или блоге Брендана Грегга.

2.1 Общие сведения

Профилирование времени обычно выполняется путём частого снятия выборок графа вызовов ЦП для сбора статистики о путях выполнения кода, потребляющих ресурсы ЦП. Чтобы профилировать код OCaml, perf должен понимать граф вызовов программы OCaml. perf поддерживает несколько способов записи графов вызовов:

  • Указатели кадров стека — способ по умолчанию.
  • Информация DWARF о кадрах вызовов (CFI).
  • Аппаратный регистр последних переходов (LBR).

Графы вызовов на основе указателей кадров стека используют соглашение, согласно которому регистр, называемый указателем кадра стека, хранит адрес начала кадра стека, а предыдущие значения указателя хранятся по известному смещению относительно текущего указателя. На основе этих данных граф вызовов текущей функции можно вычислить, используя только текущий стек. Этот процесс называется раскруткой стека. В AMD64 для хранения указателя кадра стека используется регистр $rbp, а в ARM64 — регистр $x29. В OCaml 5 в рамках реализации эффектов появились несмежные стеки; см. Retrofitting effect handlers onto OCaml (раздел 5.5). Из-за этой особенности реализации указатели кадров стека лучше работают с копирующим механизмом perf.

Графы вызовов на основе DWARF используют сведения DWARF CFI для раскрутки стека. Однако они плохо сочетаются с копирующим механизмом perf: графы вызовов часто оказываются усечёнными, поскольку perf не успевает скопировать достаточную часть стека. Кроме того, при этом создаются более объёмные файлы трассировки, запись и обработка которых требуют больше ресурсов. Наконец, для этого необходимо включить в программу отладочную информацию CFI, что немного увеличивает размер двоичных файлов.

Аппаратный регистр последних переходов (LBR) использует предоставляемый процессором механизм записи графов вызовов. У этого способа есть ограничения: он доступен только на некоторых процессорах Intel, а глубина стека ограничена (16 для Haswell и 32 начиная с Skylake).

Для профилирования кода OCaml рекомендуется использовать указатели кадров стека. Этот способ обладает следующими преимуществами:

  • Раскрутка стека вычисляется быстрее.
  • Создаются файлы трассировки меньшего размера.
  • Указатели кадров стека позволяют получать более полные графы вызовов, особенно при использовании дистрибутива Linux, в котором они поддерживаются.
  • Указатели кадров стека лучше сочетаются с копирующим механизмом perf и несмежными стеками OCaml 5.

2.2 Компиляция для профилирования

Процесс компиляции исполняемого файла для профилирования времени зависит от версии OCaml. В OCaml 4.14 и более ранних версиях можно использовать указатели кадров стека или DWARF, а начиная с OCaml 5.0 рекомендуется включать указатели кадров стека.

Чтобы включить указатели кадров стека, настройте компилятор с параметром --enable-frame-pointers. Также можно установить переключатель opam с включёнными указателями кадров стека следующим образом:

        opam switch create <YOUR-SWITCH-NAME-HERE> ocaml-option-fp

Поддержка указателей кадров стека в OCaml доступна для архитектуры AMD64 в Linux начиная с OCaml 4.12 и в macOS начиная с OCaml 5.3. Архитектура ARM64 поддерживается в Linux и macOS начиная с OCaml 5.4, тогда как другие архитектуры Tier-1 (POWER, RISC-V и s390x) в настоящее время не поддерживаются.

2.3 Профилирование выполнения

Основная команда perf для профилирования выглядит так:

        perf record -F 99 --call-graph fp PROGRAM

Параметр -F 99 задаёт частоту выборки perf 99 Гц, уменьшая избыточное создание данных при длительных запусках и сводя к минимуму совпадения с другими периодическими процессами. Параметр --call-graph fp указывает perf использовать указатели кадров стека для получения графа вызовов; далее указывается исполняемый файл OCaml, который нужно профилировать. Эта команда создаёт файл perf.data в текущем каталоге. Чтобы выбрать более информативное имя файла, используйте параметр --output.

Команда perf record работает, копируя часть стека вызовов при каждой выборке и записывая эти данные в файл perf.data. После записи эти выборки можно обработать с помощью perf report, чтобы восстановить стек вызовов профилируемой программы для каждой выборки.

perf использует символы, присутствующие в исполняемом файле OCaml, поэтому для сопоставления имён с расположениями в исходном коде полезно понимать схему изменения имён в OCaml.

Начиная с OCaml 5.3 в Linux используется схема изменения имён caml<MODULE_NAME>.<FUNCTION_NAME>_<NNN>, где NNN — случайно сгенерированное число. В macOS и порте Windows MSVC (восстановленном в OCaml 5.3) в качестве разделителя используется $. До OCaml 5.1.1 в качестве разделителя использовались два символа подчёркивания, например caml<MODULE_NAME>__<FUNCTION_NAME>_<NNN>. Linux-версия perf поддерживает все форматы.

Рассмотрим следующую программу:

(* fib.ml *)
module Compute = struct
  let rec fib n =
    if n = 0 then 0
    else if n = 1 then 1
    else fib (n-1) + fib (n-2)
end

let main () =
  let r = Compute.fib 20 in
  Printf.printf "fib(20) = %d" r

let _ = main ()

Эта программа создаёт имена camlFib.main_274 для функции main и camlFib.fib_271 для функции fib в модуле Compute.

2.4 Просмотр информации о профилировании

Команда perf report сводит воедино данные из файла perf.data. Основная команда perf report выглядит так:

        perf report -f --no-children -i perf.data

Эта команда предоставляет интерактивный интерфейс для просмотра накопленных графов вызовов и выбора функций и потоков, сведения о которых нужно изучить подробнее. В качестве альтернативы параметр --stdio выводит аналогичные данные в текстовом отчёте в stdout. Обратите внимание: если трассировки стека выглядят некорректно, возможно, программное обеспечение было собрано без поддержки указателей кадров стека.

Рассмотрим следующую программу, вычисляющую функцию Таке.

let (x,y,z) =
  try
    let x = int_of_string Sys.argv.(1) in
    let y = int_of_string Sys.argv.(2) in
    let z = int_of_string Sys.argv.(3) in
    (x,y,z)
  with _ -> (18,12,6)

let rec tak x y z =
  if x > y then
    tak (tak (x-1) y z) (tak (y-1) z x) (tak (z-1) x y)
  else z

let main () =
  let r = tak x y z in
  Printf.printf "tak %d %d %d = %d\n" x y z r

let _ = main ()

Отчёт perf для неё может выглядеть следующим образом.

Samples: 809  of event 'cycles', Event count (approx.): 24701952617
  Overhead  Command     Shared Object      Symbol
-  100.00%  tak-fp.exe  tak-fp.exe         [.] Tak.tak_402
     Tak.tak_402
     Tak.tak_402
   + Tak.tak_402
     0.00%  tak-fp.exe  [kernel.kallsyms]  [k] 0xffffb9a5ff79d854
     0.00%  perf-exec   [kernel.kallsyms]  [k] 0xffffb9a5ff719c34

Для более интересной программы — решателя судоку, реализующего алгоритм возврата Норвига, — вывод может выглядеть следующим образом.

Samples: 7K of event 'cycles:P', Event count (approx.): 141874642467
  Overhead  Command          Shared Object      Symbol
-   35.90%  sudoku_norvig.e  sudoku_norvig.exe  [.] Sudoku_norvig._pow_747
   - 35.29% Sudoku_norvig._pow_747
      + 17.70% Sudoku_norvig.eliminate_value_760
      + 15.74% Sudoku_norvig._pow_747
      + 1.03% Sudoku_norvig.fun_1435
     0.61% Sudoku_norvig.eliminate_value_760
-   12.99%  sudoku_norvig.e  sudoku_norvig.exe  [.] BatList.findnext_1543
   - 12.00% BatList.findnext_1543
      - 11.71% BatList.find_all_1539
         + 8.07% Sudoku_norvig.fun_1421
         + 2.73% Sudoku_norvig.eliminate_948
         + 0.90% Sudoku_norvig.assign_949
-   10.63%  sudoku_norvig.e  sudoku_norvig.exe  [.] Sudoku_norvig.eliminate_value_760
   + 8.29% Sudoku_norvig.eliminate_value_760
   + 1.93% Sudoku_norvig._pow_747
+    9.50%  sudoku_norvig.e  [kernel.kallsyms]  [k] asm_exc_nmi
+    7.24%  sudoku_norvig.e  sudoku_norvig.exe  [.] Sudoku_norvig.fun_1435
+    5.06%  sudoku_norvig.e  sudoku_norvig.exe  [.] caml_modify
+    3.10%  sudoku_norvig.e  sudoku_norvig.exe  [.] Sudoku_norvig.fun_1360
+    2.11%  sudoku_norvig.e  sudoku_norvig.exe  [.] Sudoku_norvig.eliminate_948
+    2.00%  sudoku_norvig.e  sudoku_norvig.exe  [.] Stdlib.List.iter_366
+    1.54%  sudoku_norvig.e  sudoku_norvig.exe  [.] Sudoku_norvig.fun_1421
+    1.47%  sudoku_norvig.e  sudoku_norvig.exe  [.] caml_apply2
+    1.42%  sudoku_norvig.e  sudoku_norvig.exe  [.] BatList.find_all_1539
+    1.20%  sudoku_norvig.e  sudoku_norvig.exe  [.] Sudoku_norvig.fun_1366
+    1.09%  sudoku_norvig.e  sudoku_norvig.exe  [.] Stdlib.List.fold_left_380

Обратите внимание: в этой реализации явно есть возможности для оптимизации функции возведения двойки в степень _pow_747, использующей рекурсию; также присутствуют некоторые части среды выполнения OCaml, например caml_modify.

Данные профилирования также можно визуализировать в виде пламенных графов, которые выделяют наиболее часто встречающиеся пути выполнения в трассировках стека. Исходные скрипты stackcollapse-perf.pl и flamegraph.pl можно найти на веб-странице Брендана Грегга Flame Graphs; работают они следующим образом:

git clone https://github.com/brendangregg/FlameGraph
cd FlameGraph

# Collect the results into perf.data
perf record -F 99 --call-graph fp PROGRAM
perf script -i perf.data | ./stackcollapse-perf.pl out.folded
flamegraph.pl out.folded > flamegraph.svg ## Create the FlameGraph svg

В качестве альтернативы можно использовать inferno — порт инструментов Flame Graphs на Rust, работающий аналогичным образом и быстрее обрабатывающий большие файлы perf.

cargo install inferno

# Collect the results into perf.data
perf script -i perf.data | inferno-collapse-perf > stacks.folded
cat stacks.folded | inferno-flamegraph > flamegraph.svg

Некоторые инструменты perf (например, perf-report(1) и perf-annotate(1)) используют отладочную информацию DWARF для сопоставления символов с расположениями в исходном коде. Если вам нужны эти возможности, программу и её зависимости необходимо скомпилировать с параметром -g, чтобы включить отладочную информацию в исполняемый файл.

Записанные данные профилирования также можно обрабатывать с помощью perf script различными способами, онлайн-инструментов, например speedscope.app и profiler.firefox.com, или любых других инструментов, поддерживающих данные в формате perf.

2.5 Заключение

Для профилирования времени выполнения машинного кода рекомендуются стандартные инструменты, такие как perf, eBPF, DTrace или Instruments (в macOS). Чтобы эти инструменты работали эффективно, часто необходимо компилировать программы с включёнными указателями кадров стека. Профилирование с помощью gprof больше не поддерживается.

Включение указателей кадров стека может повлиять на производительность некоторых архитектур (для x86_64 было измерено снижение производительности до 10%). Пользователям этой возможности рекомендуется самостоятельно измерить её влияние на свои приложения.

3 Профилирование времени с помощью macOS Instruments

Instruments — анализатор и визуализатор производительности приложений, входящий в состав XCode для macOS. В него входит инструмент командной строки xctrace, который может собирать данные профилирования времени для программ OCaml; кроме того, имеется графическое приложение для запуска профилирования и визуализации его результатов. Здесь рассматривается только сбор данных профилирования времени с помощью xctrace. Дополнительные сведения об Instruments см. в документации Apple.

Для профилирования времени с помощью macOS Instruments действуют те же предварительные требования, что и для Linux perf. Объяснение графов вызовов см. в разделе Общие сведения ( 22.2.1) предыдущего раздела, а инструкции по настройке — в разделе Компиляция для профилирования ( 22.2.2).

Основная команда xctrace для профилирования времени выглядит так:

xcrun xctrace record --output . --template "Time Profiler" \
  --target-stdout - --launch -- PROGRAM

Параметр --template Time Profiler выбирает предопределённый шаблон профилирования времени, который создаёт файл трассировки с расширением .trace. Этот файл можно открыть в Instruments. Используйте параметр --output, чтобы задать имя файла трассировки. По умолчанию xctrace при каждом запуске создаёт новый файл, а не перезаписывает существующий.

Полученный файл можно открыть в Instruments для просмотра профиля. Возможно, также удастся создать пламенные графы с помощью inferno или скриптов из репозитория https://github.com/brendangregg/FlameGraph.

Примечание

Иногда для профилирования двоичного файла OCaml в Instruments необходимо добавить к нему права (entitlements). Обычно это приводит к ошибке запуска процесса или ошибке доступа. Чтобы обойти эту проблему, выполните следующую команду, заменив PROGRAM именем программы, которую нужно трассировать.

$ codesign -s - -v -f --entitlements =(echo -n
'<?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
   "https://www.apple.com/DTDs/PropertyList-1.0.dtd"\>
 <plist version="1.0">
    <dict>
        <key>com.apple.security.get-task-allow</key>
        <true/>
    </dict>
 </plist>') PROGRAM

Дополнительные сведения о правах и codesign см. в документации Apple.

4 Глоссарий

В этой главе руководства используются следующие термины.

  • Граф вызовов Цепочка вызовов функций, ведущая к текущей функции (также называется стеком вызовов).
  • Раскрутка стека Процесс последовательного обхода стека вызовов вверх.
  • Кадр стека Область стека, выделенная для одного вызова функции (также называется кадром активации, записью активации или просто кадром).
« Отладка машинного кода (gdb, lldb)Взаимодействие C с OCaml »
Авторские права © 2026 Institut National de Recherche en Informatique et en Automatique

© 1995-2026 INRIA.
https://ocaml.org/manual/5.5/profil.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API