Добавить 64-битное смещение к указателю - PullRequest
5 голосов
/ 24 апреля 2010

В F # есть модуль NativePtr, но, похоже, он поддерживает только 32-битные смещения для своих функций добавления / получения / установки, как System.IntPtr.

Есть ли способ добавить 64-битное смещение к собственному указателю (nativeptr <'a>) в F #? Конечно, я мог бы преобразовать все адреса в 64-битные целые числа, выполнить обычные целочисленные операции и затем снова преобразовать результат в nativeptr <'a>, но это потребовало бы дополнительных инструкций add и imul. Я действительно хочу, чтобы AGU выполняли вычисления адресов.

Например, используя unsafe в C #, вы можете сделать что-то вроде

void* ptr = Marshal.AllocHGlobal(...).ToPointer();
int64 offset = ...;
T* newAddr = (T*)ptr + offset; // T has to be an unmanaged type

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

В F # мы наконец получили неуправляемое ограничение; но как мне сделать арифметику указателя?

Ответы [ 2 ]

3 голосов
/ 24 апреля 2010

Я не эксперт в этой области, но я посмотрел на реализацию 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 ГБ (сохраненные в массивах).

1 голос
/ 24 апреля 2010

Данные изображения могут легко превысить 4 ГиБ ;)

Не может, код x64 имеет ограничение смещения +/- 2 ГБ. Одна из причин, по которой вы не можете выделить массивы размером более 2 ГБ в .NET. Это ограничение существует и в неуправляемом 64-битном коде C / C ++. Существуют библиотеки, которые обходят это ограничение, например WIC, но прямая адресация всех битовых битов не имеет смысла при их использовании.

Тем не менее, такой адрес можно сгенерировать, приведя IntPtr к long в C #:

IntPtr addr = SomeCall();
long offset = blah;
addr = (IntPtr)((long)addr + offset);
...