Unsafe
В стандартной библиотеке можно встретить unsafe_
функции, такие функции ориентированны на опытных пользователей и при правильном использование способы повысить производительность за счёт ручного контроля.
Преждевременная оптимизация
Преждевременная оптимизация — корень всех зол.
Старайтесь покрывать тестами участки кода, зависимые от unsafe преобразований.
Zero-cost преобразование
Основной паттерн таких функций это понятие владения (или уникальности), которое мы должны гарантировать.
Типичный пример — мы хотим преобразовать bytes
в строку. Если воспользоваться функцией Bytes.to_string
, то она создаст копию, преобразует её в строку и вернёт эту копию.
Почему жто так?
Дело в том, что bytes
это изменяемая (mutable) структура данных, а строки — нет. Из-за чего если мы не скопируем значение, то при изменение bytes
будет изменяться и строка, а это не то поведение, что мы хотели бы, это было бы нарушением абстракций.
Демонстрация:
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 проверок и всю обязанность перекладывают на пользователя.
Операции с коллекцией
Пример с массивом:
let () =
let arr = [|"первый"; "второй" |] in
print_endline @@ Array.unsafe_get arr 3;
Array.unsafe_set arr 3 "третий";
print_endline @@ Array.unsafe_get arr 3
Итерация
По возможности используйте итерацию, а не явный цикл.
В ассемблере
Unchecked.
let x = Array.unsafe_get arr 1
(* movq 8(%rax), %rax *)
Checked.
let x = arr.(1)
(* movq -8(%rax), %rbx
cmpq $2047, %rbx
jbe .L104
movq 8(%rax), %rax *)
Флаг -unsafe
При использовании этого флага отключаются проверки при обращение по индексу через конструкции v.(i)
и s.[i]
. При компиляции в нативный код также отключается проверка деления на ноль.