Yeah, and in the blog post he mentions that he had to transform the data to SoA. If he had done that alone, he might already have seen a speedup from better cache utilization.
Also, I see no mention of alignment in the post. I understand x86/AVX2 likes your load/stores to be aligned, even if it technically allows unaligned access.
Unaligned loads/stores aren't super bad; if still within a cacheline, there's zero penalty, and on crossing cachelines it's alike two ops (except page crossing, which is more bad).
So, for 32B loads/stores and 64B cachelines, it's 1.5x more L1 cache ops (as half of the ops will cross a cacheline); perhaps bad if you're L1-cache-throughput-bound, but less so if you're at L2+ as the extra work sits in L1.
>That said shipping JIT / interpreter with your program to recompile updates / parts of it sounds silly to me.
why is it silly? modern phones can easily do this (they are running local llm, what issue is with jit?), if it's done one time per update what's the problem?
what's the problem with jitting couple of hot paths which have been updated and were not part of base app?
>Apple does not allow running code from internet either (even if interpreted iirc)
And how is React native expo/eas updates are working then? Isn't it downloading a bytecode bundle?
There's 2 different parts of their terms about this. One seems to forbid it. The other basically says that you can do it as long as you aren't shipping some kind of update to the app that changes its nature significantly. Basically the generally accepted viewpoint in the industry is that you can OTA bugfixes and small changes but you if you ship whole features without going through app store review first you are definitely on thin ice.
Try reading zig code. For me its much more readable than the other languages, and does not suffer the fact go doesnt have language level errors. Local allocators are very useful and if you dont think so, perhaps you havent dwelved too deeply into systems programming or the language isnt targeted for you.
The dot syntax used everywhere really confuses me. I get its use in struct fields, or for defining anonymous structs, but what is this one for? (Some kind of module-level enum space, where .sampler and .unknown are defined previously?)
Yeah, after looking it up, it looks like it is basically only used as either field access or an 'infer operator', is that right?
I thought it was used in four completely separate ways:
· normal struct field access
· anonymous struct definition
· field definition within structs (for reasons to do with the parser)
· an extra 'infer operator' for syntactic sugar
But there's no support for anonymous structs/fields, and all structs and fields require a type somewhere for it to be inferred. Which is why this is invalid zig:
const test = .{ .x = 0, .y = 1 };
(It would need the type to be specified in the called function definition, or inline when assigning)
I don't think that "infer operator" is a special case of field access, to me it feels like regular known-type elision similar to how C# and C++ use the var keyword if the data type can be inferred from the rhs expression:
const Enum = enum {one, two, five};
const t: Enum = .one; // Enum.one, but the type was inferred from lhs
std.debug.print("{t}\n", .{t});
Defining an anonymous struct is valid in zig; your example is only invalid because "test" is a reserved keyword. But you are correct that it reifies into a concrete type, and after initialization it doesn't coerce into other types because zig doesn't do structural typing:
const anonymous = .{ .x = 0, .y = 1 };
std.debug.print("{}\n", .{@TypeOf(anonymous)}); // will output something like test_0__struct_45138
const Point = struct { x: i32, y: i32 };
const p1 = Point{ .x = 0, .y = 1 }; // valid, explicit struct literal
const p2: Point = .{ .x = 0, .y = 1 }; // valid, anonymous struct will coerce to Point
//const pt: Point = anonymous; // error: expected type 'test_0.Point', found 'test_0__struct_45138'
And then there's fieldless anonymous structs aka tuples. I'm including them because they were used in the print statements above:
All this does for me is raise the question of why they chose to use the `.` for so many different uses. I'd be fine if it was just to infer the type, but it seems very overloaded.
I get the usefulness of allocators, I just don't see them as useful enough where I'd pick zig over another established systems programming language. Do you have an example?
I think Zig would do better than Go at things like kernels, drivers, game engines, lower level sorts of things. Edited to add the obvious: SPIR-V, for instance.
Of course there’s lots of programming that can afford to pay for GC side effects, if there weren’t we wouldn’t have invented GC, but it’s a little less universal, a little less ‘system’.
For me, I came to Zig after horrible cross-platform experiences led me to try going all the way back to C and I found that I was spending way too much time learning to deal with accidental complexity instead of essential complexity. (Respect to the C masters but I failed to adapt.)
>I think Zig would do better than Go at things like kernels, drivers, game engines, lower level sorts of things. Edited to add the obvious: SPIR-V, for instance.
But people don't write those in Go, they use Rust for it
since zig anoints an allocator interface in its stdlib, your (and the stdlib's) data structures which use allocators can be trivially reused across different allocation strategies without rewriting code; and very likely (not guaranteed ofc) if you bring in someone else's code they will cleave to convention.
Quite depends, I had times when my posix emulation of io_uring (with poll, not epoll) was faster than io_uring. For large zero-copy buffers, io_uring is king however. Also io_uring is useful even for non asynchronous IO as it can implement chain of operations as single atomic operation (mkdir + open it for example).
For something like networking, if you are maximizing packets per second, you'll hit kernel limits[1] very quickly and instead have to start leveraging features like GSO/GRO or completely bypass the network stack.
I liked Symbian a lot, but I agree that Maemo was superior! Two after what I told above, in 2010, a few friends of mine had N900 and they seemed great. I was still in my study at the time and I interviewed for a summer internship at Nokia to work on Maemo and it was going great, but at some point during the recruitment process, that part of Nokia was sold (to Intel I think? the MeeGo project was announced a bit later) so they stopped all hiring even of interns and I had to find another internship.
Rust unsafe is less safe than writing C code. Rust is not good language for writing "unsafe code". C++ smart pointers are really bad compared to arena allocators, and not allocating at all. It's very easy to end up with dangling references in C++, or double frees with smart pointers. Also the fact that C++ initialization itself is a huge foot gun.
Lot but not enough still. Most web tech is like that, almost there but not really. Webaudio prob being the worst one. Webgpu being weird thing that nobody really knows who it is for.
reply