読者です 読者をやめる 読者になる 読者になる

S.F. Page

Programming,Music,etc...

音声ファイルから動画ファイルを生成する(50) – X64 & IA-32

メモリ間コピーをXbyakを使ってAVXで書き直してみた。結果から言うとパフォーマンス的にはあまり変わらなかった。もともとSIMDに適した部分ではないのでしょうがないが、勉強にはなった。

// Xbyakでのメモリコピーコード
video_writer::copy_image::copy_image(uint32_t width, uint32_t height, uint32_t pitch)
{

    uint32_t w_byte = width * 4 / 16;
    uint32_t index = 0;

    std::function<void()> funcs[3] =
    {
        [this] { mov16(); },
        [this] { mov32(); },
        [this] { mov64(); }
    };

    for (; index < 2; ++index){
        if ((w_byte & 1) == 1)
        {
            break;
        }
        else {
            w_byte = w_byte / 2;
        }
    }

    mov(r8, w_byte);
    mov(r9, height);
    mov(r10, pitch * 2);
    L("L2");
    mov(rax, r8);
    L("L1");
    funcs[index]();
    sub(rax, 1);
    jnz("L1");
    sub(rcx, r10);
    sub(r9, 1);
    jnz("L2");
    vzeroupper();
    ret();

}

void video_writer::copy_image::mov16()
{
    vmovdqa(xmm0, ptr[rcx]);
    vmovdqa(ptr[rdx], xmm0);
    add(rcx, 16);
    add(rdx, 16);
}

void video_writer::copy_image::mov32()
{
    vmovdqa(xmm0, ptr[rcx]);
    vmovdqa(xmm1, ptr[rcx + 16]);
    vmovdqa(ptr[rdx], xmm0);
    vmovdqa(ptr[rdx + 16], xmm1);

    add(rcx, 32);
    add(rdx, 32);
}

void video_writer::copy_image::mov64()
{
    vmovdqa(xmm0, ptr[rcx]);
    vmovdqa(xmm1, ptr[rcx + 16]);
    vmovdqa(ptr[rdx], xmm0);
    vmovdqa(xmm2, ptr[rcx + 32]);
    vmovdqa(ptr[rdx + 16], xmm1);
    vmovdqa(xmm3, ptr[rcx + 48]);
    vmovdqa(ptr[rdx + 32], xmm2);
    add(rcx, 64);
    vmovdqa(ptr[rdx + 48], xmm3);
    add(rdx, 64);

}


(中略)

// video_writerコンストラクタ内のコード
// video_writer::copy_imageの生成とアセンブラコードの取得

sf::map<> map(context,texture, 0, D3D11_MAP_READ, 0);
copy_image_.reset(new video_writer::copy_image(width_, height_, map.row_pitch()));
copy_func_ = (copy_func_t)copy_image_->getCode();

//

(中略)


#define SF_AVX 
// テクスチャをメディアバッファに書き込む
void video_writer::set_texture_to_sample()
{

    // タイムスタンプの設定
    CHK(sample_->SetSampleTime(video_sample_time_));

    // 書き込み先バッファのロック
    sf::media_buffer_lock<> buffer(buffer_);

    // 読み込みテクスチャをマップ
    sf::map<> map(context_, texture_, 0, D3D11_MAP_READ, 0);

    //CHK(MFCopyImage(pbBuffer, width_ * 4, reinterpret_cast<BYTE*>(mapped.pData), mapped.RowPitch, width_ * 4, height_));
#ifndef SF_AVX
    // Xbyak で書く前のコード
    DWORD *dest = (DWORD*) buffer.buffer();
    const UINT pitch = map.row_pitch() / sizeof(uint32_t);
    DWORD *src = (DWORD*) map.data() + (height_ - 1) * pitch;

    for (UINT i = 0; i < height_; ++i){
        for (UINT j = 0; j < width_; ++j){
            *dest++ = *src++;
        }
        src -= pitch * 2;
    }
#else
    // Xbyakで書き換えた後のコード
    void * src = (uint8_t*)map.data() + (height_ - 1) * map.row_pitch();
    (copy_func_)(src,buffer.buffer());

#endif
    video_sample_time_ += hnsSampleDuration;
}

上記はコードの抜粋である。メモリコピー部分をXbyakで生成した関数呼び出しに置き換えている。Xbyak内ではメモリコピーのループ回数を減らすためにXMMレジスタを使って最大1回のループあたり64バイト単位のコピーとなるようにしている。

ソースコード全体

実行した結果はわずかにAVX版のほうが速いかな?といったレベルである。


【AVX】

Encoding Time: 20.8708 sec
Encoding Time: 21.0718 sec
Encoding Time: 20.9125 sec
Encoding Time: 21.1056 sec
Encoding Time: 20.9764 sec

【単なるメモリコピー】

Encoding Time: 21.1463 sec
Encoding Time: 21.012 sec
Encoding Time: 21.2331 sec
Encoding Time: 21.5062 sec
Encoding Time: 20.9966 sec

FFTとかレンダリング、MP4ファイルへの書き込みのほうが時間がかかるのだからメモリコピーを最適化したことによる効果は少ないと思っていたけども、もうちょっと速くなってくれると嬉しかったのだが。

だいたいAVXについては試行錯誤できるくらいの知識はついたように思うので、実際処理時間がかかっていてかつ効果のありそうなところをXbyakを使って書き直してみようかなと思っている。