On Monday, March 3rd, 2025 at 9:38 AM, Lawrence Stewart <stewart(a)serissa.com>
wrote:
...
It’s too bad that that C doesn’t have standardized rules for structures, because even
when hardware isn’t involved, one must be extremely careful with cacheline alignment of
fields in order to get good performance. __attribute__((aligned(64)) for the win, but it
is ugly. I don’t think there is even a portable way to get the cacheline size.
...
-L
Truth be told the subjectivity of implementing struct memory characteristics has
bewildered me more rather than less as time goes on. What with the UNIX kernel
being one of C's earliest applications, you find plenty of places where a struct
is used to directly represent for instance a set of I/O registers on a
peripheral (UART comes to mind). In this case, this would pretty much
necessitate a 100% predictable layout of a structure in memory, otherwise you
have no guarantee that the struct members you assign to specific registers
(THR, SR, etc.) will always be aligned with those registers.
This is an area where the "systems language" aspect does break down a bit
broadly for me, if I can't describe a contiguous memory region in a structured
way, just describe an abstract structure that various compilers put together
however they see fit, this makes it much more difficult to use structures for
anything other than abstract, in memory data in the context of a given process
(assuming all code in that process comes from the same compiler). To
effectively transfer data between systems you then need not only worry about
endianness, but the ordering itself. Sure, UNIX was all about everything being
text as much as possible, but there's something to be said about being able to
avoid the cost of serialization of data when you know endianness isn't a
problem. When compilers are free to clobber structures together in memory
haphazardly, even sharing a CPU isn't a guarantee binary data will be portable.
Of course not trying to lob unfair critique at the folks who had to make these
decisions when constructing compilers, but it just surprises me there wasn't
more impetus to ensure a structure represents not only the members but the
memory characteristics of the data as it is being stored. That said, given that
datatypes generally have minimum limits but are not *exact* across every system,
there is the fact that using the same datatypes across different CPUs, even if
the structures were very strict about no padding, order, whatever, you'd still
have the potential for wildly different memory layouts on different systems.
Not to mention systems requiring specific alignments regardless of data type.
Maybe I've answered my own concern but I still find myself curious why there
wasn't more of a push for memory predictability in a language being pushed as a
systems language. It winds up being a happy accident, rather than a matter of
intention, when extension-less ANSI C fits nicely over a set of hardware
I/O registers...
- Matt G.
P.S. I will acquiesce that deeply embedded applications where memory
details are quite important typically are not locked into one compiler
ecosystem other than through vendor decisions, so it is much more feasible to
leverage some specific extended C (GNU, llvm, etc.) for bare-metal projects
where I/O register pages and such being perfectly mapped in structs matters.