【发布时间】:2017-06-25 20:28:01
【问题描述】:
我正在尝试创建从 C# 结构到 C++ 和 CUDA 中的非托管代码的自动转换。不幸的是,我似乎无法创建匹配的结构。结构:
// C#
[StructLayout(LayoutKind.Sequential)]
struct DebugComponent
{
public float4 Float4;
public float Float;
}
// C++
struct CPP_DebugComponent
{
float4 Float4;
float Float;
};
结构体的大小使用 C# 调用计算为 20 字节:
var size = System.Runtime.InteropServices.Marshal.SizeOf<DebugComponent>();
但是在使用 sizeof 的 C++ 中,相同的结构是 32 字节:
auto size = sizeof(CPP_DebugComponent);
我相信差异源于 float4 结构的定义方式。 CUDA 对 float4 的定义将其对齐为 16 个字节:
// C++
struct __device_builtin__ __builtin_align__(16) float4
{
float x, y, z, w;
};
// For similar results without the CUDA definition, you can use:
// struct __align__(16) float4
因此向单个浮点数添加 12 个字节的填充。在 C# 中,没有这种对齐方式,导致编译器选择 4 字节的打包。为了完整起见,C# 中的 float4 定义(来自 ManagedCuda):
// C#
public struct float4
{
public float x;
public float y;
public float z;
public float w;
}
我知道我可以通过指定以下属性在 C# 中人为地重新创建正确大小的结构:
[StructLayout(LayoutKind.Sequential, Size = 32)]
struct DebugComponent { ... }
但是对于需要知道结构中所有类型的总和以及有关打包和填充的一些假设的自动代码生成。看来我无法使用StructLayoutAttribute.Pack 扩展结构。
我也知道我可以改变 C++ 代码中的包装,例如:
#pragma pack(1)
但我宁愿避免这种解决方案,因为填充会导致性能显着提高。
我的问题:我能否模仿非托管填充/打包的行为,使 C# 结构在内存大小方面与 C++ 结构保持一致?
有没有很好的替代方案来调整结构,同时牢记性能?
以上所有代码示例均在 Windows、Visual Studio 2017 上运行并在 x64 上编译。
【问题讨论】:
-
AFAIK 您拥有的唯一可移植工具是 alignas 和 aligned_storage。
-
如果你将它映射到原始内存,
System.Runtime.InteropServices.Marshal.SizeOf是无关紧要的;sizeof(DebugComponent)说什么? -
sizeof(DebugComponent)(在不安全的代码中)与此结构的 Marshal.Sizeof 相同。
-
难道你不能通过在开头添加总共消耗 12 个字节的未使用成员变量来伪造填充吗?不过,我并不建议这样做,只是说...
-
当然,C# 编译器知道关于
__builtin_align__(16)的 bean。 CLR 也没有,那是你的克星。人为地增加结构的大小不是一种解决方法,关键是结构的起始地址是 16 的倍数。不可能,您只能对齐到 32 中的 4-位代码,在 64 位代码中为 8。您可以使用 Marshal.AllocHGlobal() (与 8 对齐)进行技巧,分配超过必要的 8 并在必要时将 8 添加到指针。建议使用 C++/CLI,以便您可以使用_aligned_malloc()。