Optimization 在Julia中优化高度矢量化的代码?

Optimization 在Julia中优化高度矢量化的代码?,optimization,julia,Optimization,Julia,我想优化Julia中的以下代码,它是以一种高度矢量化的形式编写的,在这种形式下,像MatlabExcel这样的语言可以使用。MATLAB中完全相同的代码所花费的时间是0.277608秒。,快了2.8倍,所以我认为可以在Julia中完成一些事情。公平地说,我注意到MATLAB默认使用多线程,所以如果在Julia中也启用了多线程也没有问题。谢谢你的帮助 function fit_xlin(x, y, w) n = length(x) regularization = 1.0e-5

我想优化Julia中的以下代码,它是以一种高度矢量化的形式编写的,在这种形式下,像MatlabExcel这样的语言可以使用。MATLAB中完全相同的代码所花费的时间是0.277608秒。,快了2.8倍,所以我认为可以在Julia中完成一些事情。公平地说,我注意到MATLAB默认使用多线程,所以如果在Julia中也启用了多线程也没有问题。谢谢你的帮助

function fit_xlin(x, y, w)
    n = length(x)
    regularization = 1.0e-5
    xx_0_0 = fill(sum(w.*1)   , n)
    xx_1_0 = fill(sum(w.*x)   , n)
    xx_0_1 = fill(sum(w.*x)   , n)
    xx_1_1 = fill(sum(w.*x.*x), n)
    xy_0   = fill(sum(w.*y)   , n)
    xy_1   = fill(sum(w.*x.*y), n)
    xx_1_0 .+= regularization
    xx_0_1 .+= regularization

    xxk_0_0 = xx_0_0 .- w.*1
    xxk_1_0 = xx_1_0 .- w.*x
    xxk_0_1 = xx_0_1 .- w.*x
    xxk_1_1 = xx_1_1 .- w.*x.*x
    xyk_0   = xy_0   .- w.*y
    xyk_1   = xy_1   .- w.*x.*y

    det = xxk_0_0.*xxk_1_1 .- xxk_0_1.*xxk_1_0
    c0  = (xxk_1_1.*xyk_0  .- xxk_0_1.*xyk_1)./det
    c1  = (-xxk_1_0.*xyk_0 .+ xxk_0_0.*xyk_1)./det

    y_est = c0 .+ c1.*x
end 

using BenchmarkTools
function test_xlin()
    x = rand( 0.0:4.0, 5000_000)
    y = rand( 0.0:4.0, 5000_000)
    w = rand( 0.0:4.0, 5000_000)
    @btime fit_xlin($x, $y, $w)
end 
这一次是:

    julia> test_xlin();
      775.292 ms (46 allocations: 877.38 MiB)

这里的代码仍然是矢量化的,并且不使用多线程。在我的电脑上,它的速度是原来的两倍多一点,但仍然有一些事情可以在这里完成

顺便说一句,我严重怀疑Matlab代码是否经过了特别优化,因为存在一些非常浪费的操作——不必要的分配,不必要的操作(
sum(w.*1)
非常糟糕,将数组乘以1并在过程中分配额外的数组是非常浪费的:)此外,您也不需要在Matlab中分配任何向量
xx_0_0
xx_1_0
,您可以像在Julia中一样使用广播

不管怎样,这是我的第一次尝试:

