Arrays julia:实现VCN阵列的有效方法

Arrays julia:实现VCN阵列的有效方法,arrays,julia,Arrays,Julia,我有一个从json加载的数据结构,如下所示 json_in = [ Dict("customer" => "cust1", "transactions" => 1:10^6) , Dict("customer" => "cust2", "transactions" => 1:10^6) , Dict("customer" => "cust3", "transactions" => 1:10^6)] 我知道有两种方法可以将事务折叠到一个数组中 @t

我有一个从json加载的数据结构,如下所示

json_in =
  [ Dict("customer" => "cust1", "transactions" => 1:10^6)
  , Dict("customer" => "cust2", "transactions" => 1:10^6)
  , Dict("customer" => "cust3", "transactions" => 1:10^6)]
我知道有两种方法可以将事务折叠到一个数组中

@time methodA = reduce(vcat,[cust["transactions"] for cust in json_in])
@time methodB = vcat(json_in[1]["transactions"],json_in[2]["transactions"],json_in[3]["transactions"])
但是,在我的计算机上,methodA的计时为~0.22秒,methodB的计时为~0.02秒。我打算执行这数千次,因此10倍更快的性能是一件大事

我看到methodB不是很健壮,因为它只能处理3个Dicts客户,所以即使它的性能很好,也不能推广


高效连接Dict数组中元素的数组的最有效方法是什么?

正如@Gnimuc在其评论中所述,您不应该在全局范围内进行基准测试,最好使用BenchmarkTools.jl进行基准测试-以下是正确完成的时间安排:

julia> methodA(json_in) = reduce(vcat,[cust["transactions"] for cust in json_in])
method1 (generic function with 1 method)

julia> methodB(json_in) = vcat(json_in[1]["transactions"],json_in[2]["transactions"],json_in[3]["transactions"])
method2 (generic function with 1 method)

#Gnimuc's syntax from his comment
julia> methodC(json_in) = mapreduce(x->x["transactions"], vcat, json_in)
method3 (generic function with 1 method)

julia> using BenchmarkTools

julia> @benchmark methodA(json_in)
BenchmarkTools.Trial:
  memory estimate:  38.15 MiB
  allocs estimate:  15
  --------------
  minimum time:     10.584 ms (3.10% GC)
  median time:      14.781 ms (32.02% GC)
  mean time:        15.112 ms (32.19% GC)
  maximum time:     69.341 ms (85.28% GC)
  --------------
  samples:          331
  evals/sample:     1

julia> @benchmark methodB(json_in)
BenchmarkTools.Trial:
  memory estimate:  22.89 MiB
  allocs estimate:  2
  --------------
  minimum time:     5.921 ms (5.92% GC)
  median time:      8.402 ms (32.48% GC)
  mean time:        8.701 ms (33.46% GC)
  maximum time:     69.268 ms (91.09% GC)
  --------------
  samples:          574
  evals/sample:     1

julia> @benchmark methodC(json_in)
BenchmarkTools.Trial:
  memory estimate:  38.15 MiB
  allocs estimate:  12
  --------------
  minimum time:     10.599 ms (3.37% GC)
  median time:      14.843 ms (32.12% GC)
  mean time:        15.228 ms (32.24% GC)
  maximum time:     71.954 ms (85.95% GC)
  --------------
  samples:          328
  evals/sample:     1
方法B的速度仍然是原来的两倍。这正是因为它更专业化,在一个只有三个元素的数组上

另一种可能在这里工作得很好的解决方案是使用Mappedaray,它在原始数组中创建一个惰性视图:

using MappedArrays
method4(json_in) = mappedarray(x->x["transactions"], json_in)
当然,这不会连接阵列,但可以使用CatView包连接视图:

using CatViews
julia> method5(json_in) = reduce(CatView, mappedarray(x->x["transactions"], json_in))
method5 (generic function with 1 method)

julia> @benchmark method5(json_in)
BenchmarkTools.Trial:
  memory estimate:  1.73 KiB
  allocs estimate:  46
  --------------
  minimum time:     23.320 μs (0.00% GC)
  median time:      23.916 μs (0.00% GC)
  mean time:        25.466 μs (0.00% GC)
  maximum time:     179.092 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     1

因为它不分配,所以它比方法B快300倍,但使用结果可能较慢,因为非局部性-值得进行基准测试。

正如@Gnimuc在其评论中所述,您不应该在全局范围内进行基准测试,最好使用BenchmarkTools.jl进行基准测试-以下是正确完成的时间安排:

julia> methodA(json_in) = reduce(vcat,[cust["transactions"] for cust in json_in])
method1 (generic function with 1 method)

julia> methodB(json_in) = vcat(json_in[1]["transactions"],json_in[2]["transactions"],json_in[3]["transactions"])
method2 (generic function with 1 method)

