Size of structure - c#

It is possible to get size of struct using
Marshal.SizeOf(typeof(mystruct));
Is it possible to get size of a part of a structure(for example I pass to function the last field of a structure and it returns sum of sizes of previous fields)?
As I understand is it possible using reflection?

[StructLayout(LayoutKind.Explicit)]
public struct SomeStruct
{
[FieldOffset(0)]
public byte b1;
[FieldOffset(3)]
public byte b2;
[FieldOffset(7)]
public int i1;
[FieldOffset(12)]
public int i2;
}
class Program
{
static FieldOffsetAttribute GetFieldOffset(string fieldName)
{
return (FieldOffsetAttribute)typeof(SomeStruct)
.GetField(fieldName)
.GetCustomAttributes(typeof(FieldOffsetAttribute), false)[0];
}
static void Main(string[] args)
{
var someStruct = new SomeStruct { b1 = 1, b2 = 2, i1 = 3, i2 = 4 };
Console.WriteLine("field b1 offset: {0}", GetFieldOffset("b1").Value);
Console.WriteLine("field b2 offset: {0}", GetFieldOffset("b2").Value);
Console.WriteLine("field i1 offset: {0}", GetFieldOffset("i1").Value);
Console.WriteLine("field i2 offset: {0}", GetFieldOffset("i2").Value);
Console.ReadLine();
}
}
Prints:
field b1 offset: 0
field b2 offset: 3
field i1 offset: 7
field i2 offset: 12

The memory layout of a struct is not discoverable in .NET. The JIT compiler takes advantage of this, it re-orders the fields of a struct to get a more efficient layout. This plays havoc with any attempt to use the struct in a way that bypasses the normal marshaling mechanisms. Yes, Marshal.SiseOf() produces a size for a struct. But that size is only valid after using Marshal.StructureToPtr().
A direct answer to your question: no, you can't discover the size, not even with Reflection. By design.

Well, I'm not sure but I think it's impossible due to possible optimization and alignment issues.

Related

Why is the performance of this custom Vector2 struct so much worse than this custom Vector4?

