[HN Gopher] Zig Structs of Arrays (2024)
___________________________________________________________________
Zig Structs of Arrays (2024)
Author : Tomte
Score : 119 points
Date : 2026-06-04 06:28 UTC (4 days ago)
(HTM) web link (andreashohmann.com)
(TXT) w3m dump (andreashohmann.com)
| Sweepi wrote:
| OT: I just spend a few minutes searching for the source of the
| "Not all CPU operations are created equal" slide of the linked
| presentation (Andrew Kelley - Practical DOD), its here:
|
| https://6it.dev/blog/infographics-operation-costs-in-cpu-clo...
| skywal_l wrote:
| You URL seems KO, an alternative I found:
| https://x.com/chessMan786/status/1879092981892067383/photo/1
| trymas wrote:
| Slightly related recent HN post:
| https://news.ycombinator.com/item?id=48382382
| Thaxll wrote:
| This is what games do with ECS.
| hiccuphippo wrote:
| Hugged to death. Mirror:
| https://web.archive.org/web/20260608113005/https://andreasho...
| binaryturtle wrote:
| I'm just seeing a "410 Gone" error on the linked site (same
| happens to the parent URL too).
| ArneCode wrote:
| Works for me
| binaryturtle wrote:
| Still the same. I guess it's some sort of wild anti-bot stuff
| basing on the user agent?
|
| /edit
|
| Yes, as confirmed with cURL, using my browser's "User Agent":
| 410 blocked. Using some other "User Agent" and it passes
| along the data. Pretty silly, IMHO.
| smallstepforman wrote:
| The GPU loves arrays of structures AoS, since all vertex data
| fits in its triangle assembly cache. Once given to the GPU, the
| software side doesnt really care for all vertex parameters so
| this optimisation is pointless. Only relavent when you have
| instance rendering (leaves, grass) but then you only need an
| array of vec3's, not the other parameters so back to normal
| arrays.
|
| Meanwhile, game engines need operator overloading for
| adding/multiplying vectors (spatial transforms, lighting,
| physics) and core zig design philosophy prevents operator
| overloading.
|
| Blind leading the blind. Disclaimer - I do professional rendering
| engines.
| awesan wrote:
| Zig is adding native vectors including operator support, there
| are some recent issues/prs about this topic.
|
| The general technique of SoA is pretty useful both in games and
| other applications, but of course I cannot speak to the
| specific use-case you are describing.
| nvme0n1p1 wrote:
| Zig vectors force data into SIMD registers even if that would
| make the code slower. They're a specialty type. You should
| only reach for vectors if you would have used SIMD intrinsics
| in C for example.
| e4m2 wrote:
| Zig vectors do not necessarily force data into SIMD
| registers; a scalar implementation would work equally well.
| This is not just a theoretical argument, because Zig code
| that uses `@Vector` also has to compile for architectures
| that do not have SIMD instructions.
|
| That being said, the parent commenter is actually referring
| to other recent proposals as opposed to existing `@Vector`
| functionality:
|
| https://codeberg.org/ziglang/zig/issues/32032
|
| https://codeberg.org/ziglang/zig/issues/35376
| nvme0n1p1 wrote:
| Interesting, so zig might have both "vectors" and "vecs"?
| I guess naming is another thing to fix before 1.0 <g>
| beepbooptheory wrote:
| So is the argument that any SoA is pointless? Or just for GPU
| stuff? Because this isn't really talking about all that one way
| or another.
|
| Also does one really _need_ operator overloading? That feels a
| little strong. I 've gotten by with functions just fine.. Does
| that make the GPU not like me Mr. wise engineer?
| geysersam wrote:
| Genuine question: why do you think game engines _need_ operator
| overloading? I mean, what 's wrong with generic functions like
| add, multiply, dot etc.
| aaaashley wrote:
| Not GP, but I've written game engines and rendering engines.
| Vector operations are just common enough that having to write
| `.mul` every time is a huge pain, especially when you put
| many of them together for a large formula. Compare:
|
| (physics_data.velocity + omega * change) * frame_delta_time
|
| to
|
| physics_data.velocity.add(omega.mul(change)).mul(frame_delta_
| time)
|
| We learn to read and think about math a certain way, which is
| incompatible with Zig. Also, Zig's design philosophy of
| "reading code over writing code" is incompatible with the
| kind of small modification-test-cycles required when doing
| games, and creative programming in general. So Zig is sort of
| DOA anyway for that kind of thing.
|
| But I've been using Zig for non-game projects and it's been
| fantastic, so definitely not "Blind leading the blind" for
| the overall language design, imo.
| smj-edison wrote:
| I've been thinking about a way around this, and I'd be
| interested to see if comptime with a DSL wouldn't be too
| unwieldy. Something like math("(v + O * c)
| * Dt", .{ .v = physics_data.velocity, .@"O" = omega, .c =
| change, .@"Dt" = frame_delta_time})
|
| I know this is already possible with comptime, though I
| haven't implemented it yet since I haven't needed vector
| math in what I'm working on currently. Can't decide whether
| using math names is better or worse than using the full
| variable names though.
| stouset wrote:
| All this just to prevent people from using + - * / and ^.
| _Why_?
| smj-edison wrote:
| Andrew talks about it because it introduces hidden
| control flow where you're expecting simple operators. In
| Zig anything that deals with control flow is a keyword
| (including short circuiting and, which is `and` instead
| of `&&`).
|
| I'd argue though that the real disadvantage to having
| overloadable arithmetic is that you're limited to one
| implementation. This is actually my biggest beef with
| Rust, namely traits/type classes. It locks you into a
| single implementation when you may want to do something
| different based on the context. Zig pushes the dispatch
| decision to the callsite, not a trait subsystem (see how
| Zig implements hash mays for example). So I'd personally
| prefer to use a DSL, since it lets me specify what type
| of dispatch to use.
| kibwen wrote:
| Overloadable operators are not an instance of hidden
| control flow. Overloadable operators represent a user-
| defined function call, and thus can't influence control
| flow any more than a regular function. And if regular
| functions can't do anything weird to control flow (e.g.
| if your language already lacks exceptions (or even
| weirder things like Ruby-style procs)), then overloadable
| operators can't either.
|
| _> It locks you into a single implementation when you
| may want to do something different based on the context._
|
| If you want differing behavior in a certain context, and
| if you don't want to use a different method to make the
| differing behavior explicit (e.g. the `wrapping_add`
| methods that Rust provides on numeric types), then you
| can use a different type for that context, e.g. the
| `std::num::Wrapping` type that Rust provides.
| smj-edison wrote:
| > Overloadable operators are not an instance of hidden
| control flow.
|
| In general perhaps not, but in Zig it definitely does.
| Zig considers calling a function to change control flow,
| because it's no longer just an operator but something
| that can cause side effects, includinh mutating in place.
| Perhaps control flow isn't the right term, maybe non-
| trivial would be better?
|
| With regard to wrappers, I personally find them ugly
| since 1. They bring in indirection, and I have a personal
| vendetta against unnecessary indirection, 2. Wrapping
| doesn't compose well and is a pain to shephard between
| representations, 3. It's harder to make a function
| generic across different representations, and 4. Wrappers
| often don't re-export everything available to their
| underlying value.
| AnduCrandu wrote:
| It's appealing to people who want to understand and
| control everything they're doing. When I'm using pandas
| or SQLAlchemy, I have no idea what the code is actually
| doing. Most people don't care about such implementation
| details, but some people do.
| aaaashley wrote:
| yes! i had this exact idea. i also thought about
| integrating geometric/clifford algebra using zig's type
| system so that you could have one mathematical
| multivector object instead of complex / quaternion types,
| etc.
| smj-edison wrote:
| That's the other great thing about using comptime, is you
| can specify which DSL you want to use for which scenario.
| You're not locked into one implementation.
| dnautics wrote:
| I have a sibling comment -- having thought about this for
| a very very long time, zig should really implement binary
| pseudo-operator syntactic sugar. I don't think this
| violate zig's spirit of 'no hidden function calls' in
| that I don't think it takes much of a mental lift to
| "get" that (_ <+> _) means "heyo this is a function call,
| not a true operator".
| smj-edison wrote:
| At first I was going to say that I disagreed since you
| couldn't choose what implementation of addition you
| wanted, but now that I've read your comment where you
| import the type of addition used, it's growing on me.
| Would you have operator precedence, or would it be more
| like Smalltalk's binary operators?
| dnautics wrote:
| forced use of parens, or else syntax error.
| hmry wrote:
| Why have operators at all? x =
| x.add(step.mul(2)).mod(width)
|
| Or in C x = imod(iadd(x, imul(step, 2)),
| width)
|
| vs x = (x + 2*step) % width
|
| For me the answer is very simple: Operators make it easier to
| read the code which makes it easier to spot bugs. It also
| makes it easier to turn formulas from textbooks into code.
|
| If 50% of the code you're working with is using vectors and
| matrices, not having operators for those parts is quite
| annoying.
|
| Note that you can have vector operators without overloading,
| e.g. Odin has built in vector and matrix types.
|
| But personally I think it's better to give the user more
| power instead of only letting the compiler author pick which
| types to allow operators on. Like how Java overloads + but
| only on the String class. Why do they get to do it, but not
| me?
| applfanboysbgon wrote:
| Woah there, "=" is an operator! I'm afraid you're going to
| have to go to jail for using an operator in a no-operator
| zone.
| Decabytes wrote:
| > Why have operators at all?
|
| I mean as an avid Lisp fan, I feel like Lisp basically
| answers the question of how much syntax you need in a
| langauge. I must admit though, not having to deal with
| operators precedence is really nice (mod (+
| x (* 2 step)) width)
| adrian_b wrote:
| Regarding operators, there are 3 distinct problems.
|
| One is to allow the use of simple mathematical symbols as
| names for functions, instead of allowing only
| alphanumeric identifiers.
|
| Most programming languages allow only a small fixed set
| of symbols to be used as "operators", i.e. as function
| names.
|
| The better solution is to allow any Unicode character
| from certain categories, e.g. "Sm" and "Po" ("Symbol,
| math" and "Punctuation, other"), which does not have an
| already assigned role in the language syntax, to be used
| as a function name.
|
| Most LISP variants allow the use of various kinds of
| character symbols as function names.
|
| The second problem is overloading. Overloading must be
| treated uniformly for any kind of functions, regardless
| if their names are identifiers or operator symbols, i.e.
| not like in Java, where forbidding operator overloading
| was a mistake (that was an overreaction to C++, which
| allows the overloading of a few "operators" that are not
| normal functions and whose overloading should not have
| been allowed, e.g. the comma operator).
|
| The overloading of operators, especially for user-defined
| data types is something absolutely essential for
| scientific and technical computing.
|
| The majority of programmers have not been exposed to
| programs that contain a great amount of computations, so
| they are accustomed only with simple expressions that
| contain a few variables.
|
| In scientific and technical computing it is very frequent
| to have very big expressions, which may contain a large
| number of operations and variables, where the variables
| may have various types, like complex numbers, vectors,
| matrices, complex vectors, complex matrices, or there may
| be a type system with distinct types for various physical
| quantities, like voltages, electric currents,
| capacitances and so on.
|
| Anyone who had to write frequently such big expressions
| will definitely prefer, both for writing and for reading,
| to use overloaded operator symbols instead of long
| function names, which would fill most of the visual space
| with superfluous characters, obscuring the structure of
| the big expression.
|
| The third problem is the syntax of function invocation.
| Most programming languages allow functions whose names
| are identifiers to use only prefix invocation but for
| some symbolic operators they allow infix invocation.
|
| Here I also prefer the languages that do not
| differentiate between functions with alphanumeric names
| and functions with symbolic names (i.e. operators). There
| are languages where for any function it may be specified
| that it must be invoked as an infix operator, if this is
| desired.
|
| Which is the best between the 3 classic solutions for
| expression syntax, traditional expressions with infix
| operators and multi-level precedence rules (like in
| FORTRAN and ALGOL), expressions with infix operators and
| a unique precedence rule for all operators (like in APL)
| and expressions without infix operators (like in LISP),
| is debatable.
|
| Each of the 3 solutions has advantages and disadvantages,
| so the choice between them is a matter of personal
| preferences.
| dnautics wrote:
| you actually don't want "operator overloading", you want
| syntactic sugar. I once proposed just a special operator
| syntax at the parser level, but it got rejected, but if you
| REALLY wanted it, you could probably do this in about
| 100-120 lines as a fork of the zig compiler, just hacking
| (a <_> b) as a special form to be transformed into
| @"<_>"(a, b). Requiring parentheses elides questions about
| operator precedence. const @"<+>" =
| @import("operator_module").plus; ...
| const x = (a <+> b);
| fluffybucktsnek wrote:
| I think both operator overloading and most operators
| themselves are syntactic sugars. Operator overloading
| happens to point towards specific functions, whereas
| arithmetic integer operators point to compiler
| intrinsics.
| dnautics wrote:
| no, in general overloading is not syntactic sugar, it's a
| feature of the language (being able to (re-)define a
| function in place X and have it change the function in
| unrelated place Y).
| fluffybucktsnek wrote:
| I don't see how it is unrelated. If have a custom type
| `A` with an overload on `+`, it will only affect places I
| used custom type `A`. If there wasn't operator
| overloading, I would just have to use a different
| notation to call the same function, but with possibly
| worse ergonomics (which is also why I think your solution
| doesn't really satisfy that, it doesn't read like algebra
| which is kind of the point). Given that type A is
| presumed to be custom, I don't see how place Y would be
| unrelated since it deliberately uses type `A`.
|
| If we include operator overloading for any types, then
| sure. i32 + i32 might suddenly start meaning something
| else. But I think that's beyond the scope of what is
| normally asked by operator overloading.
| dnautics wrote:
| one is implementable entirely in the parser. overloading
| (operator or otherwise) in general is a deeper compiler
| feature
| Ciantic wrote:
| Rust should (eventually) support arrays of structures via
| compile-time reflection: https://fnordig.de/2026/03/25/rust-
| reflection-and-a-multi-ar...
| smj-edison wrote:
| I didn't realize compile time reflection was back on track,
| that's really exciting!
| the__alchemist wrote:
| > Meanwhile, game engines need operator overloading for
| adding/multiplying vectors (spatial transforms, lighting,
| physics) and core zig design philosophy prevents operator
| overloading.
|
| This is a frustrating decision. My use cases for low level
| languages overlap closely with my use cases for vectors (etc)
| with operator overloading. It was one of the first things which
| put a bad taste in my mouth about Zig.
| fasterik wrote:
| On the other hand, SIMD loves SoA, and so does the CPU cache.
| It all depends on what you're doing with your data.
|
| Zig professes to be a C replacement, not a C++ replacement, so
| leaving out operator overloading is consistent with that design
| goal. But I agree, I would prefer to program in a language that
| expresses mathematical relationships more naturally.
| nejam wrote:
| Jdoemhoe
| nejam wrote:
| Jfpe?
| blt wrote:
| After that build-up, I was hoping to see a toy implementation of
| a method or two for `MultiArrayList`.
___________________________________________________________________
(page generated 2026-06-09 05:01 UTC)