2017-09-02 8 views
0

私は何か不思議なことを見つけました:何らかの理由で、このバージョンの配列バージョンは、次のコードが実行された後、ほとんど常にランダムな0を含みます。スウィフトパラレル化:GCDのUnsafeMutablePointerとの比較

var a = UnsafeMutablePointer<Int>.allocate(capacity: N) 
//var a = [Int](repeating: 0, count: N) 

let n = N/iterations 

DispatchQueue.concurrentPerform(iterations: iterations) { j in 
    for i in max(j * n, 1)..<((j + 1) * n) { 
     a[i] = 1 
    } 
} 

for i in max(1, N - (N % n))..<N { 
    a[i] = 1 
} 

これには特別な理由がありますか?スウィフト配列はメモリ内で連続しているわけではないかも知れませんが、単一のスレッドから各インデックスに対してメモリ位置にアクセスするのはあまり面白くありません。

答えて

0

アレイはスレッドセーフではなく、Objective-Cオブジェクトにブリッジされていますが、COW(コピーオンライト)ロジックで値型として動作します。配列上のCOWは、要素が変更され、参照カウンタが1より大きい場合に配列全体のコピーを作成します(概念的には、実際の実装は少し微妙です)。

あなたのスレッドを変更すると、メインスレッドが参照および要素を参照するたびにメモリコピーがトリガーされます。メインスレッドはまた、COWも発生させるように変更します。最終的にはどちらのスレッドでも最後に修正されたコピーの状態です。これは無作為にlimboの変更のいくつかを残し、「欠落した」アイテムを説明します。

これを避けるには、特定のスレッドですべての変更を実行し、sync()を使用してアレイ上のCOWがスレッドによってのみ実行されるようにする必要があります(実際にメモリコピー数が減り、非常に大きな配列の場合)。このアプローチを使用すると、オーバーヘッドと潜在的な競合が発生します。スレッドの安全性のために支払う代金です。

これにアプローチするもう1つの方法は、オブジェクトの配列(参照型)を使用することです。これにより、配列は、参照されるオブジェクトのデータを変更することによって実際には変更されないポインタの単純なリストになります。実際のプログラムでは、各オブジェクトインスタンス内のスレッドの安全性を気にする必要がありますが、値型の配列を使用する場合よりも干渉(およびオーバーヘッド)が大幅に少なくなります。

+0

オブジェクト参照の配列はまだ値ですが、それはありませんか? – Raphael

+0

私もこれについて考えました。しかし、ゼロは「ランダム」であってはなりません。さて、OPは例を示していません...おそらく彼らはパターンを見つけていないかもしれません。 – Raphael

+0

はい、オブジェクトの配列には「値」が含まれていますが、これらは「参照された型」の値です。彼らは実際のコンテンツへのポインタのようなものです。 "指し示された"値(参照された値)を変更する場合は、配列内のポインタを更新する必要はありません。値型の場合、COWも内部間接を実行しますが、言語はそのメモリ操作を完全に隠します。これは混乱するかもしれませんが、値の型は二重間接指定、参照型は三重間接指定になります。比較の安全ではないポインタは単一の間接指定です。 –

関連する問題