3

In a recent project of mine, I am using some struct, the reason I did so is because I am reading from a shared memory 'stream'.

It is not really possible to post the whole code, but I can basically summarize what I am doing:

 [StructLayout(LayoutKind.Sequential, Pack = 1)]

Reading from the memory stream and packing is working just fine, but then I serialize it to send over UDP or Websocket, and I am just sending double values, so I create another temporary struct to put the data I actually want to send, and populate it:

    public struct GPUInfo
    {
        public double TotalWatts { get; set; }
        public double Voltage { get; set; }
        public double CoreUsage { get; set; }
        public double MemUsage { get; set; }
    }
GPUInfo DUMMYDATA = new GPUInfo();
DUMMYDATA.TotalWatts = 20;
DUMMYDATA.Voltage = 1;
DUMMYDATA.CoreUsage = 100;
DUMMYDATA.MemUsage = 40;


byte[] BYTE = MessagePack.MessagePackSerializer.Serialize(DATA);

WS.Send(BYTE);

I am pretty sure that I am doing the correct thing using a struct as the 'base unpacking dictionary' for the shared memory stream, I am using Marshal.PtrToStructure. There's no doubt in my mind that this is correct, but I want to serialize some of the data previously collected, should I create a new class instead of a new struct? What is the difference?

Most online resources state that we should not serialize structs. What type of Serialization are they talking about?

DO NOT USE STRUCT

From Microsoft: CLASSES ARE BETTER

What do they mean by being boxed frequently?

structs are faster it seems, but I am still trying to grasp the meaning of serialization in this context, if I create a class without methods isn't it an anti pattern? I could be using a Dictionary and then throwing it away, will it be faster than struct?

STRUCTS ARE FASTER

Please understand, this question is not about Reading Shared Memory and using a struct to Marshall it, I don't know about any other method to do so. The question is: why is it wrong to Serialize a struct but not a class.

I'm happy serializing my structs, but it might be wrong and I want to know why. I could use a class, but a class without methods is an anti-pattern? (I hear it a lot on Stackoverflow).

Reference 1 (seems like it is common sense not to serialize struct): https://github.com/BenjaminAbt/SustainableCode/tree/main/csharp/struct-vs-class

Reference 2 (smaller than 16 bytes, and not boxed frequently): https://learn.microsoft.com/en-us/dotnet/standard/design-guidelines/choosing-between-class-and-struct

Reference 3 (classes without methods are an anti pattern): https://softwareengineering.stackexchange.com/questions/160675/what-do-you-call-classes-without-methods

I don't need methods. I could also create a Dictionary and throw it away, but I want to know the reason I have to do so, so I won't repeat this mistake on the future.

layer07
  • 53
  • 2
  • 8
  • 1
    Please put links to referenced documentation, instead of only pictures. It will help to understand context of what they're talking about – JL0PD Oct 27 '22 at 03:38
  • Three links added – layer07 Oct 27 '22 at 03:54
  • 2
    I don't see anything wrong with serializing a struct. But I don't work with xml nor with System.Runtime.ISerializable. System.Text.Json requires adding attribute on constructor, which doesn't add much problem. Handwritten binary serialization is much faster for structs than for classes – JL0PD Oct 27 '22 at 05:00
  • Thanks for the input. Same, I am not using System.Runtime.ISerializable. System.Text.Json or Newtonsoft. I've also noted that MessagePack works pretty well with structs without having to tweak anything. The structs are not used as reference, just as a way to access data easily and pack it. As I said, it could be a dictionary, but then, what is the difference? – layer07 Oct 27 '22 at 05:09
  • 1
    about [boxing](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/types/boxing-and-unboxing). – Bagus Tesa Oct 27 '22 at 05:16
  • @BagusTesa, Thank you Bagus. I definitely couldn't find this public microsoft link by myself. – layer07 Oct 27 '22 at 22:33

1 Answers1

1

Ok, this is an interesting topic, and I won't claim to be expert on it. I'll just respond with what I know.

Structs are often allocated on the stack memory. This is what makes them faster in overall terms. This increased speed is what makes game developers to use them so often.

Strictly speaking, it is not always the stack, but I won't enter into a deep discussion. Quickly searching I found this answer that describes in more detail how this goes.

So yes, your preference for serialization of a struct due to its increased performance is justified.

Because oftentimes structs are allocated in the stack, and the stack is much more limited than the heap, it is recommended that your structs be small or you'll hit memory exceptions fast. You must be mindful of your stack consumption. Another reason for the 16-byte limit is probably atomic operations. Anything larger cannot be atomically handled.

And finally, for your grand million dollar question: Is it wrong to serialize a struct? Ha! I don't have a definitive answer, but I'll venture say it is more of a warning, again, due to the risk of consuming stack memory in large quantities.

José Ramírez
  • 872
  • 5
  • 7
  • 4
    Struct assignment is only atomic if it fits in a machine word. Passing a struct as an argument is also an assignment, I think that's where the 16-byte suggestion comes from, as passing a reference argument is obviously faster. – Jeremy Lakeman Oct 27 '22 at 05:49
  • 1
    @JeremyLakeman awesome. Thanks for your contribution. If you have more, keep them coming. – José Ramírez Oct 27 '22 at 05:50
  • 3
    Note than `Span` is larger than a machine word, and it's `readonly ref` to prevent leaving the stack and risking a non-atomic and therefore non-threadsafe write. – Jeremy Lakeman Oct 27 '22 at 05:51
  • 1
    Great answer, I have a C background and sometimes C# confuses me a little. I've been using C# as my backend and struct as my 'disposable' for some telemetry data, and it is working fine for around 8 months, serving around 120~ simultaneous clients with thousands of operations per second, millions of operations per day and so far it hasn't exploded. But one of our JAVA/C# programmers took a look on my C# code and said I shouldn't be Serializing structs *(too late)*, but he didn't give me an answer to what I should really do. The deeper I dove, the more confused I got. – layer07 Oct 27 '22 at 06:28
  • 2
    @layer07, don't take advice from people who can't explain it – JL0PD Oct 27 '22 at 06:43
  • 1
    Well you aren't writing the raw struct outside the machine. MessagePackSerializer should hide any problems that would arise from alignment and byte order. But I assume you already know all that. – Jeremy Lakeman Oct 27 '22 at 07:35
  • @JeremyLakeman, Thanks for the input Jeremy, I am not serializing the struct I am using to marshall the shared memory 'reads'. What I am doing is, later on the code, I am grabbing some information and creating a "dummy struct", just to populate it with whatever information I want and serialize it using MessagePack. I am using the struct just as a "semantic" readable way to write my data. It is merely a _collection of independent public fields_ . Apart from the fact that it is on the Stack on not on the Heap. I didn't find anything crazy on the compiler. – layer07 Oct 27 '22 at 22:31
  • @JL0PD Thank you for your life advice. I will certainly keep this knowledge in hand for future encounters. – layer07 Oct 27 '22 at 22:32