#Gnimuc's syntax from his comment
julia> methodC(json_in) = mapreduce(x->x["transactions"], vcat, json_in)
method3 (generic function with 1 method)

julia> using BenchmarkTools

julia> @benchmark methodA(json_in)
BenchmarkTools.Trial:
  memory estimate:  38.15 MiB
  allocs estimate:  15
  --------------
  minimum time:     10.584 ms (3.10% GC)
  median time:      14.781 ms (32.02% GC)
  mean time:        15.112 ms (32.19% GC)
  maximum time:     69.341 ms (85.28% GC)
  --------------
  samples:          331
  evals/sample:     1

julia> @benchmark methodB(json_in)
BenchmarkTools.Trial:
  memory estimate:  22.89 MiB
  allocs estimate:  2
  --------------
  minimum time:     5.921 ms (5.92% GC)
  median time:      8.402 ms (32.48% GC)
  mean time:        8.701 ms (33.46% GC)
  maximum time:     69.268 ms (91.09% GC)
  --------------
  samples:          574
  evals/sample:     1

julia> @benchmark methodC(json_in)
BenchmarkTools.Trial:
  memory estimate:  38.15 MiB
  allocs estimate:  12
  --------------
  minimum time:     10.599 ms (3.37% GC)
  median time:      14.843 ms (32.12% GC)
  mean time:        15.228 ms (32.24% GC)
  maximum time:     71.954 ms (85.95% GC)
  --------------
  samples:          328
  evals/sample:     1
方法B的速度仍然是原来的两倍。这正是因为它更专业化,在一个只有三个元素的数组上

另一种可能在这里工作得很好的解决方案是使用Mappedaray,它在原始数组中创建一个惰性视图:

using MappedArrays
method4(json_in) = mappedarray(x->x["transactions"], json_in)
当然,这不会连接阵列,但可以使用CatView包连接视图:

using CatViews
julia> method5(json_in) = reduce(CatView, mappedarray(x->x["transactions"], json_in))
method5 (generic function with 1 method)

julia> @benchmark method5(json_in)
BenchmarkTools.Trial:
  memory estimate:  1.73 KiB
  allocs estimate:  46
  --------------
  minimum time:     23.320 μs (0.00% GC)
  median time:      23.916 μs (0.00% GC)
  mean time:        25.466 μs (0.00% GC)
  maximum time:     179.092 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     1

因为它不分配,所以比方法B快300倍,但使用结果可能会慢一些,因为非局部性-值得进行基准测试。

感谢您的帮助,经过一些研究后,我想到了使用宏内联扩展代码的想法,请参阅下面的代码,它在Juliabox.com 2017年9月21日的基准测试中表现良好

macro inline_vcat(a)
  quote
    astr = $(string(a))
    s = reduce(string, string(astr,"[",aa,"][\"transactions\"],") for aa in 1:length($a))    
    string("vcat(", s[1:(end-1)],")")
  end
end

methodE(json_in) = (@inline_vcat json_in) |> parse |> eval

using BenchmarkTools
@benchmark methodE(json_in)

这种方法的一个缺点是,如果JSON中有大量~100万客户,那么生成的代码将很长,我认为解析它需要很长时间。因此,对于大型数据集来说,这可能不是一个好主意。

感谢您的帮助,经过一些研究,我提出了使用宏内联扩展代码的想法,请参见下面的代码,它在Juliabox.com 2017年9月21日的基准测试中表现得非常好

macro inline_vcat(a)
  quote
    astr = $(string(a))
    s = reduce(string, string(astr,"[",aa,"][\"transactions\"],") for aa in 1:length($a))    
    string("vcat(", s[1:(end-1)],")")
  end
end

methodE(json_in) = (@inline_vcat json_in) |> parse |> eval

using BenchmarkTools
@benchmark methodE(json_in)

这种方法的一个缺点是,如果JSON中有大量~100万客户,那么生成的代码将很长,我认为解析它需要很长时间。因此,对于大型数据集来说,这可能不是一个好主意。

提示:不要在全局范围内进行基准测试。您也可以在中使用mapreducex->x[transactions],vcat,json_,而不是reduce+comprehension。此外,我自己未回答的问题可能会重复,您可能会感兴趣。其中包含一些相关代码:@Gnimuc我认为mapreduce不会更快,因为我的reduce for组合就像mapreduce一样?是的,这里没有性能原因。只是因为它的语法比组合的reduce更简洁;提示:不要在全局范围内进行基准测试。您也可以在中使用mapreducex->x[transactions],vcat,json_,而不是reduce+comprehension。此外,我自己未回答的问题可能会重复,您可能会感兴趣。其中包含一些相关代码:@Gnimuc我认为mapreduce不会更快,因为我的reduce for组合就像mapreduce一样?是的,这里没有性能原因。只是因为它的语法比组合的reduce更简洁;