function fit_xlin2(x, y, w)
    regularization = 1.0e-5

    sumwx = (w' * x) + regularization
    sumwy = (w' * y)
    sumwxx = sum(a[1]*a[2]^2 for a in zip(w, x))
    sumwxy = sum(prod, zip(w, x, y))

    wx = w .* x
    xxk_0_0 = sum(w) .- w
    xxk_1_0 = sumwx .- wx
    xxk_1_1 = sumwxx .- wx .* x
    xyk_0 = sumwy .- w .* y
    xyk_1 = sumwxy .- wx .* y

    det = xxk_0_0 .* xxk_1_1 .- xxk_1_0 .* xxk_1_0
    c0  = (xxk_1_1 .* xyk_0  .- xxk_1_0 .* xyk_1)./det
    c1  = (-xxk_1_0 .* xyk_0 .+ xxk_0_0 .* xyk_1)./det

    return c0 .+ c1 .* x
end
编辑:通过对主循环进行分解,您可以获得相当大的速度提升。此代码比原始Julia代码快约17倍,仍然是单线程的,可读性很强:

function fit_xlin_loop(x, y, w)
    if !(size(x) == size(y) == size(w))
        error("Input vectors must have the same size.")
    end

    regularization = 1.0e-5

    sumw = sum(w)
    sumwx = (w' * x) + regularization
    sumwy = (w' * y)
    sumwxx = sum(a[1]*a[2]^2 for a in zip(w, x))
    sumwxy = sum(prod, zip(w, x, y))

    y_est = similar(x)
    @inbounds for i in eachindex(y_est)
        wx = w[i] * x[i]
        xxk_0_0 = sumw - w[i]
        xxk_1_0 = sumwx - wx
        xxk_1_1 = sumwxx - wx * x[i]
        xyk_0 = sumwy - w[i] * y[i]
        xyk_1 = sumwxy - wx * y[i]

        det = xxk_0_0 * xxk_1_1 - xxk_1_0 * xxk_1_0
        c0  = (xxk_1_1 * xyk_0 - xxk_1_0 * xyk_1) / det
        c1  = (-xxk_1_0 * xyk_0 + xxk_0_0 * xyk_1) / det

        y_est[i] = c0 + c1 * x[i]
    end
    return y_est
end

这里的代码仍然是矢量化的,并且不使用多线程。在我的电脑上,它的速度是原来的两倍多一点,但仍然有一些事情可以在这里完成

顺便说一句,我严重怀疑Matlab代码是否经过了特别优化,因为存在一些非常浪费的操作——不必要的分配,不必要的操作(
sum(w.*1)
非常糟糕,将数组乘以1并在过程中分配额外的数组是非常浪费的:)此外,您也不需要在Matlab中分配任何向量
xx_0_0
xx_1_0
,您可以像在Julia中一样使用广播

不管怎样,这是我的第一次尝试:

function fit_xlin2(x, y, w)
    regularization = 1.0e-5

    sumwx = (w' * x) + regularization
    sumwy = (w' * y)
    sumwxx = sum(a[1]*a[2]^2 for a in zip(w, x))
    sumwxy = sum(prod, zip(w, x, y))

    wx = w .* x
    xxk_0_0 = sum(w) .- w
    xxk_1_0 = sumwx .- wx
    xxk_1_1 = sumwxx .- wx .* x
    xyk_0 = sumwy .- w .* y
    xyk_1 = sumwxy .- wx .* y

    det = xxk_0_0 .* xxk_1_1 .- xxk_1_0 .* xxk_1_0
    c0  = (xxk_1_1 .* xyk_0  .- xxk_1_0 .* xyk_1)./det
    c1  = (-xxk_1_0 .* xyk_0 .+ xxk_0_0 .* xyk_1)./det

    return c0 .+ c1 .* x
end
编辑:通过对主循环进行分解,您可以获得相当大的速度提升。此代码比原始Julia代码快约17倍,仍然是单线程的,可读性很强:

function fit_xlin_loop(x, y, w)
    if !(size(x) == size(y) == size(w))
        error("Input vectors must have the same size.")
    end

    regularization = 1.0e-5

    sumw = sum(w)
    sumwx = (w' * x) + regularization
    sumwy = (w' * y)
    sumwxx = sum(a[1]*a[2]^2 for a in zip(w, x))
    sumwxy = sum(prod, zip(w, x, y))

    y_est = similar(x)
    @inbounds for i in eachindex(y_est)
        wx = w[i] * x[i]
        xxk_0_0 = sumw - w[i]
        xxk_1_0 = sumwx - wx
        xxk_1_1 = sumwxx - wx * x[i]
        xyk_0 = sumwy - w[i] * y[i]
        xyk_1 = sumwxy - wx * y[i]

        det = xxk_0_0 * xxk_1_1 - xxk_1_0 * xxk_1_0
        c0  = (xxk_1_1 * xyk_0 - xxk_1_0 * xyk_1) / det
        c1  = (-xxk_1_0 * xyk_0 + xxk_0_0 * xyk_1) / det

        y_est[i] = c0 + c1 * x[i]
    end
    return y_est
end

这里可能有很多问题需要解决,但首先要做的是将
sum(w.*1)
(太糟糕了!)更改为
sum(w)
sum(w.*x)
更改为
dot(w,x)
。这里可能有很多问题需要解决,但首先要做的是将
sum(w.*1)
(太糟糕了!)更改为
sum(w)
sum(w.*x)
to
dot(w,x)
等。很多线性代数运算正在进行,如果您没有设置
BLAS,它应该是多线程的。将\u num\u threads(n)
设置为1或让机器具有一个逻辑核。这可能就是OP在MATLAB上“默认多线程”的意思。在Julia中,BLAS操作默认也是多线程的。我认为上面的任何代码都不会调用任何BLAS例程。这些都是广播的元素操作,不使用BLAS。例外是我在
w'*x
等中使用了
dot
,但我怀疑这也会调用BLAS。无论如何,更改BLAS线程的数量没有任何效果。非常感谢,这太棒了!我简直不敢相信,现在朱莉娅已经在MATLAB的擅长方面击败了它。下一步是在默认情况下启用多线程,我的意思是对于sum、prod等常规函数,而不仅仅是已经是多线程的BLAS。@DNF,你说得对。我曾经看到过很多单独的
*
,它们属于devectorized版本。很多线性代数操作正在进行,如果您没有将
BLAS设置为多线程,那么它应该是多线程的。将\u num\u threads(n)
设置为1或者让机器具有一个逻辑核。这可能就是OP在MATLAB上“默认多线程”的意思。在Julia中,BLAS操作默认也是多线程的。我认为上面的任何代码都不会调用任何BLAS例程。这些都是广播的元素操作,不使用BLAS。例外是我在
w'*x
等中使用了
dot
,但我怀疑这也会调用BLAS。无论如何,更改BLAS线程的数量没有任何效果。非常感谢,这太棒了!我简直不敢相信,现在朱莉娅已经在MATLAB的擅长方面击败了它。下一步是在默认情况下启用多线程,我的意思是对于sum、prod等常规函数,而不仅仅是已经是多线程的BLAS。@DNF,你说得对。我已经看到了很多
*
单独的版本,它们属于devectorized版本。