ios - 表示 - xcode viewcontroller title




メモリ書き込みパフォーマンス-GPU CPU共有メモリ (2)

memkiteが提供する共有GPU / CPUのドキュメントに従って、 posix_memalignを使って入力と出力の両方のMTLBufferを割り当てています。

それ以外: posix_memalign使っているだけで、最新のAPIを使うのは簡単です

let metalBuffer = self.metalDevice.newBufferWithLength(byteCount, options: .StorageModeShared)

私のカーネル関数は、およそ1,600万の複雑な値構造体を操作し、等しい数の複雑な値構造体をメモリに書き出します。

私はいくつかの実験を行い、メタルカーネルの「複雑な数学のセクション」は0.003秒(はい!)で実行されますが、結果をバッファに書き出すには0.05秒以上かかることがあります。 私の実験では、数学の部分をコメントアウトしてメモリにゼロを代入しました。割り当てをコメントにして、0.003秒後に計算を追加するのは0.05秒かかります。

この場合、共有メモリが遅くなっていますか?試してみることのできるヒントやヒントがありますか?

追加の詳細

テストプラットフォーム

  • iPhone 6S - フレームあたり〜0.039秒
  • iPad Air 2 - フレームあたり〜0.130秒

ストリーミングデータ

シェイダーへの各更新は、構造体内のfloat型のペアの形で約50000の複素数を受け取ります。

struct ComplexNumber {
    float real;
    float imaginary;
};

カーネル署名

kernel void processChannelData(const device Parameters *parameters [[ buffer(0) ]],
                               const device ComplexNumber *inputSampleData [[ buffer(1) ]],
                               const device ComplexNumber *partAs [[ buffer(2) ]],
                               const device float *partBs [[ buffer(3) ]],
                               const device int *lookups [[ buffer(4) ]],
                               device float *outputImageData [[ buffer(5) ]],
                               uint threadIdentifier [[ thread_position_in_grid ]]);

すべてのバッファには、現在動作している50000サンプルを受け取るinputSampleData以外の、現在は変更されていないデータが含まれています。 他のバッファには、それぞれ約1600万の値(128チャンネル×130000ピクセル)が含まれています。 私は各ピクセルでいくつかの演算を実行し、複雑な結果をチャンネル間で合計し、最後に複素数の絶対値をとり、結果のfloatoutputImageDataます。

ディスパッチ

commandEncoder.setComputePipelineState(pipelineState)

commandEncoder.setBuffer(parametersMetalBuffer, offset: 0, atIndex: 0)
commandEncoder.setBuffer(inputSampleDataMetalBuffer, offset: 0, atIndex: 1)
commandEncoder.setBuffer(partAsMetalBuffer, offset: 0, atIndex: 2)
commandEncoder.setBuffer(partBsMetalBuffer, offset: 0, atIndex: 3)
commandEncoder.setBuffer(lookupsMetalBuffer, offset: 0, atIndex: 4)
commandEncoder.setBuffer(outputImageDataMetalBuffer, offset: 0, atIndex: 5)

let threadExecutionWidth = pipelineState.threadExecutionWidth
let threadsPerThreadgroup = MTLSize(width: threadExecutionWidth, height: 1, depth: 1)
let threadGroups = MTLSize(width: self.numberOfPixels / threadsPerThreadgroup.width, height: 1, depth:1)

commandEncoder.dispatchThreadgroups(threadGroups, threadsPerThreadgroup: threadsPerThreadgroup)
commandEncoder.endEncoding()
metalCommandBuffer.commit()
metalCommandBuffer.waitUntilCompleted()

GitHubの例

私はSlowというサンプルを書いて、GitHubに載せました。 ボトルネックは、入力バッファに値を書き込むことであると考えられます。 だから、ボトルネックを避ける方法が問題になると思いますか?

メモリコピー

私はさまざまなバイトコピー方法のパフォーマンスを比較するための簡単なテストを書いた。

現在のステータス

私は実行時間を0.02秒に短縮しました。これは多くのようには聞こえませんが、1秒あたりのフレーム数に大きな違いがあります。 現在、最大の改善点は、 cblas_scopy()に切り替えた結果です。


型のサイズを小さくする

もともと、私は16ビットの符号付き整数をFloat(32ビット)として事前変換していました。 これは、データのサイズを半分にするために、値を16ビットで保存するようにパフォーマンスが始まるケースです。

Swift上のObjective-C

データの移動を扱うコードについては、SwiftよりObjective-C(Warren Moore勧告)を選択することができます。 これらの特別な状況でのSwiftのパフォーマンスはまだ傷ついていません。 memcpyまたは同様の方法を呼び出すこともできます。 私は、ループポインタバッファを使用した例をいくつか見てきましたが、私の実験ではこれをゆっくり実行しました。

テストの難しさ

私は実際にマシン上の遊び場でさまざまなコピー方法との関係でいくつかの実験をしたかったのですが、残念ながらこれは役に立たなかったのです。 同じ実験のiOSデバイスのバージョンは全く異なる方法で実行されました。 相対的なパフォーマンスは類似していると思うかもしれませんが、私はこれも無効な前提であることがわかりました。 あなたがiOSデバイスをインタプリタとして使った遊び場を持つことができれば、本当に便利でしょう。


データをハフマンコードにエンコードし、GPUでデコードすることで大きなスピードアップを得ることができます( MetalHuffman参照MetalHuffman 。 それはあなたのデータにもよりますが。







metal