Я не эксперт в этой области, но я посмотрел на реализацию F # модуля NativePtr
и считаю, что нет никаких накладных расходов, связанных с преобразованием nativeptr<'a>
в nativeint
и обратно.
Реализация использует встроенный IL, а встроенный код IL не содержит никакого кода - он просто заставляет компилятор F # думать, что значение в стеке имеет другой тип:
let inline ofNativeInt (x:nativeint) = (# "" x : nativeptr<_> #)
let inline toNativeInt (x:nativeptr<_>) = (# "" x : nativeint #)
Фактически, метод NativePtr.add
также использует эти два метода - он преобразует указатель в nativeint
, а затем добавляет 32-битное целое число (умноженное на размер типа 'a
).
Итак, следующая функция должна быть в порядке:
let inline addNativeInt (x:nativeptr<'a>) (n:nativeint) : nativeptr<'a> =
(NativePtr.toNativeInt x) + n |> NativePtr.ofNativeInt
Все функции, используемые в коде, должны быть встроены, так что в итоге вы получите только одну инструкцию для добавления (хотя я не проверял это). Вам даже не нужно беспокоиться об использовании функции несколько раз в вашем коде (вы можете работать с nativeptr<'a>
все время и использовать эту функцию для добавления).
Однако разделение данных также может быть вариантом - насколько мне известно, команда MSR, которая использовала F # для обработки некоторых больших (> 2 ГБ) наборов данных, использовала именно этот подход - они разбили данные на блоки 2 ГБ (сохраненные в массивах).