我正在使用 Parallel Scala Collections 的聚合方法
def aggregate[S](z: S)(sop: (S, T) => S, cop: (S, S) => S): S
我理解為什么 cop 需要具有關聯性,而不是來自Learning Concurrent Programming in Scala的以下陳述句:
聚合操作要求 cop 是關聯的,即
z累加器的零元素,即cop(z, a) == a*
為什么z需要滿足cop(z,a) == a?
uj5u.com熱心網友回復:
這種方法是如何計算事物的?
- 對于每個元素,
T您都必須將其轉換為S元素 - 它可以由某個函式完成,S => T但作者選擇了一些更具彈性的方法 with(S, T) => S,這S可能是零,也可能是其他一些較早結束的并行計算的結果 - 啟用您提供的元素的并行組合
(S, S) => S- 這允許將任何兩個元素組合成一個
現在,并行集合有一些優化選項:
- 它可以計算
sop(element: T, z): S將每個轉換element為S,然后將每個結果與cop(result1, result2) cop(element, otherResult)如果某些操作otherResult準備就緒,它可以將這兩個操作壓縮為
這兩個操作必須在某種程度上可以互換以允許這種優化,但還有更多。
如果你想組合這樣的元素怎么辦?
s1 s2 s3 s4
\ / \ /
cop cop
\ /
\ /
\ /
cop
如果元素的數量是 2 的冪,它就可以作業。對于其他一些數字,它可以執行以下操作:
s1 s2 s3 z
\ / \ /
cop cop
\ /
\ /
\ /
cop
使任務分配更容易。
一般來說,通過假設它S是一個具有關聯性、中性的幺半群cop,z并sop提供類似于 from TtoS和S S加法的同構,操作可以很容易地在內部優化東西,同時使正確性很容易證明。您可以輕松更改實作以進一步優化它,或者使代碼更簡單。cop具有z作為中性元素/零/身份只是此要求的一部分。
如果您放棄這些要求……那么您的實作很容易(總是?)被破壞。您的結果可能會根據執行順序而改變,并且并行計算無法保證任何操作的執行順序:元素T將被轉換為S哪種順序以及Ses 將按照哪種順序進行組合。
你可以不依賴于z保持中立......但通過使S一個半群你:
- 將無法
S始終回傳-您將無法S為空集合提供中立,因此充其量您將能夠提供Option[S] - 將無法以這種方式減少 1 元素集合,因為您最初沒有什么可播種的
sop,您是否必須使用T => S代替(T, S) => S(這可能會減少演算法可用的一些優化) - 可能會有更復雜的實作,因為你必須重新排列合并的東西,這樣你就永遠不需要類似
cop(s, zero)或sop(t, zero)可能簡化內部代碼的東西
uj5u.com熱心網友回復:
讓我們從分析簽名aggregate及其作業原理開始:
def aggregate[S](z: S)(sop: (S, T) => S, cop: (S, S) => S): S
所以這表示它將并行地將集合的元素聚合為一個新的單個元素。為此,它將(語意上)將集合“拆分”為多個塊以并行處理,對于每個塊,它將運行sop (從 開始z)以將它們組合成單個S. 然后,它將組合每個塊的中間結果使用,cop直到所有元素都成功聚合為一個值。
現在,為什么z需要是“零”元素?保證結果的一致性!
這個想法是,無論您創建多少塊,結果都必須與不涉及并行性一樣。
讓我們看一個簡單的例子,我們想對所有數字求和:
someBigParallelArray.aggregate(0)(_ _, _ _)
因此,如果不是0我們會使用,5那么結果將根據創建的塊數而改變;因為每個塊都會5在最終總和上增加一個。
現在,您可能會問為什么我們甚至需要零元素?我們可能只是假設塊不為空(就像我們對cop)。
并且您或多或少是正確的(因為整個集合為空或太小的特殊情況可以不同地處理)。但是,這會aggregate非常有限,因為它只能生成與原始集合相同型別的值;并且您可以看到簽名比這更強大,它允許您生成任何型別的值,只要該型別具有有效的“零”并且您知道如何將當前型別的值添加到目標型別。
這就是允許我們寫這樣的東西的原因:
// Collects all the different values in a Set
val distinct = someBigParallelArray.aggregate(Set.empty[Int])(_ _, _ | _)
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/368227.html
