Optimizing `Normed` -> `Normed` conversions
kimikage opened this issue · comments
As I suggested here, the current Normed
-> Normed
conversions are inefficient in some cases.
FixedPointNumbers.jl/src/normed.jl
Lines 41 to 46 in 8d17739
The current conversion method has two problems:
- It always checks the input range even if there is no need, and may throw the exception.
- It always uses floating-point operations even if there is no need.
The former means that the method is not SIMD-suitable.
Regarding the latter, the conversion between types with the same f
is already specialized.
FixedPointNumbers.jl/src/normed.jl
Line 13 in ee5bd54
There is also the
N0f8
->N0f16
specialization. (I wonder why.)FixedPointNumbers.jl/src/normed.jl
Line 47 in ee5bd54
I do not think these are urgent problems. However, the optimization may be useful in the future to speed up the accumulation (reduce
). And I just found a (ugly) workaround for the constant division problem, so I am writing this issue as a memorandum or reminder.
The figures below visualize the cases where the optimization is available.
- The positive (greenish) area means that the conversion is overflow-safe (i.e. there is no need for the range checking).
- The negative (reddish) area means that the conversion is unsafe (i.e. it may throw the exception).
- The deep-colored cells means that the conversion does not need floating-point operations.
- As mentioned above, the
f1 == f2
lines are already supported.
- As mentioned above, the
You can get the result of other cases with the following script:
using Gadfly, Colors
set_default_plot_size(15cm, 8cm)
function mat(dest, src)
b1, b2 = 8*sizeof(dest), 8*sizeof(src)
if b1 > b2 # widening
safe = [b1-f1 > b2-f2 || b2 == f2 ? 1 : -1 for f2=1:b2, f1=1:b1]
else
safe = [b1-f1 >= b2-f2 ? 1 : -1 for f2=1:b2, f1=1:b1]
end
safe .* [isinteger(f1/f2) ? f2/f1 : 1/36 for f2=1:b2, f1=1:b1]
end;
function plot_mat(dest, src)
s = Scale.color_continuous(
colormap=Scale.lab_gradient(LCHab(0, 100, 20), "white", LCHab(30, 100, 200)),
minvalue=-1, maxvalue=1)
m = mat(dest, src)
spy(m,
Guide.title("Normed{$src,f2} -> Normed{$dest,f1}"),
Guide.xlabel("f1"), Guide.xticks(ticks=axes(m, 2)),
Guide.ylabel("f2"), Guide.yticks(ticks=axes(m, 1)),
Guide.colorkey(title="scale"), s,
Theme(plot_padding=[0mm,2mm,5mm,0mm]))
end;
plot_mat(UInt16, UInt8)
plot_mat(UInt32, UInt8)
plot_mat(UInt32, UInt16)
Edit: The safe areas are wrong. I forgot to take account of the "carry" or "overlapping". I will soon fix the figures and the script above. Updated.
There is also the N0f8->N0f16 specialization. (I wonder why.)
I can't remember for sure, it seemed important at the time 😄. (Probably, performance.) But it's certainly more specific than needed. Julia's compiler was not what it is now, so the implementations in #138 probably would have resulted in a big branch.
And I agree, Normed->Normed conversions may be a good way to improve reduce
. Kudos as always for looking into this!