#Difficulty understanding array types in Zig

1 messages · Page 2 of 1

warm pelican
#

This thread is basically an intro to zig lol

jade veldt
#
  1. You return a struct instead.
    An array can work if they're the same type, but then the caller just has to know what each one is - the struct obviously has fields that have names, so they can be self-explanatory.

  2. The obvious thing is to avoid the allocation, and have the user pass a buffer to you to output into instead, and validate that it's big enough at the start.
    Allocation is generally slow, though exactly how slow depends on what allocator you pick; for example, an arena allocator is pretty fast because it's specific-purpose, simple, and not thread-safe --- the GPA is general-purpose, and thread-safe, and not very optimized yet.

a) The two loops will also certainly NOT get optimized into one. You should use one if you can.
The stack is unrelated to this.

b) It does matter, yeah. As does how you're accessing it; iterating through a large array is quicker for small elements, than random access. But it's not just about the size; f32s are going to be faster than f16s, because f16s are not something that the CPU understands, so it'd need to do software emulation. GPUs are better in that case. But also, you'd have to decide on the tradeoff between speed, memory usage, and result accuracy; all things to consider.
As such though, I would stick with f32s.

  1. In times like this it's useful to ask yourself what you're actually trying to achieve, and see if you can spot inventive ways of reducing how much work needs to be done.
    For instance, as I said, allocation is slow -- and you need to free those allocations at some point too; "why do 3 when you could do 1?" 😄
    For example:
const backing = try allocator.alloc(f32, array_len * 3);
defer allocator.free(backing); // only need to free this one slice, instead of all three.

const xy = backing[0..array_len];
const x2 = backing[array_len..][0..array_len];
const y2 = backing[array_len*2..][0..array_len];
// we've just partitioned the 'backing' memory into three 'array_len'-sized pieces!
#

Also note that we can free backing there at the end of the scope (what defer does), and that's fine -- because you don't return any part of that; you just use it temporarily as "scratch space" (some space to put some intermediate stuff) while you figure out the answer.

#

You can also still use a similar approach as in my example if you don't use an allocator instead, just by partitioning the slice given to function, instead of the one returned from allocating - saves you needing the caller to pass three buffers instead of just one.

#
  1. can be done with export fn, because the default calling convention of exported functions in Zig is C's convention.
    But it's generally useful to be explicit in long-term code: export fn func() callconv(.C) void {}, for example.