Skip to content

Optimizing Normed -> Normed conversions #140

Closed
@kimikage

Description

@kimikage

As I suggested here, the current Normed -> Normed conversions are inefficient in some cases.

function Normed{T,f}(x::Normed{T2}) where {T <: Unsigned,T2 <: Unsigned,f}
U = Normed{T,f}
y = round((rawone(U)/rawone(x))*reinterpret(x))
(0 <= y) & (y <= typemax(T)) || throw_converterror(U, x)
reinterpret(U, _unsafe_trunc(T, y))
end

The current conversion method has two problems:

  1. It always checks the input range even if there is no need, and may throw the exception.
  2. 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.

Normed{T1,f}(x::Normed{T2,f}) where {T1 <: Unsigned,T2 <: Unsigned,f} = Normed{T1,f}(convert(T1, x.i), 0)

There is also the N0f8->N0f16 specialization. (I wonder why.)
N0f16(x::N0f8) = reinterpret(N0f16, convert(UInt16, 0x0101*reinterpret(x)))

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.

n8_n16
n8_n32
n16_n32

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions