尝试从
此网页实现
alignof(T)
公式,但它与Unity的不匹配:
template <typename T>
struct alignof
{
enum { s = sizeof (T), value = s ^ (s & (s - 1)) };
};
测试结果:
Struct1 : SizeOf: 1, 1, AlignOf: 1, 1, Match: True, True
Struct2 : SizeOf: 2, 2, AlignOf: 2, 1, Match: True, False
Struct3 : SizeOf: 3, 3, AlignOf: 1, 1, Match: True, True
Struct4 : SizeOf: 4, 4, AlignOf: 4, 1, Match: True, False
Struct5 : SizeOf: 4, 4, AlignOf: 4, 4, Match: True, True
Struct6 : SizeOf: 8, 8, AlignOf: 8, 4, Match: True, False
Struct7 : SizeOf: 12, 12, AlignOf: 4, 4, Match: True, True
Struct8 : SizeOf: 16, 16, AlignOf: 16, 4, Match: True, False
实现/测试显示上述公式并不总是匹配:
using System.Runtime.InteropServices;
using JetBrains.Annotations;
namespace Whatever.Tests;
[TestClass]
public class UnitTestTemp
{
[UsedImplicitly]
public required TestContext TestContext { get; set; }
[TestMethod]
public void TestMethod1()
{
PrintSizeOfAlignOf<Struct1>();
PrintSizeOfAlignOf<Struct2>();
PrintSizeOfAlignOf<Struct3>();
PrintSizeOfAlignOf<Struct4>();
PrintSizeOfAlignOf<Struct5>();
PrintSizeOfAlignOf<Struct6>();
PrintSizeOfAlignOf<Struct7>();
PrintSizeOfAlignOf<Struct8>();
}
public void PrintSizeOfAlignOf<T>() where T : struct
{
var sizeOf1 = Marshal.SizeOf<T>();
var sizeOf2 = UnsafeUnity.SizeOf<T>();
var alignOf1 = sizeOf1 ^ (sizeOf1 & (sizeOf1 - 1));
var alignOf2 = UnsafeUnity.AlignOf<T>();
TestContext.WriteLine(
$"{typeof(T).Name,-12}: " +
$"SizeOf: {sizeOf1,2}, {sizeOf2,2}, " +
$"AlignOf: {alignOf1,2}, {alignOf2,2}, " +
$"Match: {sizeOf1 == sizeOf2,6}, {alignOf1 == alignOf2,6}");
}
public struct Struct1
{
public byte B1;
}
public struct Struct2
{
public byte B1, B2;
}
public struct Struct3
{
public byte B1, B2, B3;
}
public struct Struct4
{
public byte B1, B2, B3, B4;
}
public struct Struct5
{
public int B1;
}
public struct Struct6
{
public int B1, B2;
}
public struct Struct7
{
public int B1, B2, B3;
}
public struct Struct8
{
public int B1, B2, B3, B4;
}
}
Unity 实现的小型包装器,使用 UnityAssemblies:
using Unity.Collections.LowLevel.Unsafe;
namespace Whatever
{
public static class UnsafeUnity
{
public static int AlignOf<T>() where T : struct
{
return UnsafeUtility.AlignOf<T>();
}
public static int SizeOf<T>() where T : struct
{
return UnsafeUtility.SizeOf<T>();
}
}
}
问题:
基本上,它适用于奇数值,但不适用于偶数值。
为什么我需要它:
我有一些支持 SIMD 的代码,可以在 Unity 上运行,也可以在常规 .NET 上运行。
出于性能问题,我需要进行对齐分配。
在 Unity 中我可以访问前两种方法,但在常规 .NET 中我不能。
虽然 NativeMemory.AlignedAlloc 存在,但我仍然需要一些
alignof(T)
。
因此,我必须为常规 .NET 编写一个自定义对齐的内存分配器。
问题:
编写
alignof(T)
方法的正确公式是什么?
该算法似乎是类型中的最大原始尺寸:
public static int AlignOf<T>() where T : struct
{
var type = typeof(T);
if (!UnsafeHelper.IsBlittable<T>())
{
throw new InvalidOperationException("The type is not blittable.");
}
var result = 1;
var fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
foreach (var field in fields)
{
var fieldType = field.FieldType;
var sizeOf = Marshal.SizeOf(fieldType);
if (fieldType.IsPrimitive)
{
result = Math.Max(result, sizeOf);
}
}
return result;
}
结果现在与 Unity 的
alignof
实现相匹配:
Struct1 : SizeOf: 1, 1, AlignOf: 1, 1, Match: True, True
Struct2 : SizeOf: 2, 2, AlignOf: 1, 1, Match: True, True
Struct3 : SizeOf: 3, 3, AlignOf: 1, 1, Match: True, True
Struct4 : SizeOf: 4, 4, AlignOf: 1, 1, Match: True, True
Struct5 : SizeOf: 4, 4, AlignOf: 4, 4, Match: True, True
Struct6 : SizeOf: 8, 8, AlignOf: 4, 4, Match: True, True
Struct7 : SizeOf: 12, 12, AlignOf: 4, 4, Match: True, True
Struct8 : SizeOf: 16, 16, AlignOf: 4, 4, Match: True, True
Struct9 : SizeOf: 60, 60, AlignOf: 4, 4, Match: True, True
Struct10 : SizeOf: 272, 272, AlignOf: 4, 4, Match: True, True
但是,这样的方法应该抛出非 blittable 类型。
搜索并提出了以下链接的简化组合:
https://aakinshin.net/posts/blittable/
我忽略了托管数组,因为它们在 Unity 的 Burst 下不可blittable。
public static class UnsafeHelper
{
public static bool IsBlittable<T>()
{
return IsBlittableCache<T>.Value;
}
private static bool IsBlittable(this Type type)
{
var handle = default(GCHandle);
try
{
var instance = FormatterServices.GetUninitializedObject(type);
handle = GCHandle.Alloc(instance, GCHandleType.Pinned);
return true;
}
catch
{
return false;
}
finally
{
if (handle.IsAllocated)
{
handle.Free();
}
}
}
private static class IsBlittableCache<T>
{
public static readonly bool Value = IsBlittable(typeof(T));
}
}