Goでの画像処理のパフォーマンスを改善した話 CPUキャッシュ、スライス
ウォーターマークをスクラッチでGoで実装した過程で、あまたの改善を行いましたが、その一つを紹介。
「画像のブロック処理のために、先んじて1次元スライスを並べ替える。」です。 メモリ効率は改善しましたが、速度については残念ながら期待する結果が得られませんでした。
Before After スライスを左上から行優先 スライスを左上からブロック/行優先 「ブロック」とは画像の分割したまとまりを指します。 例えば「4ピクセル四方のブロックに分割する」としたとき、80ピクセル四方の画像は、横に20個、縦に20個の計400個のブロックに分割できます。
ウォーターマークのアルゴリズムでは、画像の各ブロックに対してやや重たい計算します。 この時、データの順番を予め並び替えることで、CPUキャッシュを活用し、かつスライスのコピーをなくすことで、速度を改善を試みました。
実装詳細 もとのスライスから取得するためにコピーを複数回、かつ計算結果を戻すのに同じだけコピーを行います。 このコピーをしないように、取りやすい形にスライスを並べ替えておくのが今回の改善です。
src := []int{.....} // block 0番目を取りたい! // Before // src の 10 ~ 13, 24 ~ 27, xxx, xxx の4箇所からデータを取らないと行けない! copy(dist[0], src[0]) copy(dist[1], src[1]) copy(dist[2], src[2]) copy(dist[3], src[3]) // After // 先にシャッフル // 一度だけ参照すればOK! return src[0:8:8] 詳細は以下。
// Before wavelets := dwt.HaarDWT(img, srcWidth, nil) src := wavelets[0] // cA blockAt := 0 for startY := 0; startY < waveletHeight; startY += waveletBlockHeight { for startX := 0; startX < waveletWidth; startX += waveletBlockWidth { // ここでsrcからコピー b := get(src, startX, startY) // 計算: DCT -> SVD -> 計算 -> 逆変換で戻す v, idct := dct.
[続きを読む]