Skip to content

Unsafe

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

Преждевременная оптимизация

Преждевременная оптимизация — корень всех зол.

Старайтесь покрывать тестами участки кода, зависимые от unsafe преобразований.

Zero-cost преобразование

Основной паттерн таких функций это понятие владения (или уникальности), которое мы должны гарантировать.

Типичный пример — мы хотим преобразовать bytes в строку. Если воспользоваться функцией Bytes.to_string, то она создаст копию, преобразует её в строку и вернёт эту копию.

Почему жто так?

Дело в том, что bytes это изменяемая (mutable) структура данных, а строки — нет. Из-за чего если мы не скопируем значение, то при изменение bytes будет изменяться и строка, а это не то поведение, что мы хотели бы, это было бы нарушением абстракций.

Демонстрация:

ocaml
let () =
  let b = Bytes.make 10 'a' in (* "aaaaaaaaaa" *)
  let s = string_of_bytes b in (* "aaaaaaaaaa" *)

  Bytes.set b 2 'B';

  print_endline s (* aaBaaaaaaa *)

Но если мы уверены и готовы гарантировать, что исходный объект не будет изменяться, то можем сделать unsafe_to_string, которые не произведет никаких новых выделений памяти, а просто вернёт новый тип для того же объекта.

Unchecked

Другой распространённый паттерн — unchecked операции, такие операции не производят никаких runtime проверок и всю обязанность перекладывают на пользователя.

Операции с коллекцией

Пример с массивом:

ocaml
let () =
  let arr = [|"первый"; "второй" |] in
  print_endline @@ Array.unsafe_get arr 3;
  Array.unsafe_set arr 3 "третий";
  print_endline @@ Array.unsafe_get arr 3

Итерация

По возможности используйте итерацию, а не явный цикл.

В ассемблере

Unchecked.

ocaml
let x = Array.unsafe_get arr 1
(* movq    8(%rax), %rax *)

Checked.

ocaml
let x = arr.(1)
(* movq    -8(%rax), %rbx
   cmpq    $2047, %rbx
   jbe     .L104
   movq    8(%rax), %rax *)

Флаг -unsafe

При использовании этого флага отключаются проверки при обращение по индексу через конструкции v.(i) и s.[i]. При компиляции в нативный код также отключается проверка деления на ноль.

Изменение внутреннего представления

В стандартной библиотеке есть модуль Obj дающий операции над внутреннем представлением OCaml-значений. Not for the casual user!

Мат. часть

Смотрите RWO, Memory Representation of Values и Interfacing C with OCaml.

Transmute

Мы можем интерпретировать одно значение как другое, либо преобразовать его в полиморфную форму, посредством функции Obj.magic:

ocaml
# Obj.magic 121;;
- : 'a = <poly>
ocaml
# (Obj.magic 121 : char);;
- : char = 'y'
ocaml
# (Obj.repr 121 |> Obj.obj : char);;
- : char = 'y'

Юзкейс с файловыми дескрипторами:

ocaml
# (Obj.magic Unix.stdout : int);;
- : int = 1
ocaml
# (Obj.magic 1 : Unix.file_descr);;
- : Unix.file_descr = <abstr>