Tried implementing alignof(T)
formula from this web page but it doesn't match with Unity's:
template <typename T>
struct alignof
{
enum { s = sizeof (T), value = s ^ (s & (s - 1)) };
};
The actual result, the first sizeof
/alignof
come from Unity:
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
The implementation showing that above formula doesn't always match:
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;
}
}
The tiny wrapper around Unity implementation, using 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>();
}
}
}
Problem:
Basically, it works for odd values but not even values.
Why I need that:
I have some SIMD-enabled code that can run on Unity but also regular .NET.
For performance concerns, I need to do aligned allocations.
While in Unity I have access to the first two methods, in regular .NET I do not.
And although NativeMemory.AlignedAlloc exists, I still need some alignof(T)
.
Therefore, I have to write a custom aligned memory allocator for regular .NET.
Question:
What's the right formula for writing an alignof(T)
method?
The algorithm appears to be the largest primitive size in the type:
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;
if (fieldType.IsPrimitive)
{
result = Math.Max(result, Marshal.SizeOf(fieldType));
}
}
return result;
}
The results now do match with Unity's alignof
implementation:
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
However, such method should throw on non-blittable types.
Searched and came up with a simplified mix of the links below:
How do I check if a type fits the unmanaged constraint in C#?
The fastest way to check if a type is blittable?
https://aakinshin.net/posts/blittable/
I've ignored managed arrays as they're not blittable under Unity's Burst.
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));
}
}