0. はじめに
前回の記事で WebP ユーティリティの cwebp.exe を試行したが,公式サイトで提供されているバイナリは 64bit 版のみであり,32bit 版はない。32bit 版が必要であれば自分でソースコードを入手してビルドする必要があるのだ。なぜ 32bit 版に拘るかというと筆者の経験では 32bit 版のほうが速い場合もあるからだ。ちなみに下記の記事は 32bit 版のほうが速いという稀有なケースであった。
今さら森博嗣「笑わない数学者」のビリヤードの問題を解く(2)~計算機を使う - Qiita
ということでソースコードを入手して自分で 32bit 版をビルドすることにする。ついでに近年評判の高い Clang でもビルドを試みることにした。
1. WebPユーティリティのダウンロード
Windows 用のバイナリはWebP のダウンロードリポジトリからダウンロードできる。この記事を書いている現時点では libwebp-1.4.0-windows-x64.zip が最新版である。残念ながら 64bit 版しか提供されていないので,32bit 版が必要な人は自分でソースコードを入手してビルドする必要がある。
2. ソースコードの入手
まずソースコードをダウンロードする。
git clone https://chromium.googlesource.com/webm/libwebp
3. Visual Studio のインストール
マイクロソフトの公式サイトより,Visual Studio 2022 Community Edition をインストールする。評判の高い Clang コンパイラも併せてインストールする。なお,Clang は LLVM の公式サイトからダウンロードする手もあるが,Visual Studio との相性を鑑みて Visual Studio Installer からインストールした。
4. コマンドラインビルド
Visual Studio をインストールしていればスタートメニューに下記のショートカットが作られているので,用途に応じて選択する。ホスト環境とターゲット環境を混同し易いので,特に必要でない限りは Cross Tools ではなく Native Tools を使ったほうが良いだろう。
ショートカット名 | ホスト | ターゲット |
---|---|---|
x64 Native Tools Command Prompt for VS 2022 | x64 (64bit) | |
x64_x86 Cross Tools Command Prompt for VS 2022 | x64 (64bit) | x86 (32bit) |
x86 Native Tools Command Prompt for VS 2022 | x86 (32bit) | |
x86_x64 Cross Tools Command Prompt for VS 2022 | x86 (32bit) | x64 (64bit) |
Visual C++ 用のメイクファイルが提供されているのでコマンドプロンプトから nmake.exe
を用いてビルドする。google 謹製のツールの良いところはビルドが簡単かつスムーズに通ることである。ターゲット(32bit/64bit)によらず,全く同じようにビルドできるのが凄いところだ。
cd libwebp
nmake /f Makefile.vc CFG=release-static RTLIBCFG=static OBJDIR=output
5. メイクファイルを改造する。
この後いろいろカスタマイズすることを考えてメイクファイルを改造する。
google 謹製のメイクファイルは大仰で内容を把握するのが大変である。このためスタティックリンクかつリリースビルド専用として作り直すことにした。目的はメイクファイルの見通しを良くすることによって,さらなる最適化の余地を図ること,そして評判の高い Clang コンパイラの適用である。なお,デフォルトでは Clang には対応していない様子。CMake を使えば良いのかもしれないが。
コンパイラのオプションが多少異なるだけで大半が共通のため,共通部分を抜き出して別ファイル Makefile.inc
とした。
Visual C++ 用メイクファイル
google 謹製のメイクファイルに対してバッファーのセキュリティチェックの省略オプション /GS-
およびプログラム全体の最適化オプション /GL
を追加した。併せてリンカオプションにも /LTCG
を追加した。また,対応プロセッサがインテル第4世代プロセッサ haswell 以降に限られてしまうが,オプション /arch:AVX2
を追加した。
#-------------------------------------------------------------------------------
# Makefile for Microsoft Visual C++, release build, static link
#-------------------------------------------------------------------------------
CC = cl.exe
LINKLIB = lib.exe
LINKEXE = link.exe
CCFLAGS = /nologo /c /O2 /DNDEBUG /MT /I. /Isrc /W3 /EHsc
CCFLAGS = $(CCFLAGS) /DWIN32 /D_CRT_SECURE_NO_WARNINGS /DWIN32_LEAN_AND_MEAN
CCFLAGS = $(CCFLAGS) /DHAVE_WINCODEC_H /DWEBP_USE_THREAD
CCFLAGS = $(CCFLAGS) /arch:AVX2 /GS- /GL
LLFLAGS = /nologo /LTCG
LDFLAGS = /nologo /LARGEADDRESSAWARE /MANIFEST:EMBED /NXCOMPAT /DYNAMICBASE
LDFLAGS = $(LDFLAGS) /LTCG
!IF "$(VSCMD_ARG_TGT_ARCH)" == "x86"
LDFLAGS = $(LDFLAGS) /SAFESEH
!ENDIF
WINLIBS = ole32.lib windowscodecs.lib shlwapi.lib
!IFDEF VSCMD_ARG_TGT_ARCH
OUT_DIR = output\release-static\$(VSCMD_ARG_TGT_ARCH)vc
!ELSE
!ERROR 環境変数 VSCMD_ARG_TGT_ARCH が未定義です。
!ENDIF
#-------------------------------------------------------------------------------
!INCLUDE Makefile.inc
Clang 用メイクファイル
プログラム全体の最適化オプション /GL
を外して,代わりにリンク時最適化オプション -flto
を付けた。同様にリンカオプションの /LTCG
も外した。ちなみに Clang の場合,リンク時最適化オプション -flto
を用いなければ Visual C++ の lib.exe
および link.exe
を使用できる。しかし,オプション -flto
を用いると専用の llvm-lib.exe
および lld-link.exe
を使用する必要がある。
#-------------------------------------------------------------------------------
# Makefile for LLVM Clang, release build, static link
#-------------------------------------------------------------------------------
CC = clang-cl.exe
LINKLIB = llvm-lib.exe
LINKEXE = lld-link.exe
CCFLAGS = /nologo /c /O2 /DNDEBUG /MT /I. /Isrc /W3
CCFLAGS = $(CCFLAGS) /DWIN32 /D_CRT_SECURE_NO_WARNINGS /DWIN32_LEAN_AND_MEAN
CCFLAGS = $(CCFLAGS) /DHAVE_WINCODEC_H /DWEBP_USE_THREAD
CCFLAGS = $(CCFLAGS) /GS- /arch:AVX2 -flto
LLFLAGS = /nologo
LDFLAGS = /nologo /LARGEADDRESSAWARE /MANIFEST:EMBED /NXCOMPAT /DYNAMICBASE
!IF "$(VSCMD_ARG_TGT_ARCH)" == "x86"
LDFLAGS = $(LDFLAGS) /SAFESEH
!ENDIF
WINLIBS = ole32.lib windowscodecs.lib shlwapi.lib
!IFDEF VSCMD_ARG_TGT_ARCH
OUT_DIR = output\release-static\$(VSCMD_ARG_TGT_ARCH)clang
!ELSE
!ERROR 環境変数 VSCMD_ARG_TGT_ARCH が未定義です。
!ENDIF
#-------------------------------------------------------------------------------
!INCLUDE Makefile.inc
コンパイラオプションの違い
オプションの違いをまとめると以下のようになる。※赤字は32bit版のみ有効
項目 | 変数 | Visual C++ | Clang |
---|---|---|---|
コンパイラ | CC | cl.exe | clang-cl.exe |
コンパイラ オプション |
CCFLAGS | /GL | -flto |
ライブラリアン | LINKLIB | lib.exe | llvm-lib.exe |
ライブラリアン オプション |
LLFLAGS | /LTCG | |
リンカ | LINKEXE | link.exe | lld-link.exe |
リンカ オプション |
LDFLAGS | /LTCG /SAFESEH | /SAFESEH |
メイクファイル共通部
長いので興味のある方は以下参照されたい。
Makefile.inc(共通部)はコチラ
#-------------------------------------------------------------------------------
# このファイルは下記のメイクファイルでインクルードして使用します。
# Makefile.vc2 ... x86/x64, cl.exe
# Makefile.clang ... x86/x64, clang-cl.exe
#-------------------------------------------------------------------------------
# 出力ディレクトリ
#-------------------------------------------------------------------------------
OUTPUT_DIRS = \
$(OUT_DIR)\lib \
$(OUT_DIR)\bin \
$(OUT_DIR)\obj\dec \
$(OUT_DIR)\obj\demux \
$(OUT_DIR)\obj\dsp \
$(OUT_DIR)\obj\enc \
$(OUT_DIR)\obj\examples \
$(OUT_DIR)\obj\extras \
$(OUT_DIR)\obj\imageio \
$(OUT_DIR)\obj\mux \
$(OUT_DIR)\obj\sharpyuv \
$(OUT_DIR)\obj\utils
#-------------------------------------------------------------------------------
# ターゲットの定義
#-------------------------------------------------------------------------------
all: \
$(OUTPUT_DIRS) \
$(OUT_DIR)\lib\libwebpdecoder.lib \
$(OUT_DIR)\lib\libsharpyuv.lib \
$(OUT_DIR)\lib\libwebp.lib \
$(OUT_DIR)\lib\libwebpdemux.lib \
$(OUT_DIR)\lib\libwebpmux.lib \
$(OUT_DIR)\bin\cwebp.exe \
$(OUT_DIR)\bin\dwebp.exe \
$(OUT_DIR)\bin\vwebp.exe \
$(OUT_DIR)\bin\webpmux.exe \
$(OUT_DIR)\bin\img2webp.exe \
$(OUT_DIR)\bin\get_disto.exe \
$(OUT_DIR)\bin\webp_quality.exe \
$(OUT_DIR)\bin\vwebp_sdl.exe \
$(OUT_DIR)\bin\webpinfo.exe \
$(OUT_DIR)\bin\gif2webp.exe \
$(OUT_DIR)\bin\anim_diff.exe \
$(OUT_DIR)\bin\anim_dump.exe
#-------------------------------------------------------------------------------
# オブジェクトの定義
#-------------------------------------------------------------------------------
SHARPYUV_OBJS = \
$(OUT_DIR)\obj\sharpyuv\sharpyuv.obj \
$(OUT_DIR)\obj\sharpyuv\sharpyuv_cpu.obj \
$(OUT_DIR)\obj\sharpyuv\sharpyuv_csp.obj \
$(OUT_DIR)\obj\sharpyuv\sharpyuv_dsp.obj \
$(OUT_DIR)\obj\sharpyuv\sharpyuv_gamma.obj \
$(OUT_DIR)\obj\sharpyuv\sharpyuv_neon.obj \
$(OUT_DIR)\obj\sharpyuv\sharpyuv_sse2.obj
DEC_OBJS = \
$(OUT_DIR)\obj\dec\alpha_dec.obj \
$(OUT_DIR)\obj\dec\buffer_dec.obj \
$(OUT_DIR)\obj\dec\frame_dec.obj \
$(OUT_DIR)\obj\dec\idec_dec.obj \
$(OUT_DIR)\obj\dec\io_dec.obj \
$(OUT_DIR)\obj\dec\quant_dec.obj \
$(OUT_DIR)\obj\dec\tree_dec.obj \
$(OUT_DIR)\obj\dec\vp8_dec.obj \
$(OUT_DIR)\obj\dec\vp8l_dec.obj \
$(OUT_DIR)\obj\dec\webp_dec.obj
ENC_OBJS = \
$(OUT_DIR)\obj\enc\alpha_enc.obj \
$(OUT_DIR)\obj\enc\analysis_enc.obj \
$(OUT_DIR)\obj\enc\backward_references_cost_enc.obj \
$(OUT_DIR)\obj\enc\backward_references_enc.obj \
$(OUT_DIR)\obj\enc\config_enc.obj \
$(OUT_DIR)\obj\enc\cost_enc.obj \
$(OUT_DIR)\obj\enc\filter_enc.obj \
$(OUT_DIR)\obj\enc\frame_enc.obj \
$(OUT_DIR)\obj\enc\histogram_enc.obj \
$(OUT_DIR)\obj\enc\iterator_enc.obj \
$(OUT_DIR)\obj\enc\near_lossless_enc.obj \
$(OUT_DIR)\obj\enc\picture_enc.obj \
$(OUT_DIR)\obj\enc\picture_csp_enc.obj \
$(OUT_DIR)\obj\enc\picture_psnr_enc.obj \
$(OUT_DIR)\obj\enc\picture_rescale_enc.obj \
$(OUT_DIR)\obj\enc\picture_tools_enc.obj \
$(OUT_DIR)\obj\enc\predictor_enc.obj \
$(OUT_DIR)\obj\enc\quant_enc.obj \
$(OUT_DIR)\obj\enc\syntax_enc.obj \
$(OUT_DIR)\obj\enc\token_enc.obj \
$(OUT_DIR)\obj\enc\tree_enc.obj \
$(OUT_DIR)\obj\enc\vp8l_enc.obj \
$(OUT_DIR)\obj\enc\webp_enc.obj
DSP_DEC_OBJS = \
$(OUT_DIR)\obj\dsp\alpha_processing.obj \
$(OUT_DIR)\obj\dsp\alpha_processing_mips_dsp_r2.obj \
$(OUT_DIR)\obj\dsp\alpha_processing_neon.obj \
$(OUT_DIR)\obj\dsp\alpha_processing_sse2.obj \
$(OUT_DIR)\obj\dsp\alpha_processing_sse41.obj \
$(OUT_DIR)\obj\dsp\cpu.obj $(OUT_DIR)\obj\dsp\dec.obj \
$(OUT_DIR)\obj\dsp\dec_clip_tables.obj \
$(OUT_DIR)\obj\dsp\dec_mips32.obj \
$(OUT_DIR)\obj\dsp\dec_mips_dsp_r2.obj \
$(OUT_DIR)\obj\dsp\dec_msa.obj \
$(OUT_DIR)\obj\dsp\dec_neon.obj \
$(OUT_DIR)\obj\dsp\dec_sse2.obj \
$(OUT_DIR)\obj\dsp\dec_sse41.obj \
$(OUT_DIR)\obj\dsp\filters.obj \
$(OUT_DIR)\obj\dsp\filters_mips_dsp_r2.obj \
$(OUT_DIR)\obj\dsp\filters_msa.obj \
$(OUT_DIR)\obj\dsp\filters_neon.obj \
$(OUT_DIR)\obj\dsp\filters_sse2.obj \
$(OUT_DIR)\obj\dsp\lossless.obj \
$(OUT_DIR)\obj\dsp\lossless_mips_dsp_r2.obj \
$(OUT_DIR)\obj\dsp\lossless_msa.obj \
$(OUT_DIR)\obj\dsp\lossless_neon.obj \
$(OUT_DIR)\obj\dsp\lossless_sse2.obj \
$(OUT_DIR)\obj\dsp\lossless_sse41.obj \
$(OUT_DIR)\obj\dsp\rescaler.obj \
$(OUT_DIR)\obj\dsp\rescaler_mips32.obj \
$(OUT_DIR)\obj\dsp\rescaler_mips_dsp_r2.obj \
$(OUT_DIR)\obj\dsp\rescaler_msa.obj \
$(OUT_DIR)\obj\dsp\rescaler_neon.obj \
$(OUT_DIR)\obj\dsp\rescaler_sse2.obj \
$(OUT_DIR)\obj\dsp\upsampling.obj \
$(OUT_DIR)\obj\dsp\upsampling_mips_dsp_r2.obj \
$(OUT_DIR)\obj\dsp\upsampling_msa.obj \
$(OUT_DIR)\obj\dsp\upsampling_neon.obj \
$(OUT_DIR)\obj\dsp\upsampling_sse2.obj \
$(OUT_DIR)\obj\dsp\upsampling_sse41.obj \
$(OUT_DIR)\obj\dsp\yuv.obj \
$(OUT_DIR)\obj\dsp\yuv_mips32.obj \
$(OUT_DIR)\obj\dsp\yuv_mips_dsp_r2.obj \
$(OUT_DIR)\obj\dsp\yuv_neon.obj \
$(OUT_DIR)\obj\dsp\yuv_sse2.obj \
$(OUT_DIR)\obj\dsp\yuv_sse41.obj
DSP_ENC_OBJS = \
$(OUT_DIR)\obj\dsp\cost.obj \
$(OUT_DIR)\obj\dsp\cost_mips32.obj \
$(OUT_DIR)\obj\dsp\cost_mips_dsp_r2.obj \
$(OUT_DIR)\obj\dsp\cost_neon.obj \
$(OUT_DIR)\obj\dsp\cost_sse2.obj \
$(OUT_DIR)\obj\dsp\enc.obj \
$(OUT_DIR)\obj\dsp\enc_mips32.obj \
$(OUT_DIR)\obj\dsp\enc_mips_dsp_r2.obj \
$(OUT_DIR)\obj\dsp\enc_msa.obj \
$(OUT_DIR)\obj\dsp\enc_neon.obj \
$(OUT_DIR)\obj\dsp\enc_sse2.obj \
$(OUT_DIR)\obj\dsp\enc_sse41.obj \
$(OUT_DIR)\obj\dsp\lossless_enc.obj \
$(OUT_DIR)\obj\dsp\lossless_enc_mips32.obj \
$(OUT_DIR)\obj\dsp\lossless_enc_mips_dsp_r2.obj \
$(OUT_DIR)\obj\dsp\lossless_enc_msa.obj \
$(OUT_DIR)\obj\dsp\lossless_enc_neon.obj \
$(OUT_DIR)\obj\dsp\lossless_enc_sse2.obj \
$(OUT_DIR)\obj\dsp\lossless_enc_sse41.obj \
$(OUT_DIR)\obj\dsp\ssim.obj \
$(OUT_DIR)\obj\dsp\ssim_sse2.obj
UTILS_DEC_OBJS = \
$(OUT_DIR)\obj\utils\bit_reader_utils.obj \
$(OUT_DIR)\obj\utils\color_cache_utils.obj \
$(OUT_DIR)\obj\utils\filters_utils.obj \
$(OUT_DIR)\obj\utils\huffman_utils.obj \
$(OUT_DIR)\obj\utils\palette.obj \
$(OUT_DIR)\obj\utils\quant_levels_dec_utils.obj \
$(OUT_DIR)\obj\utils\rescaler_utils.obj \
$(OUT_DIR)\obj\utils\random_utils.obj \
$(OUT_DIR)\obj\utils\thread_utils.obj \
$(OUT_DIR)\obj\utils\utils.obj
UTILS_ENC_OBJS = \
$(OUT_DIR)\obj\utils\bit_writer_utils.obj \
$(OUT_DIR)\obj\utils\huffman_encode_utils.obj \
$(OUT_DIR)\obj\utils\quant_levels_utils.obj
IMAGEIO_DEC_OBJS = \
$(OUT_DIR)\obj\imageio\image_dec.obj \
$(OUT_DIR)\obj\imageio\jpegdec.obj \
$(OUT_DIR)\obj\imageio\metadata.obj \
$(OUT_DIR)\obj\imageio\pngdec.obj \
$(OUT_DIR)\obj\imageio\pnmdec.obj \
$(OUT_DIR)\obj\imageio\tiffdec.obj \
$(OUT_DIR)\obj\imageio\webpdec.obj \
$(OUT_DIR)\obj\imageio\wicdec.obj
IMAGEIO_ENC_OBJS = \
$(OUT_DIR)\obj\imageio\image_enc.obj
IMAGEIO_UTIL_OBJS = \
$(OUT_DIR)\obj\imageio\imageio_util.obj
MUX_OBJS = \
$(OUT_DIR)\obj\mux\anim_encode.obj \
$(OUT_DIR)\obj\mux\muxedit.obj \
$(OUT_DIR)\obj\mux\muxinternal.obj \
$(OUT_DIR)\obj\mux\muxread.obj
DEMUX_OBJS = \
$(OUT_DIR)\obj\demux\anim_decode.obj \
$(OUT_DIR)\obj\demux\demux.obj
EXTRAS_OBJS = \
$(OUT_DIR)\obj\extras\extras.obj \
$(OUT_DIR)\obj\extras\quality_estimate.obj \
$(OUT_DIR)\obj\extras\sharpyuv_risk_table.obj
EX_UTIL_OBJS = \
$(OUT_DIR)\obj\examples\example_util.obj
EX_GIF_DEC_OBJS = \
$(OUT_DIR)\obj\examples\gifdec.obj
EX_ANIM_UTIL_OBJS = \
$(OUT_DIR)\obj\examples\anim_util.obj
#-------------------------------------------------------------------------------
# スタティックリンクライブラリ *.LIB の依存関係の定義
#-------------------------------------------------------------------------------
$(OUT_DIR)\lib\libsharpyuv.lib: $(SHARPYUV_OBJS)
$(OUT_DIR)\lib\libwebpmux.lib: $(MUX_OBJS)
$(OUT_DIR)\lib\libwebpdemux.lib: $(DEMUX_OBJS)
$(OUT_DIR)\lib\libwebpdecoder.lib: \
$(DEC_OBJS) $(DSP_DEC_OBJS) $(UTILS_DEC_OBJS)
$(OUT_DIR)\lib\libwebp.lib: \
$(DEC_OBJS) $(DSP_DEC_OBJS) $(UTILS_ENC_OBJS) \
$(ENC_OBJS) $(DSP_ENC_OBJS) $(UTILS_DEC_OBJS) \
$(OUT_DIR)\lib\libsharpyuv.lib
#-------------------------------------------------------------------------------
# 実行ファイル *.EXE の依存関係の定義
#-------------------------------------------------------------------------------
$(OUT_DIR)\bin\cwebp.exe: $(OUT_DIR)\obj\examples\cwebp.obj \
$(EX_UTIL_OBJS) \
$(IMAGEIO_DEC_OBJS) $(IMAGEIO_UTIL_OBJS) \
$(OUT_DIR)\lib\libwebpdemux.lib \
$(OUT_DIR)\lib\libsharpyuv.lib \
$(OUT_DIR)\lib\libwebp.lib
$(OUT_DIR)\bin\dwebp.exe: $(OUT_DIR)\obj\examples\dwebp.obj \
$(EX_UTIL_OBJS) \
$(IMAGEIO_DEC_OBJS) $(IMAGEIO_ENC_OBJS) $(IMAGEIO_UTIL_OBJS) \
$(OUT_DIR)\lib\libwebpdemux.lib \
$(OUT_DIR)\lib\libwebp.lib
$(OUT_DIR)\bin\vwebp.exe: $(OUT_DIR)\obj\examples\vwebp.obj \
$(EX_UTIL_OBJS) \
$(IMAGEIO_UTIL_OBJS) \
$(OUT_DIR)\lib\libwebpdemux.lib \
$(OUT_DIR)\lib\libwebp.lib
$(OUT_DIR)\bin\webpmux.exe: $(OUT_DIR)\obj\examples\webpmux.obj \
$(EX_UTIL_OBJS) \
$(IMAGEIO_UTIL_OBJS) \
$(OUT_DIR)\lib\libwebpmux.lib \
$(OUT_DIR)\lib\libwebp.lib
$(OUT_DIR)\bin\img2webp.exe: $(OUT_DIR)\obj\examples\img2webp.obj \
$(EX_UTIL_OBJS) \
$(IMAGEIO_DEC_OBJS) $(IMAGEIO_UTIL_OBJS) \
$(OUT_DIR)\lib\libsharpyuv.lib \
$(OUT_DIR)\lib\libwebp.lib \
$(OUT_DIR)\lib\libwebpdemux.lib \
$(OUT_DIR)\lib\libwebpmux.lib
$(OUT_DIR)\bin\get_disto.exe: $(OUT_DIR)\obj\extras\get_disto.obj \
$(IMAGEIO_DEC_OBJS) $(IMAGEIO_UTIL_OBJS) \
$(OUT_DIR)\lib\libwebpdemux.lib \
$(OUT_DIR)\lib\libwebp.lib
$(OUT_DIR)\bin\webp_quality.exe: $(OUT_DIR)\obj\extras\webp_quality.obj \
$(EXTRAS_OBJS) \
$(IMAGEIO_UTIL_OBJS) \
$(OUT_DIR)\lib\libwebp.lib
$(OUT_DIR)\bin\vwebp_sdl.exe: $(OUT_DIR)\obj\extras\vwebp_sdl.obj \
$(OUT_DIR)\obj\extras\webp_to_sdl.obj \
$(IMAGEIO_UTIL_OBJS) \
$(OUT_DIR)\lib\libwebp.lib
$(OUT_DIR)\bin\webpinfo.exe: $(OUT_DIR)\obj\examples\webpinfo.obj \
$(EX_UTIL_OBJS) \
$(IMAGEIO_DEC_OBJS) $(IMAGEIO_UTIL_OBJS) \
$(OUT_DIR)\lib\libwebpdemux.lib \
$(OUT_DIR)\lib\libwebp.lib
$(OUT_DIR)\bin\gif2webp.exe: $(OUT_DIR)\obj\examples\gif2webp.obj \
$(EX_UTIL_OBJS) $(EX_GIF_DEC_OBJS) \
$(IMAGEIO_UTIL_OBJS) \
$(OUT_DIR)\lib\libwebpmux.lib \
$(OUT_DIR)\lib\libwebp.lib
$(OUT_DIR)\bin\anim_diff.exe: $(OUT_DIR)\obj\examples\anim_diff.obj \
$(EX_UTIL_OBJS) $(EX_GIF_DEC_OBJS) $(EX_ANIM_UTIL_OBJS) \
$(IMAGEIO_UTIL_OBJS) \
$(OUT_DIR)\lib\libwebpdemux.lib \
$(OUT_DIR)\lib\libwebp.lib
$(OUT_DIR)\bin\anim_dump.exe: $(OUT_DIR)\obj\examples\anim_dump.obj \
$(EX_UTIL_OBJS) $(EX_GIF_DEC_OBJS) $(EX_ANIM_UTIL_OBJS) \
$(IMAGEIO_ENC_OBJS) $(IMAGEIO_UTIL_OBJS) \
$(OUT_DIR)\lib\libwebpdemux.lib \
$(OUT_DIR)\lib\libwebp.lib
#-------------------------------------------------------------------------------
# 出力ディレクトリの生成
#-------------------------------------------------------------------------------
$(OUTPUT_DIRS):
@if not exist $@ mkdir $@
#-------------------------------------------------------------------------------
# ライブラリ *.LIB の生成コマンド
#-------------------------------------------------------------------------------
$(OUT_DIR)\lib\libwebpdecoder.lib \
$(OUT_DIR)\lib\libsharpyuv.lib \
$(OUT_DIR)\lib\libwebp.lib \
$(OUT_DIR)\lib\libwebpdemux.lib \
$(OUT_DIR)\lib\libwebpmux.lib:
$(LINKLIB) $(LLFLAGS) /out:$@ $**
#-------------------------------------------------------------------------------
# 実行ファイル *.EXE の生成コマンド
#-------------------------------------------------------------------------------
$(OUT_DIR)\bin\cwebp.exe \
$(OUT_DIR)\bin\dwebp.exe \
$(OUT_DIR)\bin\vwebp.exe \
$(OUT_DIR)\bin\webpmux.exe \
$(OUT_DIR)\bin\img2webp.exe \
$(OUT_DIR)\bin\get_disto.exe \
$(OUT_DIR)\bin\webp_quality.exe \
$(OUT_DIR)\bin\vwebp_sdl.exe \
$(OUT_DIR)\bin\webpinfo.exe \
$(OUT_DIR)\bin\gif2webp.exe \
$(OUT_DIR)\bin\anim_diff.exe \
$(OUT_DIR)\bin\anim_dump.exe:
$(LINKEXE) $(LDFLAGS) /out:$@ $** $(WINLIBS)
#-------------------------------------------------------------------------------
# オブジェクト *.OBJ の生成コマンド
#-------------------------------------------------------------------------------
{examples}.c{$(OUT_DIR)\obj\examples}.obj::
$(CC) $(CCFLAGS) /Fo$(OUT_DIR)\obj\examples\ $<
{extras}.c{$(OUT_DIR)\obj\extras}.obj::
$(CC) $(CCFLAGS) /Fo$(OUT_DIR)\obj\extras\ $<
{imageio}.c{$(OUT_DIR)\obj\imageio}.obj::
$(CC) $(CCFLAGS) /Fo$(OUT_DIR)\obj\imageio\ $<
{sharpyuv}.c{$(OUT_DIR)\obj\sharpyuv}.obj::
$(CC) $(CCFLAGS) /Fo$(OUT_DIR)\obj\sharpyuv\ $<
{src\dec}.c{$(OUT_DIR)\obj\dec}.obj::
$(CC) $(CCFLAGS) /Fo$(OUT_DIR)\obj\dec\ $<
{src\dsp}.c{$(OUT_DIR)\obj\dsp}.obj::
$(CC) $(CCFLAGS) /Fo$(OUT_DIR)\obj\dsp\ $<
{src\enc}.c{$(OUT_DIR)\obj\enc}.obj::
$(CC) $(CCFLAGS) /Fo$(OUT_DIR)\obj\enc\ $<
{src\mux}.c{$(OUT_DIR)\obj\mux}.obj::
$(CC) $(CCFLAGS) /Fo$(OUT_DIR)\obj\mux\ $<
{src\demux}.c{$(OUT_DIR)\obj\demux}.obj::
$(CC) $(CCFLAGS) /Fo$(OUT_DIR)\obj\demux\ $<
{src\utils}.c{$(OUT_DIR)\obj\utils}.obj::
$(CC) $(CCFLAGS) /Fo$(OUT_DIR)\obj\utils\ $<
#-------------------------------------------------------------------------------
# 生成物のクリーンナップ
#-------------------------------------------------------------------------------
clean:
@if exist $(OUT_DIR) rd /s /q $(OUT_DIR)
メイクファイルの実行
Visual C++ の場合,下記のコマンドを実行する。Makefile.vc2
はオリジナルの Makefile.vc
と同じフォルダにあるものとする。
nmake -f Makefile.vc2
Clang の場合,下記のコマンドを実行する。Makefile.clang
はオリジナルの Makefile.vc
と同じフォルダにあるものとする。
nmake -f Makefile.clang
こうして生成物は下記のフォルダに作られる。
コンバイラ | ターゲット | フォルダ |
---|---|---|
Visual C++ | 32bit | output\release-static\x86vc\bin |
64bit | output\release-static\x64vc\bin |
|
Clang | 32bit | output\release-static\x86clang\bin |
64bit | output\release-static\x64clang\bin |
バイナリサイズの比較
original とはWebP のダウンロードリポジトリからダウンロードしたものである。
コンパイラ | 32bit版 | 64bit版 |
---|---|---|
original | なし | 742,912 bytes |
Visual C++ | 609,280 bytes | 749,056 bytes |
Clang | 817,152 bytes | 866,816 bytes |
バイナリの確認方法
作ったバイナリが32bit版 or 64bit版のいずれか確認する方法を示す。
c:\libwebp>dumpbin /headers output\release-static\x86vc\bin\cwebp.exe | findstr machine
14C machine (x86)
32 bit word machine
c:\libwebp>dumpbin /headers output\release-static\x64vc\bin\cwebp.exe | findstr machine
8664 machine (x64)
c:\libwebp>dumpbin /headers output\release-static\x86clang\bin\cwebp.exe | findstr machine
14C machine (x86)
32 bit word machine
c:\libwebp>dumpbin /headers output\release-static\x64clang\bin\cwebp.exe | findstr machine
8664 machine (x64)
6. パフォーマンス比較
題材は前回の記事で用いたサンプル画像である。300dpiでスキャンしたカラー画像4枚,白黒画像36枚のビットマップファイル *.BMP を cwebp.exe
を用いて可逆圧縮(lossless)モードかつ最高品質 z=9
にて圧縮した際に要した計算時間の平均値を示す。original を基準100とした相対値で示す。
なお,今回はインテル第8,11,13世代のプロセッサで比較することができた。
ちなみに第8世代を 100 とすると,第11世代は 70~75,第13世代は 50~55 となる。
7. 結論
-
コンパイラの 32bit版 or 64bit 版問題については明確な結論が出た。コンパイラを問わず,プロセッサの世代を問わず 64bit版のほうが明らかに高速である。google の公式サイトで64bit版しか提供しないのも当たり前であろう。
-
コンパイルオプションの工夫により,同じ Visual C++ でも第8世代プロセッサでは original に対して 5% 程度の改善を得ることができたが,プロセッサの世代が新しくなるにつれて優位性が無くなり,第13世代では逆効果になってしまったのは悲しい。
-
Clang は凄い。プロセッサの世代を問わず,最強である。ただし,プロセッサの世代が新しくなるにつれ,Clang の優位性が少しずつ薄れていくのは不思議である。Clang のバイナリサイズは増えていることから,Clang ではループアンローリングや関数のインライン展開などの最適化を積極的に行っていたと考えられるが,新世代のプロセッサではこれらのテクニックの効果が薄れているのだろう。
なお,オリジナルのメイクファイルだと gif2webp.exe
, anim_diff.exe
, anim_dump.exe
のビルドに失敗してしまうので,この機にビルドできるよう修正した。ただし動作未検証である。