2014-01-30 5 views
5
(doseq [e coll1] 
    (myfunc e)) 

ist sehr schnell, wenn alles, was Sie interessieren, Nebenwirkungen sind. Was, wenn ich myfunc Elemente aus mehreren Sammlungen "parallel" nehmen möchte, d. H. myfunc auf die ersten Elemente jeder Sammlung anwenden, dann auf alle zweiten Elemente, dann auf alle dritten Elemente usw.? Beachten Sie, dass dies genauso eine Frage über die Funktionalität von for als doseq ist, aber wenn man eine Sequenz als Ausgabe wünscht, wird map tun, was benötigt wird, also for ist nicht notwendig.Effizienter Weg, Sammlungen parallel mit doseq (oder für) zu gehen?

(doseq [e1 coll1 
     e2 coll2] 
    (myfunc e1 e2)) 

stattdessen myfunc auf alle möglichen Kombinationen von Elementen aus den beiden Sammlungen gelten. Wenn ich im Voraus weiß, was die Elemente der Sammlung sein werden, könnte ich einen :when Test verwenden, um nur bestimmte Elemente zu kombinieren, aber angenommen, dass ich das nicht weiß?

Eine Lösung ist ntuples zu erstellen, um das cartesianischen Produkt zu vermeiden, aber das ist zeitraubend, der den Geschwindigkeitsvorteil Entfernen doseq in erster Linie mit:

(let [argvecs (map vector coll1 coll2)] ; seq of ntuples of interleaved vals 
    (doseq [args argvecs] 
    (apply myfunc args)))) 

(Hier geht es um 8X als ein langsamer sein kann . Single-Sammlung doseq Siehe Zeiten für domap1 und domap17 am Ende this question)

Antwort

4

Wenn Sie den Aufwand für das Erstellen von Tupeln mit Karte vermeiden wollen, alles, was Sie tun können, ist es selbst zu schreiben, als eine Schleife/Wiederholung, die jede Sammlung manuell durchläuft. Aber wirklich, Sie werden immer noch am Ende müssen ein Tupel erstellen, so dass Sie (apply f args), wo args das n-te Element jeder Sammlung ist. Sie sparen ein paar Cons-Zellen, indem Sie keine Liste solcher Tupel erstellen, aber das ist alles. Ein Großteil der Kosten von Variadic-Funktionen wie diesem ruft apply auf und baut die Listen dazu auf. Sie können das vermeiden, indem Sie eine 2-artige Version Ihres doseq-Geschwisters schreiben, und eine 3-arity und ... Aber die n-arity-Version wird immer langsamer sein.

+0

Nützliche Punkte über 'apply' - Danke. In Experimenten, die ich mit "recur" gemacht habe (siehe Link in der Frage), ist es langsamer als "doseq" für eine einzelne Sammlung, also würde ich nicht erwarten, dass es besser ist als eine Paralleliteration 'doseq'. – Mars

+1

In der anderen Frage, die ich von diesem Split abbrachte, bewertete ich einen Zielaritäts- gegen Varargs-Vergleich mit Loop/Recurve und es gab einen Leistungsunterschied von ~ 150%. Der Leistungsunterschied zwischen Loop-/Recur- und Dorun-Maps in großen Sammlungen war extrem. – noisesmith

+0

Eines Tages könnte ich die 'doseq' Quelle kopieren, Code für komplexe' für' Optionen wie ': while' ausstreichen und es parallel durchlaufen lassen, ohne ein kartesisches Produkt zu erstellen. Eines Tages. Könnte sein. Sieht mir zur Zeit ziemlich haarig aus. Ich werde nie so viel über Clojure Eingeweide erfahren. – Mars

0

Wenn ich gut verstehen, das ist, was map tut.

(map + [1 2] [3 4]) 
; => (4 6) 

Wenn Sie möchten, dass nur die Effekte, die Sie dorun in der resultierenden Karte verwenden können:

(dorun (map (comp println +) [1 2] [3 4])) 
; => nil 
+0

