等效于 Java 的 DirectByteBuffer 的 C# 方法
Posted
技术标签:
【中文标题】等效于 Java 的 DirectByteBuffer 的 C# 方法【英文标题】:C# method equivalent to Java's DirectByteBuffer 【发布时间】:2013-07-20 21:50:00 【问题描述】:我正在学习如何在 C# 中使用 C++ DLL,并制作了一个 c++ 函数,该函数将两个已分配(编组)变量集相乘。在我将分配的组合大小从 512MB 增加到 1024 之前,一切都在 C# 和 C++ 中运行良好。然后 Visual C# 给出“受保护的内存访问冲突”的错误。其来源是 dll 函数,它用浮点数填充缓冲区。限制必须介于 512MB 和 1024MB 之间。 Marshal.alloc 仅接受 int 大小的缓冲区长度,因此实际上每次分配有 2GB 限制,但是当我尝试使用更小的块来超过限制时,会出现同样的错误。
问题: 是否有任何在 C# 中没有上限/限制的 directbytebuffer 等效项? 还是我在做一些简单的指针错误?
dll 和 main 项目都是 64 位目标的,可以使用普通数组超过 5-6 GB 的内存。
这是写入缓冲区的 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);
测试了任何硬件错误的 RAM,但通过了内存测试,因此肯定存在软件问题。
谢谢。
windows7-64 位,cpu 64 位,两个项目的目标机器 64 位。
【问题讨论】:
那么你到底为什么要使用Marshal.AllocHGlobal
呢?我不确定你为什么要这样做不安全,你能详细说明你到底想做什么吗?结果应该是什么,等等。
@aevitas 分配空间作为点积函数和矩阵乘法的大缓冲区。这比 Parallel.For 版本更快,减少普通数组。(快 6-7 倍)它在 java 上运行完美,但我试图在 C# 中做同样的事情。
你收到OutOfMemoryException
了吗?
不,内存访问冲突错误。来自使用内在函数写入内存的 c++ 函数。
在我看来 offset = (a - bufferAdr.ToInt64() % a);
评估为负值或其他东西,这导致您的 AccessViolationException
。调试时在其上设置断点,看看该等式的实际结果是什么。如果你分配不足,你会得到一个不同的例外。
【参考方案1】:
__declspec(dllexport) void floatOne(long av, int n)
这是您代码中的一个严重错误,使用 MSVC 编译时,long 类型在 64 位模式下是 32 位。这不足以存储指针。在您开始分配更大的内存块之前,它会意外工作。 “av”的正确参数类型是指针类型,至少是void*
。当然,没有理由避免仅仅声明它float*
。如果您不试图欺骗编译器,代码总是会更好地工作。您必须在 C# 代码中将其声明为 IntPtr。
您尝试对对齐进行的操作非常难以理解。要求是地址与 SSE2 代码的 16 的倍数对齐。你可以使用这个辅助方法:
static IntPtr AlignAddressForSSE2(IntPtr addr)
return new IntPtr((addr.ToInt64() + 15) & unchecked((long)0xfffffffffffffff0L));
还将 15 添加到 Marshal.AllocHGlobal() 参数(实际上 8 就足够了)。或者简单地从你的 DLL 中导出两个函数,通过使用 _aligned_malloc() 和 _aligned_free() 来处理这个问题。
【讨论】:
非常感谢,这解决了我的错误。在 DLL 中使用 (void *) 并在 C# 中使用 IntPtr。我还尝试了 malloc/free 包装器以查看它是如何工作的,但 malloc 没有分配(或者它没有显示在 Windows 管理器中)由于某种原因但工作正常,并且 free 工作正常也没有任何警告。以上是关于等效于 Java 的 DirectByteBuffer 的 C# 方法的主要内容,如果未能解决你的问题,请参考以下文章
PHP 等效于 .NET/Java 的 toString()
C# 等效于 Java 的 Arrays.fill() 方法[重复]