在C#中编写alignof(T)方法?

问题描述 投票:0回答:1

尝试从

此网页
实现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)
方法的正确公式是什么?

c# algorithm memory-alignment alignof
1个回答
0
投票

该算法似乎是类型中的最大原始尺寸

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 类型。

搜索并提出了以下链接的简化组合:

如何检查类型是否符合 C# 中的非托管约束?

检查类型是否可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));
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.