Я учусь, как использовать C ++ DLL в C # и сделал функцию c ++, которая умножает два распределенных (маршалловых) набора переменных. Все хорошо работает как в C #, так и в C ++, пока я не увеличу общий размер выделений до 1024 с 512 МБ. Затем Visual C # выдает ошибку «нарушения доступа к защищенной памяти». Источником этого является функция dll, которая заполняет буфер с помощью float. Предел должен быть между 512 МБ и 1024 МБ. Marshal.alloc допускает только длину буфера целого размера, поэтому на каждое выделение существует ограничение в 2 ГБ, но когда я пытаюсь использовать меньшие порции, чтобы преодолеть ограничение, выдает ту же ошибку.
Вопрос: Есть ли какой-нибудь прямой байтовый эквивалент, который не ограничен / ограничен в C #?
Или я делаю простую ошибку указателя?
И DLL, и основные проекты нацелены на 64 бита и могут использовать более 5-6 ГБ памяти с обычными массивами.
Вот функция c ++, которая пишет в буфер:
__declspec(dllexport) void floatOne(long av, int n)
{
float * vektor1=(float *)av;
_mm256_zeroall();
__m256 r0=_mm256_setr_ps(1.0f,1.0f,1.0f,1.0f,1.0f,1.0f,1.0f,1.0f);
for(int i=0;i<n;i+=8)
{
_mm256_store_ps(vektor1+i, r0);
}
_mm256_zeroall();
return;
}
Вот как это используется в C #:
public void one()
{
floatOne(bufferAdr.ToInt64() + offset, N);
// offset here is the properly aligned address to start usage
// N is private variable of vektor class (vector length)
}
Вот как происходит распределение:
public vektor(int n /* number of elements*/, int a /* alignmentı*/)
{
N = n;
bufferAdr = Marshal.AllocHGlobal(4*n + 4*a);
//a-1 was enough but I put a*4 to be sure it doesnt overflow.
offset = (a - bufferAdr.ToInt64() % a);
}
Вот импорт DLL:
[DllImport("cpuKullanim.dll", EntryPoint = "floatOne")]
public static extern void floatOne(long adres1, int n);
Проверено ОЗУ на наличие каких-либо аппаратных ошибок, но пройдены мем-тесты, поэтому должна быть программная проблема.
Благодарю.
Windows7-64 бит, процессор 64 бит, целевой компьютер 64 бит для обоих проектов.
__declspec(dllexport) void floatOne(long av, int n)
Это серьезная ошибка в вашем коде, долго Тип 32-битный в 64-битном режиме при компиляции с MSVC. Этого недостаточно для хранения указателя. Это будет работать случайно, пока вы не начнете выделять большие куски памяти. Правильный тип аргумента для «av» — это тип указателя, по крайней мере void*
, Конечно, нет причин не объявлять это float*
, Код всегда работает лучше, если вы не пытаетесь обмануть компилятор. Вы должны объявить его как IntPtr в своем коде C #.
То, что вы пытаетесь сделать с выравниванием, довольно непостижимо. Требование состоит в том, чтобы адрес был кратен 16 для кода SSE2. Вы можете использовать этот вспомогательный метод:
static IntPtr AlignAddressForSSE2(IntPtr addr) {
return new IntPtr((addr.ToInt64() + 15) & unchecked((long)0xfffffffffffffff0L));
}
Также добавьте 15 к аргументу Marshal.AllocHGlobal () (на самом деле достаточно 8). Или просто экспортируйте две функции из вашей DLL, которые позаботятся об этом, используя _aligned_malloc () и _aligned_free ().
Других решений пока нет …