While benchmarking some custom vector types, I discovered that, to my surprise, my Vector2 type is much slower for many basic operations when read from an array than my Vector4 type (and Vector3), despite the code itself having fewer operations, fields, and variables. Here is a greatly simplified example that demonstrates this:
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
namespace VectorTest
{
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct TestStruct4
{
public float X, Y, Z, W;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public TestStruct4(float x, float y, float z, float w)
{
X = x;
Y = y;
Z = z;
W = w;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TestStruct4 operator +(in TestStruct4 a, in TestStruct4 b)
{
return new TestStruct4(
a.X + b.X,
a.Y + b.Y,
a.Z + b.Z,
a.W + b.W);
}
}
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct TestStruct2
{
public float X, Y;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public TestStruct2(float x, float y)
{
X = x;
Y = y;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TestStruct2 operator +(in TestStruct2 a, in TestStruct2 b)
{
return new TestStruct2(
a.X + b.X,
a.Y + b.Y);
}
}
public class Program
{
private const int COUNT = 10000;
private static readonly TestStruct4[] s_arr4 = new TestStruct4[COUNT];
private static readonly TestStruct2[] s_arr2 = new TestStruct2[COUNT];
static unsafe void Main()
{
for(int i = 0; i < s_arr4.Length; i++)
s_arr4[i] = new TestStruct4(i, i * 2, i * 3, i * 4);
for(int i = 0; i < s_arr2.Length; i++)
s_arr2[i] = new TestStruct2(i, i * 2);
BenchmarkRunner.Run<Program>();
}
[Benchmark]
public TestStruct4 BenchmarkTestStruct4()
{
TestStruct4 ret = default;
for (int i = 0; i < COUNT; i++)
ret += s_arr4[i];
return ret;
}
[Benchmark]
public TestStruct2 BenchmarkTestStruct2()
{
TestStruct2 ret = default;
for (int i = 0; i < COUNT; i++)
ret += s_arr2[i];
return ret;
}
}
}
Running this benchmark results in:
Method
Mean
Error
StdDev
BenchmarkTestStruct4
9.863 us
0.0706 us
0.0626 us
BenchmarkTestStruct2
22.412 us
0.3100 us
0.2899 us
As you can see, TestStruct2 is more than twice as slow as TestStruct4 (at least on my computer). Given that TestStruct2 is essentially identical to TestStruct4 except that it has fewer fields and has to do fewer adds, I would have expected it to, at worst, be the same speed as TestStruct4, but actually it's slower. Can anyone explain why this is?
Further experimentation has revealed that, if I add another float or two of padding to MyStruct2 (and use Unsafe.SkipInit to avoid the cost of initializing those), then the performance improves to match that of MyStruct4. So I'm guessing that there's some sort of alignment issue going on with MyStruct2, but I don't understand what specifically that might be. Pasting the code into SharpLab does not reveal any obvious obvious differences in the ASM (though I might not just understand the ASM well enough to spot something).
EDIT: This is running on .NET 5 on Windows 10 64-bit.
(Note: I'm NOT interested in having a discussion about whether it is prudent to write one's own vector types when there are plenty of existing types already. I have my reasons for doing do and they are beside the point of this question, which I ask out of an academic curiosity to understand why there is such a huge performance difference.)
EDIT: As requested, here are the byte layouts of the two structs:
Type layout for 'TestStruct4'
Size: 16 bytes. Paddings: 0 bytes (%0 of empty space)
|===========================|
| 0-3: Single X (4 bytes) |
|---------------------------|
| 4-7: Single Y (4 bytes) |
|---------------------------|
| 8-11: Single Z (4 bytes) |
|---------------------------|
| 12-15: Single W (4 bytes) |
|===========================|
Type layout for 'TestStruct2'
Size: 8 bytes. Paddings: 0 bytes (%0 of empty space)
|===========================|
| 0-3: Single X (4 bytes) |
|---------------------------|
| 4-7: Single Y (4 bytes) |
|===========================|
In the case of the struct TestStruct4 the '+' operator overload method the generated assembly instructions use XMM registers to store and increment the value, so the addition instructions looks like this:
00007FFF72084077 vaddss xmm0,xmm0,dword ptr [rdx]
00007FFF7208407B vaddss xmm1,xmm1,dword ptr [rdx+4]
00007FFF72084080 vaddss xmm2,xmm2,dword ptr [rdx+8]
00007FFF72084085 vaddss xmm3,xmm3,dword ptr [rdx+0Ch]
Nice and tidy. Now here is what gets generated for TestStruct2:
00007FFF6FE2B3EA vmovss xmm0,dword ptr [rsp+20h]
00007FFF6FE2B3F0 vaddss xmm0,xmm0,dword ptr [rdx]
00007FFF6FE2B3F4 vmovss xmm1,dword ptr [rsp+24h]
00007FFF6FE2B3FA vaddss xmm1,xmm1,dword ptr [rdx+4]
00007FFF6FE2B3FF vmovss dword ptr [rsp+20h],xmm0
00007FFF6FE2B405 vmovss dword ptr [rsp+24h],xmm1
Here the '+' operator overload method assembly instructions does not store the values in XMM registers, but in a memory, because of that there is an additional overhead - at the beginning it moves the initial value from the memory to XMM, and at the end it moves the modified value back to the memory.
It is not really clear why it is happening, but it does look a lot like a compiler failure to properly optimize the code. To solve this particular issue you could change the type of the field from float to double, then it will get optimized and performance wise it will be essentially the same. Or, if changing the type is not an option, the solution would be, as you mentioned - to add a dummy field.
The IL and JIT ASM certainly don't tell us much. I'm thinking that some CPU/Cache behavior is in play here. Perhaps you are seeing a cost of switching between 128-bit and 256-bit use of the AVX instructions?
VZEROUPPER - Set the upper half of all YMM registers to zero. Used when switching between 128-bit use and 256-bit use.
If you start both your test runs with a single TestStruct2 then perhaps both tests will get a small "set AVX mode" penalty, allowing the TestStruct2 to perform better? Maybe changing the order of the two tests would also yield different results.

StructLayoutAttribute.Pack confusion

I have some problem interpreting the result of two pieces of code.
using System;
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential, Pack = 0)]
struct MyStruct
{
public byte b1;
public char c2;
public int i3;
}
public class Example
{
public unsafe static void Main()
{
MyStruct myStruct = new MyStruct();
byte* addr = (byte*)&myStruct;
Console.WriteLine("Size: {0}", sizeof(MyStruct));
Console.WriteLine("b1 Offset: {0}", &myStruct.b1 - addr);
Console.WriteLine("c2 Offset: {0}", (byte*)&myStruct.c2 - addr);
Console.WriteLine("i3 Offset: {0}", (byte*)&myStruct.i3 - addr);
Console.ReadLine();
}
}
The above result is
Size : 8
b1 Offset: 0
c2 Offset: 2
i3 Offset: 4
If I comment out public char c2; and Console.WriteLine("c2 Offset: {0}", (byte*)&myStruct.c2 - addr);, I would get
Size : 8
b1 Offset: 0
i3 Offset: 4
Now I think I can explain the second scenario, where the default packing size is the size of the largest element size of myStruct when Pack = 0. So it is 1 byte + 3 bytes of padding + 4 bytes = 8.
But the same does not seem apply for the first scenario. My expected result would be (1 byte + 3 bytes of padding) + (2 bytes for char + 2 bytes of padding) + (4 bytes for int). So the total size should be 12 as the packing size of 4 byte(the size of int), and the respective offset are 0, 4, 8.
What am I missing here?
Thanks
To understand alignment it might be helpful to think about something reading your struct in X-byte chunks (where X is your type alignment). In your examples that X is 4. If no padding is added, reading first 4 bytes of your first struct (with char) will read byte, char, and then one byte of the next int field. This (avoid reading partial field bytes) is why padding is needed. To "fix" the problem - one byte of padding is needed. Then first 4-byte read will read byte and char fields (and one byte of padding), and next 4-byte read will read integer field. It's wasteful to add padding as you expected, because you can achieve the same goal with smaller total size (8 bytes over your expected 12).

