My between functions, could be a PR for Base (but shouldn’t be needed… maybe non-exported building blocks for codegen?):
julia> between_arbitrary(from, op, to)=op(from, to) #do they violate coding standard with op (or in_val) in the middle?
julia> between_inclusive(from, in_val, to)=between_arbitrary(from, <=, in_val) & between_arbitrary(in_val, <=, to)
They simplify:
new_uppercase(c::Char) = begin test1='a' <= c; test2=c <= 'z'; return test1&test2 ? Char(xor(UInt32(c), 0x20)) : Char(ccall(:utf8proc_toupper, UInt32, (UInt32,), c)) end
to:
new_uppercase(c::Char) = !between_inclusive('a', c, 'z') ? Char(ccall(:utf8proc_toupper, UInt32, (UInt32,), c)) : Char(xor(UInt32(c), 0x20))
that assembly code, has one jump (one cmpl and ja each), not two jumps and compares as with straightforward (‘a’ <= c <= ‘z’).
[I don’t like that c ? “then” : “else”, considers the else, more likely; I fixed with !c and reversing. Both have as many jumps.]
Same applies to:
[Still frustrated to not see the 5x speedup that I timed.]
julia> new_uppercase_for_now_only_for_ascii(c::Char) = Char(xor(UInt32(c), between_inclusive('a', c, 'z') << 5))
julia> new_uppercase_for_now_only_for_ascii(c::Char) = Char(xor(UInt32(c), ('a' <= c <= 'z') << 5))
c = map(Char, rand(32:127, 5000000)); # note, not, that would work (getting to also run fast): c = rand(Char, 5000000);
julia> @time map(new_uppercase_for_now_only_for_ascii, c);
First run: 0.045256 seconds (8 allocations: 19.074 MB)
..
julia> @time map(new_uppercase_for_now_only_for_ascii, c);
0.017182 seconds (8 allocations: 19.074 MB)
vs.
julia> @time map(new_uppercase, c);
0.079274 seconds (8 allocations: 19.074 MB)
“Off-topic”:
Absurd is a nice word. I might disagree about it’s use here. I was going to let this drop; as I said, not important, at the bottom of the list of my concerns with the code here.
yes, for x86, no for “LLVM assembly language”
I should have remembered, or just done that.