2017-09-19 2 views
1

J'ai une structure de données que j'ai chargé dans de JSON qui ressemble au-dessousjulia: des moyens efficaces pour des réseaux VCAT n

json_in = 
    [ Dict("customer" => "cust1", "transactions" => 1:10^6) 
    , Dict("customer" => "cust2", "transactions" => 1:10^6) 
    , Dict("customer" => "cust3", "transactions" => 1:10^6)] 

Je connais deux méthodes pour réduire la transactions dans un réseau

@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"]) 

Cependant, le timing de la méthode A est ~ 0.22s vs ~ 0.02s pour methodB sur mon ordinateur. J'ai l'intention d'effectuer cela des milliers de fois, donc une performance 10 fois plus rapide est une grosse affaire.

Je vois que la méthode B n'est pas très robuste car elle ne peut traiter que 3 Dicts (clients) donc même si elle est performante elle ne généralise pas.

Quel serait le moyen le plus efficace de concaténer efficacement des tableaux qui sont des éléments d'un tableau de Dict?

+1

Indice: ne pas référencer dans un contexte global. Vous pouvez également utiliser 'mapreduce (x-> x [" transactions "], vcat, json_in)' au lieu de 'reduce + comprehension'. – Gnimuc

+0

Copie possible de [Vitesse d'exécution de Julia] (https://stackoverflow.com/questions/44263741/julia-speed-of-execution) – Gnimuc

+1

Aussi, ma propre question (sans réponse) pourrait vous intéresser (contient un code pertinent): [Lecture tableau JSON en type Julia DataFrame] (https://stackoverflow.com/questions/46143997/reading-json-array-into-julia-dataframe-like-type) –

Répondre

4

Comme les États @Gnimuc dans son commentaire, vous ne devriez pas référence portée mondiale et points de référence sont mieux fait en utilisant BenchmarkTools.jl - ici sont les timings bien fait:

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 

Méthode B est toujours comme deux fois aussi vite. C'est exactement parce qu'il est plus spécialisé, sur un tableau avec exactement trois éléments.

Une solution alternative qui pourrait bien fonctionner ici est d'utiliser un MappedArray, ce qui crée une vue paresseuse dans le tableau original:

using MappedArrays 
method4(json_in) = mappedarray(x->x["transactions"], json_in) 

Bien sûr, cela ne concaténer pas les tableaux, mais vous pouvez concaténer vues en utilisant le package 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 

parce qu'il ne lui attribue pas est comme 300x plus rapide que la méthode B (mais il est possible, il est plus lent à utiliser le résultat en raison de la non-localité - d'une valeur d'étalonnage).

+0

demande si nous pouvons utiliser une méthode basée sur une macro pour développer le code – xiaodai

0

Merci pour l'aide, après quelques recherches je suis venu avec cette idée inline développer le code en utilisant des macros, voir le code ci-dessous, et il reste très bien sur les points de référence (sur Juliabox.com 21Sep2017)

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) 

enter image description here

une lacune de cette méthode est que s'il y a un grand (~ 1 million) clients dans le JSON alors le code généré sera long et l'analyse, il faudrait beaucoup de temps, je suppose bien. Par conséquent, ce n'est probablement pas une bonne idée pour les grands ensembles de données.