Add getCarry and getBorrow operations - #27
Conversation
|
we tend to avoid |
In assembly, what you're looking for is to verify that we use the carry flag which is usually the case with add/sub. Also, no need to call out these operations as special - ie the aim overall is to have fine-grained operations that allow the caller to select the version that offers the smallest API possible in every (reasonably hardware-supported, on some platform) integer situation - while on typical x86 this might result in an operation whose implementation simply calls some other function (just like div on x86 always gives remainder too), it might be the case that on some platforms this might change in the future and we might have a "better" alternative. |
… implementations; drop the `get` from the op names; grop `in` from the input param names.
…dd benchmarks. The carryingAdd benchmark was too optimistic as it ignored the carry flag entirely allowing the compiler to remove its computation entirely.
The carryingAdd benchmark was too optimistic as it ignored the carry flag entirely allowing the compiler to remove its computation entirely.
|
@arnetheduck Thanks for the comments!
I've renamed
This is a very valuable comment, thanks. I spent a lot of time figuring out the best way to implement carry without copying and pasting code. Those "proxy ops" felt like a good idea under the assumption that implementing carry fundamentally means reusing carryingAdd. Well, this assumption is simply wrong, and I've implemented carry and borrow that are faster than carryingAdd and borrowingSub in pure Nim and using C intrinsics. |
arnetheduck
left a comment
There was a problem hiding this comment.
Nice!
There will always be some tension between reusing code and maintaining a semblance of structure and duplication - it's easy to go too far in either direction, ie sometimes it's more clear to write the same code twice than to introduce elaborate code sharing schemes.
glibc vs musl is a telling example of this point where glibc has a massively complex include directory structure while musl doesn't bother and copy-pastes freely .. when you want to find out what code is actually being used on your platform, the latter is certainly easier to understand for the reader -> sometimes duplication is a feature and the trick is to learn when ;) Hardware-near code in particular has a different maintenance cost ratio since the hardware itself changes slowly - it means that duplication in hardware-near code is cheaper too since it's unlikely to change often.
Co-authored-by: Jacek Sieka <arnetheduck@gmail.com>
Closes #22
I've researched the ways of calculating the carry flag without calculating the sum and it turns out calculating the sum and discarding the sum is the optimal solution.
I didn't want to put getCarry or getBorrow into composite.nim as those are not composite. But repeating the same
carryingAdd(a, b, carryIn).carryOutfor all implementations would be silly.So my solution is to simply add the necessary logic directly into the dispatcher. This diverges from the original architecture where dispatchers are purely
when-branches but I'd rather fix the docs and allow that than force a clumsy code pattern for the sake of purity.The PR does not include benchmarks. This is because there are no implementations for
getCarryorgetBorrow, it's just sugar aroundcarryingAddandborrowingSub.