With all the dimension keyword and higher-order reduction machinery around it these days, it’s actually quite hard to find the meat of the sum
implementation, and when you find it, it’s kind of hard to figure out what’s going on. But that’s just because the same code is used for all reductions (reduce
, sum
, prod
, etc.). It’s not because there’s anything going on that the user can’t do themselves—there’s nothing special or built-in about it. The straightforward mysum
implementation that @lungben wrote is super simple and even faster than the built-in sum
, although it’s less accurate since it doesn’t use pairwise summation.
Regarding the add_int
intrinsic: every programming language at some point needs to step out of itself and explain how actual work gets done. Otherwise you’d have a turtles all the way down situation: if everything is generic functions calling other generic functions, how do you get to machine code? At some point you have to stop stacking turtles and actually do a hardware add operation. The add_int
intrinsic is how you can tell Julia, “stop calling generic functions and just do a hardware add here.”
The sortperm
function is no different than sum
in this regard: it doesn’t use addition, but if you’re sorting a floating-point array, it will use Core.Intrinsics.lt_float
to compare them and Core.arrayref
and Core.arrayset
to read and write array values. The latter two are built-in non-generic functions rather than intrinsics. Once upon a time, intrinsics were something that only code generation knew how to emit and didn’t have a runtime C implementation, whereas built-in functions only had a runtime callable C implementation and code gen didn’t know how to emit them. These days we have an interpreter which has runtime implementations of all intrinsics and the compiler now knows how to generate code for most (all?) built-ins. So that distinction is no longer really meaningful and only persists because it’s not really worth the time and effort to change it. Historically, however, that’s why there are both intrinsics and built-ins.
Users don’t generally have to call or know about built-ins or intrinsics (although they can) because all the operations they implement are available via generic functions like +
that are exported from Base
and which, unlike built-in functions or intrinsics, you can extend to your own types. From the normal Julia user’s perspective, it’s all just generic functions. But any programming language that does actual work needs to have some kind of primitive exit point where it can invoke machine code.