【问题标题】:How can the speed of nested arrays in Julia be improved?如何提高 Julia 中嵌套数组的速度?
【发布时间】:2017-03-24 14:30:39
【问题描述】:

以下函数nested_arrays 生成(令人惊讶的)“深度”n 的嵌套数组。但是,即使使用较小的 n 值(23 等)运行,运行和显示输出也需要相当长的时间。

julia> nested_arrays(n) = n == 1 ? [1] : [nested_arrays(n - 1)]
nested_arrays (generic function with 1 method)

julia> nested_arrays(1)
1-element Array{Int64,1}:
 1

julia> nested_arrays(2)
1-element Array{Array{Int64,1},1}:
 [1]

julia> nested_arrays(3)
1-element Array{Array{Array{Int64,1},1},1}:
 Array{Int64,1}[[1]]

julia> nested_arrays(10)
1-element Array{Array{Array{Array{Array{Array{Array{Array{Array{Array{Int64,1},1},1},1},1},1},1},1},1},1}:
 Array{Array{Array{Array{Array{Array{Array{Array{Int64,1},1},1},1},1},1},1},1}[Array{Array{Array{Array{Array{Array{Array{Int64,1},1},1},1},1},1},1}[Array{Array{Array{Array{Array{Array{Int64,1},1},1},1},1},1}[Array{Array{Array{Array{Array{Int64,1},1},1},1},1}[Array{Array{Array{Array{Int64,1},1},1},1}[Array{Array{Array{Int64,1},1},1}[Array{Array{Int64,1},1}[Array{Int64,1}[[1]]]]]]]]]

有趣的是,当在行尾使用@time 宏或; 时,计算结果花费的时间相对较少。相反,REPL 中结果的实际显示花费了大部分时间。

这种奇怪的行为不会在 Python 中显示出来。

In [1]: def nested_lists(n):
   ...:     if n == 1:
   ...:         return [1]
   ...:     return [nested_lists(n - 1)]
   ...: 

In [2]: nested_lists(10)
Out[2]: [[[[[[[[[[1]]]]]]]]]]

In [3]: %time nested_lists(100)
CPU times: user 0 ns, sys: 0 ns, total: 0 ns
Wall time: 37.7 µs
Out[3]: [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[1]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]

为什么这个函数在 Julia 中这么慢? Julia 是否为Array{T, 1} 中的不同类型T 重新编译display 函数?如果有,这是为什么呢?

这段代码的速度可以提高吗,还是不应该在 Julia 中完成?在实际意义上,我对此的主要关注点是,例如,加载一个复杂的嵌套 JSON 文件,其中无法简单地使用 n 维数组。