Ah ja, das hätte ich erwähnen sollen. Natürlich macht das das Gleiche, aber es ist mehr als 4 Mal langsamer als "doseq" für eine einzelne Sammlung, also scheint es nicht die effizienteste Lösung für mehrere Sammlungen zu sein. Es stellt sich heraus, dass 'mapv' ziemlich schnell ist, als noisemiths Antwort [hier] (http://stackoverflow.com/questions/21449549/how-to-write-variadic-clojure-macro-that-takes-collections-as- Argumente/21465689? Noredirect = 1 # 21465689) zeigt, aber immer noch nicht so schnell wie 'doseq'. – Mars

+0

Mmm ... Wenn man sich die (wundervolle) "doseq" -Implementierung anschaut, scheint sie für verschiedene Arten von Sammlungen optimiert zu sein. Können Sie Ihre Tests mit nicht indizierten Sammlungen (wie Listen) wiederholen und sehen, ob dies der Fall ist? Wenn ich mir den 'doseq'-Code anschaue, scheint mir, dass die' dorun'-Alternative genauso schnell arbeiten sollte wie die doseq, wenn die Sammlung bei O (1) nicht indexierbar ist. –

+0

Gute Idee. Ich hatte einen PersistentVector übergeben. Probieren Sie es einfach mit einem LazySeq und dann mit einer PersistentList aus: Jedes Mal war 'doseq' in meinen Tests immer noch viel schneller als' map' mit 'dorun' (Länge der Sammlung: 1000, side-function-Funktion setzt ein Element in einem Kern. Matrixvektorvektor, wobei das Element durch die Ganzzahl aus der Sammlung ausgewählt wird. (In den Tests von A. Webb, mit längeren Sammlungen und einem langsameren Nebeneffekt, nehme ich an, es gibt viel weniger Unterschied.) – Mars

1

Verwenden Sie (dorun (map f coll1 coll2 ..)) oder (dorun (map apply f colls)).

Je mehr Sie fragen f, desto länger wird es dauern.

Verwenden Sie in einer einzigen Sammlung doseq. Es gibt vermeidbaren Aufwand von der Lazy-Seq-Struktur.

(bench (doseq [e (range N)] (f e))) 
Execution time mean : 4.959713 ms 

(bench (dorun (map f (range N)))) 
Execution time mean : 5.669721 ms 

Auf zwei Sammlungen, beachten Sie f hat zweimal statt einmal hinzuzufügen, also würde ich doppelt so lange erwarten diese zu nehmen. Hinweis jetzt beide Versionen haben einige strukturelle Overhead.

(bench (let [argvecs (map vector (range N) (range N))] 
    (doseq [e argvecs] (apply f e)))) 
Execution time mean : 11.876843 ms 

(bench (dorun (map f (range N) (range N)))) 
Execution time mean : 11.145435 ms 
+0

Danke A. Webb. Ich kann sehen, dass mit Ihrer 'f',' doseq' und 'dorun map' viel näher sind als die Zeiten, die ich mit meiner Funktion bekommen habe, meine andere letzte Frage, in der Sie auch hilfreiche Kommentare gegeben haben. – Mars

3

Wenn es schneller Sie sind, nachdem Sie auf reflection-warnings und vielleicht Besuche die loop-primitive (mit von wiederkehrenden (Rest coll1) (Rest coll2)) ...

Kasse auch Clojure is still schnell und drehen sollten Leistungstest Rahmen Criterium um sicherzustellen, dass Sie das Richtige messen.

+0

Danke claj. Ich werde diese Ressourcen prüfen. Ja gut; Ich mache immer Geschwindigkeitstests mit Criterium. Verlasse das Haus nicht ohne es. – Mars

+0

claj, gibt es einen Tippfehler in den Links in Ihrer Antwort? 'loop-primitive' verweist auf dieselbe Seite wie 'reflection-warnings'. – Mars

+0

Yup, behoben! Vielen Dank. – claj

Verwandte Themen