How to Convert byte[] to BitArray and then pick specific bits from the BitArray

I have this BitArray that contains the following:
byte[] myBytes = {0, 0, 2, 176, 168, 3, 19, 0};
BitArray myBitArr = new BitArray(myBytes);
The result is some how revered on the disp:(EDIT: THIS IS NOT THE ISSUE)
00000000 00000000 01000000 00001101 00010101 11000000 11001000 00000000
reveres, is:
00000000 000000**00 00000010 1011**0000 10101000 00000011 00010011 00000000
To get the bits out from the myBitArr I use the following
bool[] myNumbers = GetBitSBiArray(myBitArr, 36, 50);
with this helping method
private static bool[] GetBitSBiArray(BitArray ba, int from, int to)
{
bool[] temp = new bool[to - from];
int t = 0;
for (int i = ba.Length - to; i < ba.Length - from; i++)
{
temp[t] = ba.Get(i);
t++;
}
return temp;
}
the method above returns somehow the wrong result:
00010000 000000 (14bit)
the correct result is:
00000000 101011 (14bit) or 43
i'm not interested in overflow or other exceptions for now.
what is wrong with my method and what alternatives do i have?
The problem is, you get all caught up in all those mysterious reversals. The format of BitArray's byte[] doesn't fit your format of byte[], and it's biting you.
It seems that your interpretation of the data is "highest bit has index 0", lowest bit h. What you need to map into is "highest bit is right, and every individual byte is lower endian".
I'd suggest getting rid of that helper code entirely. The problem is that you're using the wrong format for initializing the BitArray - the obvious solution is to fix up the input, rather than creating helper methods to "remap" the indexing on each access.
The simplest way to get the result you want is
BitArray myBitArr = new BitArray(myBytes.Reverse().ToArray());
and the method
private static bool[] GetBitSBiArray(BitArray ba, int from, int to)
{
bool[] temp = new bool[to - from];
int t = 0;
for (int i = from; i < to; i++)
{
temp[temp.Length - t - 1] = ba.Get(i);
t++;
}
return temp;
}
The idea is that Reverseing the byte array will bring the bits in the individual bytes in line (that is, you get rid of the "reversed" order in each separate byte), and it will flip the order of bits, all in one operation.
The extra reversal with the temp[temp.Length - t - 1] on output is to match the order in your sample - I assume it's actually unnecessary, since that's your manual rendering of the bits, rather than the order you want to use them in. If you simply use temp[t], the first bool will correspond to bit 36, and the last to bit 50. This also means that you probably don't need to use your helper function anymore - simply use bitArray.Get(36) to get bit 36.
Actually I don't know how you counted the bits and how bits [36,50] are going to be "00000000 101011". But here is my answer: I would write such a function this way:
private static bool[] GetBitSBiArray(BitArray ba, int from, int to)
{
//from and to are zero-based
//you can change it the way you prefer.
if (to < from || to >= ba.Length)
return null;
List<bool> temp = new List<bool>();
int t = 0;
for (int i = from; i < to; i++)
temp.Add(ba.Get(i));
return temp.ToArray();
}