【问题讨论】:

    标签: arrays nested julia


    【解决方案1】:

    是的,这完全是由于编译时间造成的。您可以通过@time-ing display 看到这一点。第二次显示很快:

    julia> nested_arrays(n) = n == 1 ? [1] : [nested_arrays(n - 1)]
    nested_arrays (generic function with 1 method)
    
    julia> @time display(nested_arrays(15));
    1-element Array{Array{Array{Array{Array{Array{Array{Array{Array{Array{Array{Array{Array{Array{Array{Int64,1},1},1},1},1},1},1},1},1},1},1},1},1},1},1}:
     Array{Array{Array{Array{Array{Array{Array{Array{Array{Array{Array{Array{Array{Int64,1},1},1},1},1},1},1},1},1},1},1},1},1}[Array{Array{Array{Array{Array{Array{Array{Array{Array{Array{Array{Array{Int64,1},1},1},1},1},1},1},1},1},1},1},1}[Array{Array{Array{Array{Array{Array{Array{Array{Array{Array{Array{Int64,1},1},1},1},1},1},1},1},1},1},1}[Array{Array{Array{Array{Array{Array{Array{Array{Array{Array{Int64,1},1},1},1},1},1},1},1},1},1}[Array{Array{Array{Array{Array{Array{Array{Array{Array{Int64,1},1},1},1},1},1},1},1},1}[Array{Array{Array{Array{Array{Array{Array{Array{Int64,1},1},1},1},1},1},1},1}[Array{Array{Array{Array{Array{Array{Array{Int64,1},1},1},1},1},1},1}[Array{Array{Array{Array{Array{Array{Int64,1},1},1},1},1},1}[Array{Array{Array{Array{Array{Int64,1},1},1},1},1}[Array{Array{Array{Array{Int64,1},1},1},1}[Array{Array{Array{Int64,1},1},1}[Array{Array{Int64,1},1}[Array{Int64,1}[[1]]]]]]]]]]]]]]
     11.682721 seconds (8.83 M allocations: 371.698 MB, 1.82% gc time)
    
    julia> @time display(nested_arrays(15));
    1-element Array{Array{Array{Array{Array{Array{Array{Array{Array{Array{Array{Array{Array{Array{Array{Int64,1},1},1},1},1},1},1},1},1},1},1},1},1},1},1}:
     Array{Array{Array{Array{Array{Array{Array{Array{Array{Array{Array{Array{Array{Int64,1},1},1},1},1},1},1},1},1},1},1},1},1}[Array{Array{Array{Array{Array{Array{Array{Array{Array{Array{Array{Array{Int64,1},1},1},1},1},1},1},1},1},1},1},1}[Array{Array{Array{Array{Array{Array{Array{Array{Array{Array{Array{Int64,1},1},1},1},1},1},1},1},1},1},1}[Array{Array{Array{Array{Array{Array{Array{Array{Array{Array{Int64,1},1},1},1},1},1},1},1},1},1}[Array{Array{Array{Array{Array{Array{Array{Array{Array{Int64,1},1},1},1},1},1},1},1},1}[Array{Array{Array{Array{Array{Array{Array{Array{Int64,1},1},1},1},1},1},1},1}[Array{Array{Array{Array{Array{Array{Array{Int64,1},1},1},1},1},1},1}[Array{Array{Array{Array{Array{Array{Int64,1},1},1},1},1},1}[Array{Array{Array{Array{Array{Int64,1},1},1},1},1}[Array{Array{Array{Array{Int64,1},1},1},1}[Array{Array{Array{Int64,1},1},1}[Array{Array{Int64,1},1}[Array{Int64,1}[[1]]]]]]]]]]]]]]
      0.001688 seconds (2.38 k allocations: 102.766 KB)
    

    那么为什么这么慢呢?这里的显示递归遍历所有数组并将它们相互嵌套打印出来。这是用 14 种不同类型递归调用 show — 一种有 14 个嵌套数组,然后它的元素有 13 个嵌套数组,然后它的元素有 12 个……等等!每个show 方法都被独立编译。为特定元素类型编译专门的方法是 Julia 如何生成非常高效的代码的关键部分。这意味着它能够专门化在每个元素上完成的每一个操作,而无需任何运行时类型检查或分派。不幸的是,在这种情况下,它妨碍了。

    您可以使用Any[] 数组解决此问题;在 JSON 文件的上下文中,这很有意义,因为您不知道它是否包含字符串、数组或数字等。这要快得多,因为它只需要为 @987654327 编译 show 方法@array 一次,然后递归使用它。

    # new session
    julia> nested_arrays(n) = n == 1 ? Any[1] : Any[nested_arrays(n - 1)]
    nested_arrays (generic function with 1 method)
    
    julia> @time display(nested_arrays(15));
    1-element Array{Any,1}:
     Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[1]]]]]]]]]]]]]]
      1.571632 seconds (767.12 k allocations: 32.472 MB, 1.04% gc time)
    
    julia> @time display(nested_arrays(15));
    1-element Array{Any,1}:
     Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[1]]]]]]]]]]]]]]
      0.000606 seconds (839 allocations: 30.859 KB)
    
    julia> @time display(nested_arrays(100));
    1-element Array{Any,1}:
     Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[Any[1]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
      0.002523 seconds (17.76 k allocations: 579.297 KB)
    

    【讨论】:

    • 我要补充一点,这是一个例子,说明 Julia 倾向于编译专门版本的函数(这通常会使 Julia 速度更快)是错误的:最好只编译一个单一的、缓慢的、通用版本的数组的显示函数。 Python 总是这样做,在这种情况下,它恰好是正确的做法。将来,专业化启发式可以很容易地变得更智能,而无需更改任何语言语义。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-07-31
    • 2018-12-29
    • 1970-01-01
    • 2020-11-19
    • 2020-02-02
    • 2020-03-11
    • 1970-01-01
    相关资源
    最近更新 更多