2016-08-03 10 views
1

Lassen sich die folgende Codezeile mehrmals ausgeführt:Verhalten von scala fach parallel Sammlungen

Set(1,2,3,4,5,6,7).par.fold(0)(_ - _) 

Die Ergebnisse sind sehr interessant:

scala> Set(1,2,3,4,5,6,7).par.fold(0)(_ - _) 
res10: Int = 8 
scala> Set(1,2,3,4,5,6,7).par.fold(0)(_ - _) 
res11: Int = 20 

jedoch klar soll es wie in seiner sequentiellen Version sein :

scala> Set(1,2,3,4,5,6,7).fold(0)(_ - _) 
res15: Int = -28 

ich verstehe, dass der Betrieb - ist nicht asso catiative auf ganzen Zahlen und das ist der Grund für ein solches Verhalten, aber meine Frage ist ziemlich einfach: bedeutet es nicht, dass fold sollte nicht in .par Implementierung von Sammlungen parallelisiert werden?

Antwort

4

Wenn Sie am standard library documentation betrachten, sehen Sie, dass fold ist undeterministic hier:

die Elemente dieser Sequenz faltet den angegebenen assoziativen binären Operator. Die Reihenfolge, in der Vorgänge für Elemente ausgeführt werden, ist nicht angegeben und möglicherweise nicht deterministisch.

Als Alternative gibt es foldLeft:

Wendet einen binären Operator auf einen Startwert und alle Elemente dieser Sequenz, nach rechts nach links gehen. Wendet einen binären Operator auf einen Startwert und alle Elemente dieser Auflistung oder des Iterators an, von links nach rechts.

Hinweis: kann für verschiedene Läufe unterschiedliche Ergebnisse liefern, es sei denn, der zugrundeliegende Sammlertyp ist geordnet oder der Operator ist assoziativ und kommutativ.

Als Set ist keine Sammlung bestellt, gibt es keine kanonische Reihenfolge, in der die Elemente gefaltet werden können, so dass die Standard-Bibliothek ermöglicht es selbst für foldLeft undeterministic selbst zu sein. Wenn Sie hier eine geordnete Sequenz verwenden würden, wäre in diesem Fall foldLeft deterministisch.

+0

Vielen Dank für Ihre Antwort. Verlängert das Lesen von Dokumenten wie üblich und handelte nach Intuition :) – tkachuko

+1

Das Scaladoc ist eine Lüge. 'SeqLike # fold' tatsächlich nutzt' foldLeft': https://github.com/scala/scala/blob/1706a37eb84ec252aea77bccebad3e48448534ad/src/library/scala/collection/TraversableOnce.scala#L212 –

+0

Aber es bedeutet nicht, es ist die gleiche für Par. Sie werden tatsächlich unabhängig voneinander in parallelen Sammlungen implementiert. – tkachuko

4

Die scaladoctut sagen:

Die Reihenfolge, in der die Elemente reduziert werden nicht spezifiziert ist und nicht deterministisch sein kann.

So, wie Sie erwähnt, eine binäre Operation in ParSet#fold angelegt, die nicht assoziativ ist, kein deterministisches Ergebnis produzieren garantiert. Der obige Text ist Warnung ist alles was du bekommst.

Bedeutet das, ParSet#fold (und Cousins) sollten nicht parallelisiert werden? Nicht genau. Wenn Ihre binäre Operation kommutativ ist und Sie nicht auf Nicht-Determinismus von Nebenwirkungen achten (nicht dass ein foldsollte irgendwelche haben), dann gibt es kein Problem. Sie werden jedoch mit dem Vorbehalt konfrontiert, dass Sie vorsichtig um parallele Sammlungen gehen müssen.

Ob es ist richtig ist eher eine Frage der Meinung. Man könnte argumentieren, dass eine Methode, wenn sie zu einem zufälligen Nicht-Determinismus führen kann, nicht in einer Sprache oder Bibliothek existieren sollte. Die Alternative besteht jedoch darin, die Funktionalität zu entfernen, sodass ParSet die Funktionalität fehlt, die in den meisten anderen Auflistungsimplementierungen vorhanden ist. Sie könnte die gleiche Linie des Denkens verwenden, um auch die Entfernung von Stream#foreach vorschlagen zu verhindern, dass Menschen versehentlich endlose Schleifen auf unendliche Ströme auslösen, aber sollten Sie?

+1

So sprechen wir über das Gleichgewicht zwischen dem Lesen der Dokumentation und dem Determinismus. Nun, immer noch strittig, aber Beispiel mit dem Stream ist nett. Danke für die Antwort. – tkachuko