@ngn what can we do about [that](https://ngn.codeberg.page/k/#eJzT1jeM11PSUDSwVtLRULJW0ldRNDQwMNDUUdJUAgBJdgTb)? I figured out i reach a stack limit, when i parse a long list (1;2;...) because the byte code builds that list which pushes each element to the stack. you seem to do the same. What can we do about that?
i'll have to think about another method. it's still unfortunate that a native data structure (the list) cannot be parsed if it is too large, where large is 1000 already.
@rak1507 i'm alright, thanks. it just blocks my vision for up to half an hour at unpredictable times. doesn't hurt. and i hope it doesn't cause further long-term damage.
@chrispsn ah.. that makes sense
we could get rid of fd@ as "write()" and move it to fd 0: but what about all the `symbol@ overloads?
@dzaima's timings of dzaima/cbqn vs ngn/k: https://dzaima.github.io/paste/#0U1HITcxOVchWcHa2Tc5JzEtXUFbIS8/Tz@ZSgciU2OblZ2WWKKTZquv6Gyvounh5hsQHhzgGhdjqGqorJAPVOzsF@nFxKcSUKFQbalfUqisaGkAAl6GJiRGXgoKCJljqw9ypK2sPrXjUNhWuwNjQUM80txiqu0IbCSKbY2JiYAozB2SKNl4CzQ5LAwuoHdUaMEE7TSBTW1O/otaECypmAnEDdiWW5iDrDQ0ede3QVnjUsCi@PCMzJzVeAa4aKGGnqYBsGNi1ROgwMtazNAY6DwA
@rak1507 i've heard rumours that one apl-using company decided to measure the avg array length in their application and it turned out to be between 1 and 2 :)
oh i'm already focusing like everything I've got on optimizations constantly. And have written like almost no optimized code for high-rank arrays anyways :)
so, the claim that it's all marketing over science is wrong, and the claim that dyalog is 30x faster than ngn/k is also wrong - he was comparing +/ on a tall narrow matrix without splitting apl's matrix
point is that high rank allows you to do more things without worrying about performance. the 30x is small example in one point of the many in the article
Reading geocar's entry doesn't seem to contradict @Marshall's claim that you're barred from publishing your own benchmarks? That you can always repeat pre-packaged sets doesn't really tell you anything beyond that a specific test case, defined by someone else, takes X s to run on some other system?
@xpqz "there is no evidence" != "i have no access to such evidence". the evidence is there. it just costs a lot to access it.. and yeah, i too think that's bad. but that doesn't make k any slower.
@dzaima all it takes to do the fair comparison is a flip (monadic +)
@dzaima even dyalog sometimes recommends using "inverted tables" as roger calls them
concluding from the case being avoidable that it doesn't need to be mentioned is like saying an article about "JS bad" doesn't need to mention all the avoidable bad things in JS
@ktye scalar performance is an extremely important thing I constantly work on and test. I think the most important thing I'm doing everywhere is managing inlining (and outlining - extracting rare cases to a noinline function) & shortening the number of steps in the fast path case (have spent way too long on that and made many things ugly (but still fast) in the process)
the claims around K fitting into L1 making it fast have bugged me too. the restrictions on benchmarking the official K's are frustrating, but I'm not sure that ngn/k is a valid substitute for that purpose. I think this claim (and some others, like K is as fast as/faster than C or whatnot) come from a specific time and place where K was competing directly against C for the purposes of implementing trading systems. Java went through the same stuff
(the "problem" with Java was garbage collection causing unacceptable latency spikes. presumably this has been improved in the past couple of decades, but in the mid/late 90s it was likely a valid concern)
@dzaima ..which is pretty good, considering that this (which i assume is the main vm loop?) doesn't look like it'll convert to a nice lookup table switch
this is where my monadic ¨ ends up in this case. Specialized loop for an i32arr argument, and it directly calls the specific function pointer from the operand. 25 x86-64 instrs
@ngn it's just for(i=0;i<length;i++) res[i] = f(operand, x[i]); where f is a C function pointer (in the case of {1+𝕩}¨ it points to the entry of function block monadic calling). but yeah, i don't even know where to find your each impl. But I don't think you need to go that crazy to have a good each
(there's of course some type conversion and overflow checking in the real code, as you can see from my link not being literally that, but that's unrelated sad mess)
those should be times in the function itself, and none of its invocations (inlining can merge functions together though, but i don't think that should happen here)
(also, fwiw, my switches all have default: __builtin_unreachable(); to let the compiler know to not worry about bounds)
damn, "app" is huge https://dzaima.github.io/paste/#07R1Zc9vG@T2/AklnWkCCKVzERVEaRU46HieOr9pKPBwNjoVNi4JoEpJAVe1j39P@w/6SfotDxLG7AESqoiJzxhaJxS72O/e7duFMp7zA7Xw6O0U7/pUzPnV2Po6jnZOdk29@uEBhZHPewpug@TffvEQzDy5w2ecbrvxh/346njvzOTp1JwvuLODmyIvGZyHXi1Ac2eyu0s1Hky1DkrhdZzrdq3RyMBg2ofcb7kDmkRyISObj6HA/fmd/h2az78RYEAh3OheBeLAYnh0MXm1Nh87s48XgPb81FRdfeOfwCr5tbwuC4ExlfkEZAYXpCG/Eq3QYNERhMgoSX02HW2h7W/wynG7Px/xU/MvwL8IAxvbewPCncOELjD64Sh/2Zfvbb7fwhcXQOYXnXQmD@Ilzvp8/3V4Qnx8DrPE4Amiv9j9OeLhPFgYS6dbP80A8OuTfH7z5mT90P2iSpY8Gz7jP82N04Uz4wy3xmYj/EwbPwmF@NT4U41B0xTe/cS5MiJeEgXMY8q4YCsKLZCgUAqph2kcHPHI8@C7C…
i assume tagged data vs heap-allocated objects or something?
@dzaima ah, that's the imperative equivalent of a virtual function call. yeah, that would be long. but I get away with it being a bunch of small functions and not one big one so it seems less bad :D
this is how all function objects look. Then there's this field for heap-allocated objects that specifies what kind of function it is (user-defined block, builtin, etc)
@ngn CBQNs two types each do their own thing though. The tag shows user-visible type - array/char/number/function/modifier/namespace; the heap type shows the actual internal implementation
@dzaima it's t_funBI for a built-in, and t_fun_block for a user-defined block. But in most cases you don't need to care about that
a call to one is always "untag, read field, call that"
the union means i can't accidentally return a raw untagged integer. and since most things outside h.h don't need to look at the internal value manually, that's a good trade imo
@ngn in fact, originally I planned to actually have a separate i32 scalar type. But after writing a lot of code, i decided the extra complexity probably isn't worth it
(i never actually wrote any code for scalar i32 though, so who knows. maybe worth coming back to at some point)