C# structure size evaluation

Please, can anyone tell what is the size of the structure, and how to calculate it:
[StructLayoutAttribute(LayoutKind.Sequential, Pack=2)]
public struct SomeStruct
{
public short sh;
public int i;
public byte b;
public ushort u;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=7)]
public byte[] arr;
}
I suppose you want to understand how is memory allocated, otherwise you can use:
System.Runtime.InteropServices.Marshal.SizeOf(TheStruct)
Well you choose to pack in a 2 byte boundary, so you basically allocate memory at step of two.
so short ( signed or unsigned ) take 2 bytes, int take four bytes byte would take just one, but due to the packing you specify take two as well, for the same reason for the array we need to consider 8.
So we have
2+4+2+2+8 = 18 bytes
You can get exact size of this structure using following code -
System.Runtime.InteropServices.Marshal.SizeOf(SomeStruct)
Try this:
int size = System.Runtime.InteropServices.Marshal.SizeOf(new SomeStruct());
Console.WriteLine(size);
or, from a related post, this:
int size;
unsafe
{
size = sizeof(SomeStruct);
}
Both will yield 18, as Felice Pollano has explained.

What is the Fastest way to convert byte[] to float[] and vice versa?

Which is the fastest way to convert a byte[] to float[] and vice versa (without a loop of course).
I'm using BlockCopy now, but then I need the double memory. I would like some kind of cast.
I need to do this conversion just to send the data through a socket and reconstruct the array in the other end.
Surely msarchet's proposal makes copies too. You are talking about just changing the way .NET thinks about a memory area, if you dont' want to copy.
But, I don't think what you want is possible, as bytes and floats are represented totally different in memory. A byte uses exactly a byte in memory, but a float uses 4 bytes (32 bits).
If you don't have the memory requirements to store your data, just represent the data as the data type you will be using the most in memory, and convert the values you actually use, when you use them.
How do you want to convert a float (which can represent a value between ±1.5 × 10−45 and±3.4 × 10^38) into a byte (which can represent a value between 0 and 255) anyway?
(see more info her about:
byte: http://msdn.microsoft.com/en-us/library/5bdb6693(v=VS.100).aspx
float: http://msdn.microsoft.com/en-us/library/b1e65aza.aspx
More about floating types in .NET here: http://csharpindepth.com/Articles/General/FloatingPoint.aspx
You can use StructLayout to achieve this (from Stack Overflow question C# unsafe value type array to byte array conversions):
[StructLayout(LayoutKind.Explicit)]
struct UnionArray
{
[FieldOffset(0)]
public Byte[] Bytes;
[FieldOffset(0)]
public float[] Floats;
}
static void Main(string[] args)
{
// From bytes to floats - works
byte[] bytes = { 0, 1, 2, 4, 8, 16, 32, 64 };
UnionArray arry = new UnionArray { Bytes = bytes };
for (int i = 0; i < arry.Bytes.Length / 4; i++)
Console.WriteLine(arry.Floats[i]);
}
IEnumerable<float> ToFloats(byte[] bytes)
{
for(int i = 0; i < bytes.Length; i+=4)
yield return BitConverter.ToSingle(bytes, i);
}
Two ways if you have access to LINQ:
var floatarray = ByteArry.AsEnumerable.Cast<float>().ToArray();
or just using Array Functions
var floatarray = Array.ConvertAll(ByteArray, item => (float)item);

Categories

Resources