【问题标题】:Why my Julia code runs slower than javascript?为什么我的 Julia 代码运行速度比 javascript 慢?
【发布时间】:2018-05-02 17:36:08
【问题描述】:

最近,我对 Julia-lang 很感兴趣,因为它声称是一种具有接近 C 性能的动态语言。但是,到目前为止,我的经验并不好(至少在性能方面)。 我正在编写的应用程序需要随机访问特定数组索引,然后将它们的值与其他特定数组索引进行比较(经过多次迭代)。以下代码模拟了我对程序的需求: 我的 Julia 代码在大约 8 秒内完成执行,而 java 脚本代码在 chrome 环境中需要不到 1 秒的时间! 我对 Julia 代码做错了吗?提前非常感谢。

这里是 Julia 代码:

n=5000;
x=rand(n)
y=rand(n)
mn=ones(n)*1000;
tic();
for i in 1:n;
    for j in 1:n;
        c=abs(x[j]-y[i]);
        if(c<mn[i])
            mn[i]=c;
        end
    end
end
toc();

Javascript 代码:(> 比上面的 julia 代码快 8 倍!)

n=5000; x=[]; y=[]; mn=[];
for(var i=0; i<n; i++){x.push(Math.random(1))}
for(var i=0; i<n; i++){y.push(Math.random(1))}
for(var i=0; i<n; i++){mn.push(1000)}
console.time('test');
for(var i=0; i<n; i++){
    for(var j=0; j<n; j++){
        c=Math.abs(x[j]-y[i]);
        if(c<mn[i]){
            mn[i]=c;
        }       
    }
} 
console.timeEnd('test');

【问题讨论】:

  • 如果您还没有从(优秀的)答案中收集到这一点,请阅读官方文档中的performance tips 部分。它将极大地帮助您避免这些性能问题,而无需深入了解 Julia 的内部运作方式。
  • 非常感谢,现在我看到了 Julia 在哪里以及如何在 JavaScript 性能方面大放异彩。

标签: performance julia benchmarking


【解决方案1】:

Performance Tips

避免使用全局变量

一个全局变量可能有它的值,因此它的类型,改变 在任何时候。这使得编译器难以优化 使用全局变量的代码。变量应该是本地的,或者作为 尽可能为函数提供参数。

任何对性能至关重要或进行基准测试的代码都应该是 在函数内部。

我们发现全局名称通常是常量,并声明它们 因此大大提高了性能:

julia> const n = 5000; const x, y = rand(n), rand(n); const mn = fill(1000.0, n);

julia> function foo!(mn, x, y)
           n = length(mn)
           @inbounds for i in 1:n, j in 1:n
               c = abs(x[j] - y[i])
               if(c < mn[i])
                   mn[i] = c
               end
           end
           return mn
       end
foo! (generic function with 1 method)

julia> using BenchmarkTools: @btime

julia> @btime foo!(mn, x, y)
  15.432 ms (0 allocations: 0 bytes)

【讨论】:

  • 随时更改:更具体地说,作为任何(非内联)函数调用的可能副作用,对吧? Julia 全局变量不像 C++ std::atomic 共享全局变量,它们可以被其他线程异步更改吗? (我不太了解 Julia,只是好奇而已。但我认为性能建议是说当前编译器不会在全局变量上寻找优化,即使唯一的函数调用是 abs(),它应该是内置的并且可以内联。所以这是当前编译器中错过的优化,对吧?因此仍然是很好的建议。)
  • 不是专家,但我认为问题在于,如果变量持有的值的 种类 可以随时更改(即在每个函数调用中),那么您不能只使用采用 Int 的 abs 函数(然后针对 'Int 进行优化,因为你不知道它将是一个 Int。所以你仍然必须调用“通用”,检查类型,然后运行正确的。此外,您甚至不确定 map 的结果类型,因为它可能会改变(当 abs 函数改变时)。编译器无法优化他无法推断的东西
  • 也不是专家,但我认为任何时候更改的问题都来自于函数调用,并且所有内容都是可覆盖的。 - 是一个函数调用,&lt;x[j] 甚至 = IIRC 也是如此。 x[j] 真的是 getindex(x, i),理论上可以在你的代码中覆盖它来改变 x(尽管这显然是一个糟糕的想法)。它仍然是一个错过的优化,因为可以为编译器提供非变异内置函数的知识,验证它们没有被覆盖并在此基础上进行优化,但它并非微不足道,并且有其自身的问题。
【解决方案2】:

Julia 代码应始终位于函数内部,以便编译器对其进行优化。此外,您正在测量编译时间和执行时间:要获得准确的测量结果,您应该调用该函数两次(第一次用于编译)。

function test(n)
    x=rand(n);
    y=rand(n);
    mn=ones(n)*1000;

    for i in 1:n;
        for j in 1:n;
            c=abs(x[j]-y[i])
            if(c<mn[i])
                mn[i]=c
            end
        end
    end
    (mn, x, y)
end

test(1)
@time test(5000);

这在我的笔记本电脑上需要 0.04 秒。铬 1s 中的 javascript (firefox web console 53s中的javascript)

【讨论】:

  • 没错。此外,您可以在 for 循环中添加 @inbounds 注释,并将 xymn 的创建移动到函数外部并传递它们(与 JavaScript 代码中的工作量相同)。最后,如果您第二次运行@time ...,执行时间应该会更快,因为您不会支付编译成本。
猜你喜欢
  • 2016-10-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多