これは何
GNU make から ninja に移行を検討中のメモです。
H.264 のリファレンスソフトウエアデコーダを題材に、 make と ninja のビルド時間を比較します。
単に makefile と build.ninja を見比べたい人は hello world の makefile と build.ninja や .a ファイルのmakefile と build.ninja をどうぞご覧ください。
H.264 リファレンスデコーダって何?
H.264 という、一昔前のビデオ圧縮形式があります。
これのデコーダを開発する開発者や、圧縮アルゴリズムを検討したい研究者が利用することを想定したソフトウエアでソースコードが公開されています。
Linux 向けのビルド環境は素の makefile なので、これと ninja とのビルド時間を比較してみます。
想定読者
makefile を自分でかける人
.PHONY: clean を毎回書くのに飽き飽きした人
素の make の動作が遅いなぁと思う人
ninja 向けのメタビルドツールを作りたい人
結論
| コマンド名 | 所要時間(秒) |
|---|---|
| ninja | 17.547524 |
| make | 32.162396 |
ninja が圧倒的に早いです。ninja 公式が売りにしているとおり、早いです。
簡単な構成のプログラムであれば、build.ninja を直接書くのも全然ありと思います。
meson や cmake を使うと、記述量が減りますが、build.ninja の生成時間も気になりますんで。
測定環境
alpine linux on docker for windows 10
コンテナイメージは、下記 Dockerfile です。
FROM gliderlabs/alpine:latest
RUN set -eux; \
apk --update add --no-cache \
libc-dev \
gcc \
make \
tar \
ccache \
ninja
WORKDIR /root
このイメージで、 make と ninja を実施し、 powershell -C Measure-Command { cmd /C ninja.bat -C build } のように、 powershell で実行時間を測定しています。ninja.bat の中身は下記です。makeを試すときは、ninja を make に書き換えたファイルを使用しています。
@echo off
setlocal
set docker_image=gcc
cd /d "%~dp0"
docker run --rm -it -v "%CD%":/root %docker_image% ninja %*
if errorlevel 1 pause
ファイル構成
jm19.zip を取得し、展開すると下記のディレクトリが作成されます。
JM/
|- bin
|- doc
|- JM.xcodeproj
|- lcommon
|- ldecod
|- lencod
|- rtp_loss
`- rtpdump
make での bin/ldecod.exe のビルド
カレントディレクトリを JM として、 make -C ldecod STC=1 を実行すると、今回の測定と同じオプションでビルドできます。
ninja での bin/ldecod のビルド
下記 build.ninja を JM/build.ninja に作成します。ビルドは、カレントディレクトリが JM とすると ninja となります。 JM/bin/ldecod が生成されます。
root = .
builddir = build
exedir = bin
# cc = ccache cc
cc = cc
cflags = -Wall -O3 -std=gnu99 -pedantic -ffloat-store -fno-strict-aliasing -fsigned-char
cppflags = -DNDEBUG -D__USE_LARGEFILE64 -D_FILE_OFFSET_BITS=64 -I$root/lcommon/inc -I$root/ldecod/inc
ldlibs = -lm -static
rule compile
description = compiling object file "$out" ...
deps = gcc
depfile = $out.d
command = $cc -MMD -MP -MF $out.d $cflags $cppflags $target_arch -c -o $out $in
rule link
description = creating binary "$out"
command = $cc $ldflags $target_arch $in $loadlibes $ldlibs -o $out
build $exedir/ldecod: link $
$builddir/blk_prediction.o $
$builddir/config_common.o $
$builddir/img_io.o $
$builddir/img_process.o $
$builddir/input.o $
$builddir/io_raw.o $
$builddir/io_tiff.o $
$builddir/mbuffer_common.o $
$builddir/memalloc.o $
$builddir/mv_prediction.o $
$builddir/nalucommon.o $
$builddir/parsetcommon.o $
$builddir/resize.o $
$builddir/transform.o $
$builddir/win32.o $
$
$builddir/annexb.o $
$builddir/biaridecod.o $
$builddir/block.o $
$builddir/cabac.o $
$builddir/configfile.o $
$builddir/context_ini.o $
$builddir/decoder_test.o $
$builddir/dec_statistics.o $
$builddir/erc_api.o $
$builddir/erc_do_i.o $
$builddir/erc_do_p.o $
$builddir/errorconcealment.o $
$builddir/filehandle.o $
$builddir/fmo.o $
$builddir/header.o $
$builddir/image.o $
$builddir/intra16x16_pred.o $
$builddir/intra16x16_pred_mbaff.o $
$builddir/intra16x16_pred_normal.o $
$builddir/intra4x4_pred.o $
$builddir/intra4x4_pred_mbaff.o $
$builddir/intra4x4_pred_normal.o $
$builddir/intra8x8_pred.o $
$builddir/intra8x8_pred_mbaff.o $
$builddir/intra8x8_pred_normal.o $
$builddir/intra_chroma_pred.o $
$builddir/intra_chroma_pred_mbaff.o $
$builddir/intra_pred_common.o $
$builddir/ldecod.o $
$builddir/leaky_bucket.o $
$builddir/loopFilter.o $
$builddir/loop_filter_mbaff.o $
$builddir/loop_filter_normal.o $
$builddir/macroblock.o $
$builddir/mbuffer.o $
$builddir/mbuffer_mvc.o $
$builddir/mb_access.o $
$builddir/mb_prediction.o $
$builddir/mb_read.o $
$builddir/mc_direct.o $
$builddir/mc_prediction.o $
$builddir/nal.o $
$builddir/nalu.o $
$builddir/output.o $
$builddir/parset.o $
$builddir/quant.o $
$builddir/read_comp_cabac.o $
$builddir/read_comp_cavlc.o $
$builddir/rtp.o $
$builddir/sei.o $
$builddir/transform8x8.o $
$builddir/vlc.o
build $builddir/blk_prediction.o : compile $root/lcommon/src/blk_prediction.c
build $builddir/config_common.o : compile $root/lcommon/src/config_common.c
build $builddir/img_io.o : compile $root/lcommon/src/img_io.c
build $builddir/img_process.o : compile $root/lcommon/src/img_process.c
build $builddir/input.o : compile $root/lcommon/src/input.c
build $builddir/io_raw.o : compile $root/lcommon/src/io_raw.c
build $builddir/io_tiff.o : compile $root/lcommon/src/io_tiff.c
build $builddir/mbuffer_common.o : compile $root/lcommon/src/mbuffer_common.c
build $builddir/memalloc.o : compile $root/lcommon/src/memalloc.c
build $builddir/mv_prediction.o : compile $root/lcommon/src/mv_prediction.c
build $builddir/nalucommon.o : compile $root/lcommon/src/nalucommon.c
build $builddir/parsetcommon.o : compile $root/lcommon/src/parsetcommon.c
build $builddir/resize.o : compile $root/lcommon/src/resize.c
build $builddir/transform.o : compile $root/lcommon/src/transform.c
build $builddir/win32.o : compile $root/lcommon/src/win32.c
build $builddir/annexb.o : compile $root/ldecod/src/annexb.c
build $builddir/biaridecod.o : compile $root/ldecod/src/biaridecod.c
build $builddir/block.o : compile $root/ldecod/src/block.c
build $builddir/cabac.o : compile $root/ldecod/src/cabac.c
build $builddir/configfile.o : compile $root/ldecod/src/configfile.c
build $builddir/context_ini.o : compile $root/ldecod/src/context_ini.c
build $builddir/decoder_test.o : compile $root/ldecod/src/decoder_test.c
build $builddir/dec_statistics.o : compile $root/ldecod/src/dec_statistics.c
build $builddir/erc_api.o : compile $root/ldecod/src/erc_api.c
build $builddir/erc_do_i.o : compile $root/ldecod/src/erc_do_i.c
build $builddir/erc_do_p.o : compile $root/ldecod/src/erc_do_p.c
build $builddir/errorconcealment.o : compile $root/ldecod/src/errorconcealment.c
build $builddir/filehandle.o : compile $root/ldecod/src/filehandle.c
build $builddir/fmo.o : compile $root/ldecod/src/fmo.c
build $builddir/header.o : compile $root/ldecod/src/header.c
build $builddir/image.o : compile $root/ldecod/src/image.c
build $builddir/intra16x16_pred.o : compile $root/ldecod/src/intra16x16_pred.c
build $builddir/intra16x16_pred_mbaff.o : compile $root/ldecod/src/intra16x16_pred_mbaff.c
build $builddir/intra16x16_pred_normal.o : compile $root/ldecod/src/intra16x16_pred_normal.c
build $builddir/intra4x4_pred.o : compile $root/ldecod/src/intra4x4_pred.c
build $builddir/intra4x4_pred_mbaff.o : compile $root/ldecod/src/intra4x4_pred_mbaff.c
build $builddir/intra4x4_pred_normal.o : compile $root/ldecod/src/intra4x4_pred_normal.c
build $builddir/intra8x8_pred.o : compile $root/ldecod/src/intra8x8_pred.c
build $builddir/intra8x8_pred_mbaff.o : compile $root/ldecod/src/intra8x8_pred_mbaff.c
build $builddir/intra8x8_pred_normal.o : compile $root/ldecod/src/intra8x8_pred_normal.c
build $builddir/intra_chroma_pred.o : compile $root/ldecod/src/intra_chroma_pred.c
build $builddir/intra_chroma_pred_mbaff.o : compile $root/ldecod/src/intra_chroma_pred_mbaff.c
build $builddir/intra_pred_common.o : compile $root/ldecod/src/intra_pred_common.c
build $builddir/ldecod.o : compile $root/ldecod/src/ldecod.c
build $builddir/leaky_bucket.o : compile $root/ldecod/src/leaky_bucket.c
build $builddir/loopFilter.o : compile $root/ldecod/src/loopFilter.c
build $builddir/loop_filter_mbaff.o : compile $root/ldecod/src/loop_filter_mbaff.c
build $builddir/loop_filter_normal.o : compile $root/ldecod/src/loop_filter_normal.c
build $builddir/macroblock.o : compile $root/ldecod/src/macroblock.c
build $builddir/mbuffer.o : compile $root/ldecod/src/mbuffer.c
build $builddir/mbuffer_mvc.o : compile $root/ldecod/src/mbuffer_mvc.c
build $builddir/mb_access.o : compile $root/ldecod/src/mb_access.c
build $builddir/mb_prediction.o : compile $root/ldecod/src/mb_prediction.c
build $builddir/mb_read.o : compile $root/ldecod/src/mb_read.c
build $builddir/mc_direct.o : compile $root/ldecod/src/mc_direct.c
build $builddir/mc_prediction.o : compile $root/ldecod/src/mc_prediction.c
build $builddir/nal.o : compile $root/ldecod/src/nal.c
build $builddir/nalu.o : compile $root/ldecod/src/nalu.c
build $builddir/output.o : compile $root/ldecod/src/output.c
build $builddir/parset.o : compile $root/ldecod/src/parset.c
build $builddir/quant.o : compile $root/ldecod/src/quant.c
build $builddir/read_comp_cabac.o : compile $root/ldecod/src/read_comp_cabac.c
build $builddir/read_comp_cavlc.o : compile $root/ldecod/src/read_comp_cavlc.c
build $builddir/rtp.o : compile $root/ldecod/src/rtp.c
build $builddir/sei.o : compile $root/ldecod/src/sei.c
build $builddir/transform8x8.o : compile $root/ldecod/src/transform8x8.c
build $builddir/vlc.o : compile $root/ldecod/src/vlc.c
build.ninja 風の makefile でビルドを試す
標準のビルド結果と, ninja のビルド結果がバイナリ一致しなかったので、微妙にオプションがそろえられていなかったようです。なので、下記 makefile でもビルドしてみました。 JM/build_mk/makefile に配置しています。
make -C build_mk を実行すると、 JM/bin/ldecod.exe が生成されます。ビルド時間は 32.0608236 秒でした。標準の makefile とほぼ同じで、測定誤差程度の差といえるでしょう。
CFLAGS=-Wall -O3 -std=gnu99 -pedantic -ffloat-store -fno-strict-aliasing -fsigned-char -MMD -MP
CPPFLAGS=-DNDEBUG -D__USE_LARGEFILE64 -D_FILE_OFFSET_BITS=64 -I../lcommon/inc -I../ldecod/inc
LDLIBS=-lm -static
vpath %.c ../lcommon/src
vpath %.c ../ldecod/src
OBJS= \
blk_prediction.o \
config_common.o \
img_io.o \
img_process.o \
input.o \
io_raw.o \
io_tiff.o \
mbuffer_common.o \
memalloc.o \
mv_prediction.o \
nalucommon.o \
parsetcommon.o \
resize.o \
transform.o \
win32.o \
\
annexb.o \
biaridecod.o \
block.o \
cabac.o \
configfile.o \
context_ini.o \
decoder_test.o \
dec_statistics.o \
erc_api.o \
erc_do_i.o \
erc_do_p.o \
errorconcealment.o \
filehandle.o \
fmo.o \
header.o \
image.o \
intra16x16_pred.o \
intra16x16_pred_mbaff.o \
intra16x16_pred_normal.o \
intra4x4_pred.o \
intra4x4_pred_mbaff.o \
intra4x4_pred_normal.o \
intra8x8_pred.o \
intra8x8_pred_mbaff.o \
intra8x8_pred_normal.o \
intra_chroma_pred.o \
intra_chroma_pred_mbaff.o \
intra_pred_common.o \
ldecod.o \
leaky_bucket.o \
loopFilter.o \
loop_filter_mbaff.o \
loop_filter_normal.o \
macroblock.o \
mbuffer.o \
mbuffer_mvc.o \
mb_access.o \
mb_prediction.o \
mb_read.o \
mc_direct.o \
mc_prediction.o \
nal.o \
nalu.o \
output.o \
parset.o \
quant.o \
read_comp_cabac.o \
read_comp_cavlc.o \
rtp.o \
sei.o \
transform8x8.o \
vlc.o
.PHONY: clean
../bin/ldecod.exe:$(OBJS)
@echo 'creating binary "$@"'
@$(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@
@echo '... done'
clean:
@echo remove all objects
$(RM) $(OBJS:%.o=%.d) $(OBJS) ldecod.exe
-include $(OBJS:%.o=%.d)
おまけ
visual studio 用の build.ninja も作ってみました。 chocolatey で install される version 1.8.2 では、バグを踏んで動かないので注意ください。ビルドは 6.3088257秒でした。早い。 docker 立ち上げないからだとおもいます。
root = .
builddir = build_win
exedir = bin
msvc_deps_prefix = メモ: インクルード ファイル:
cflags = /Wall /Os
cppflags = /DNDEBUG /DWIN64 /I$root\lcommon\inc /I$root\ldecod\inc
ldlibs = Ws2_32.lib
rule compile
description = compiling object file "$out" ...
deps = msvc
command = cl /nologo /showIncludes $cflags $cppflags /c /Fo$out $in
rule link
description = creating binary "$out"
command = link /NOLOGO $ldflags $target_arch $loadlibes $ldlibs /OUT:$out $in
build $exedir\ldecod.exe : link $
$builddir\blk_prediction.obj $
$builddir\config_common.obj $
$builddir\img_io.obj $
$builddir\img_process.obj $
$builddir\input.obj $
$builddir\io_raw.obj $
$builddir\io_tiff.obj $
$builddir\mbuffer_common.obj $
$builddir\memalloc.obj $
$builddir\mv_prediction.obj $
$builddir\nalucommon.obj $
$builddir\parsetcommon.obj $
$builddir\resize.obj $
$builddir\transform.obj $
$builddir\win32.obj $
$
$builddir\annexb.obj $
$builddir\biaridecod.obj $
$builddir\block.obj $
$builddir\cabac.obj $
$builddir\configfile.obj $
$builddir\context_ini.obj $
$builddir\decoder_test.obj $
$builddir\dec_statistics.obj $
$builddir\erc_api.obj $
$builddir\erc_do_i.obj $
$builddir\erc_do_p.obj $
$builddir\errorconcealment.obj $
$builddir\filehandle.obj $
$builddir\fmo.obj $
$builddir\header.obj $
$builddir\image.obj $
$builddir\intra16x16_pred.obj $
$builddir\intra16x16_pred_mbaff.obj $
$builddir\intra16x16_pred_normal.obj $
$builddir\intra4x4_pred.obj $
$builddir\intra4x4_pred_mbaff.obj $
$builddir\intra4x4_pred_normal.obj $
$builddir\intra8x8_pred.obj $
$builddir\intra8x8_pred_mbaff.obj $
$builddir\intra8x8_pred_normal.obj $
$builddir\intra_chroma_pred.obj $
$builddir\intra_chroma_pred_mbaff.obj $
$builddir\intra_pred_common.obj $
$builddir\ldecod.obj $
$builddir\leaky_bucket.obj $
$builddir\loopFilter.obj $
$builddir\loop_filter_mbaff.obj $
$builddir\loop_filter_normal.obj $
$builddir\macroblock.obj $
$builddir\mbuffer.obj $
$builddir\mbuffer_mvc.obj $
$builddir\mb_access.obj $
$builddir\mb_prediction.obj $
$builddir\mb_read.obj $
$builddir\mc_direct.obj $
$builddir\mc_prediction.obj $
$builddir\nal.obj $
$builddir\nalu.obj $
$builddir\output.obj $
$builddir\parset.obj $
$builddir\quant.obj $
$builddir\read_comp_cabac.obj $
$builddir\read_comp_cavlc.obj $
$builddir\rtp.obj $
$builddir\sei.obj $
$builddir\transform8x8.obj $
$builddir\vlc.obj
build $builddir\blk_prediction.obj : compile $root\lcommon\src\blk_prediction.c
build $builddir\config_common.obj : compile $root\lcommon\src\config_common.c
build $builddir\img_io.obj : compile $root\lcommon\src\img_io.c
build $builddir\img_process.obj : compile $root\lcommon\src\img_process.c
build $builddir\input.obj : compile $root\lcommon\src\input.c
build $builddir\io_raw.obj : compile $root\lcommon\src\io_raw.c
build $builddir\io_tiff.obj : compile $root\lcommon\src\io_tiff.c
build $builddir\mbuffer_common.obj : compile $root\lcommon\src\mbuffer_common.c
build $builddir\memalloc.obj : compile $root\lcommon\src\memalloc.c
build $builddir\mv_prediction.obj : compile $root\lcommon\src\mv_prediction.c
build $builddir\nalucommon.obj : compile $root\lcommon\src\nalucommon.c
build $builddir\parsetcommon.obj : compile $root\lcommon\src\parsetcommon.c
build $builddir\resize.obj : compile $root\lcommon\src\resize.c
build $builddir\transform.obj : compile $root\lcommon\src\transform.c
build $builddir\win32.obj : compile $root\lcommon\src\win32.c
build $builddir\annexb.obj : compile $root\ldecod\src\annexb.c
build $builddir\biaridecod.obj : compile $root\ldecod\src\biaridecod.c
build $builddir\block.obj : compile $root\ldecod\src\block.c
build $builddir\cabac.obj : compile $root\ldecod\src\cabac.c
build $builddir\configfile.obj : compile $root\ldecod\src\configfile.c
build $builddir\context_ini.obj : compile $root\ldecod\src\context_ini.c
build $builddir\decoder_test.obj : compile $root\ldecod\src\decoder_test.c
build $builddir\dec_statistics.obj : compile $root\ldecod\src\dec_statistics.c
build $builddir\erc_api.obj : compile $root\ldecod\src\erc_api.c
build $builddir\erc_do_i.obj : compile $root\ldecod\src\erc_do_i.c
build $builddir\erc_do_p.obj : compile $root\ldecod\src\erc_do_p.c
build $builddir\errorconcealment.obj : compile $root\ldecod\src\errorconcealment.c
build $builddir\filehandle.obj : compile $root\ldecod\src\filehandle.c
build $builddir\fmo.obj : compile $root\ldecod\src\fmo.c
build $builddir\header.obj : compile $root\ldecod\src\header.c
build $builddir\image.obj : compile $root\ldecod\src\image.c
build $builddir\intra16x16_pred.obj : compile $root\ldecod\src\intra16x16_pred.c
build $builddir\intra16x16_pred_mbaff.obj : compile $root\ldecod\src\intra16x16_pred_mbaff.c
build $builddir\intra16x16_pred_normal.obj : compile $root\ldecod\src\intra16x16_pred_normal.c
build $builddir\intra4x4_pred.obj : compile $root\ldecod\src\intra4x4_pred.c
build $builddir\intra4x4_pred_mbaff.obj : compile $root\ldecod\src\intra4x4_pred_mbaff.c
build $builddir\intra4x4_pred_normal.obj : compile $root\ldecod\src\intra4x4_pred_normal.c
build $builddir\intra8x8_pred.obj : compile $root\ldecod\src\intra8x8_pred.c
build $builddir\intra8x8_pred_mbaff.obj : compile $root\ldecod\src\intra8x8_pred_mbaff.c
build $builddir\intra8x8_pred_normal.obj : compile $root\ldecod\src\intra8x8_pred_normal.c
build $builddir\intra_chroma_pred.obj : compile $root\ldecod\src\intra_chroma_pred.c
build $builddir\intra_chroma_pred_mbaff.obj : compile $root\ldecod\src\intra_chroma_pred_mbaff.c
build $builddir\intra_pred_common.obj : compile $root\ldecod\src\intra_pred_common.c
build $builddir\ldecod.obj : compile $root\ldecod\src\ldecod.c
build $builddir\leaky_bucket.obj : compile $root\ldecod\src\leaky_bucket.c
build $builddir\loopFilter.obj : compile $root\ldecod\src\loopFilter.c
build $builddir\loop_filter_mbaff.obj : compile $root\ldecod\src\loop_filter_mbaff.c
build $builddir\loop_filter_normal.obj : compile $root\ldecod\src\loop_filter_normal.c
build $builddir\macroblock.obj : compile $root\ldecod\src\macroblock.c
build $builddir\mbuffer.obj : compile $root\ldecod\src\mbuffer.c
build $builddir\mbuffer_mvc.obj : compile $root\ldecod\src\mbuffer_mvc.c
build $builddir\mb_access.obj : compile $root\ldecod\src\mb_access.c
build $builddir\mb_prediction.obj : compile $root\ldecod\src\mb_prediction.c
build $builddir\mb_read.obj : compile $root\ldecod\src\mb_read.c
build $builddir\mc_direct.obj : compile $root\ldecod\src\mc_direct.c
build $builddir\mc_prediction.obj : compile $root\ldecod\src\mc_prediction.c
build $builddir\nal.obj : compile $root\ldecod\src\nal.c
build $builddir\nalu.obj : compile $root\ldecod\src\nalu.c
build $builddir\output.obj : compile $root\ldecod\src\output.c
build $builddir\parset.obj : compile $root\ldecod\src\parset.c
build $builddir\quant.obj : compile $root\ldecod\src\quant.c
build $builddir\read_comp_cabac.obj : compile $root\ldecod\src\read_comp_cabac.c
build $builddir\read_comp_cavlc.obj : compile $root\ldecod\src\read_comp_cavlc.c
build $builddir\rtp.obj : compile $root\ldecod\src\rtp.c
build $builddir\sei.obj : compile $root\ldecod\src\sei.c
build $builddir\transform8x8.obj : compile $root\ldecod\src\transform8x8.c
build $builddir\vlc.obj : compile $root\ldecod\src\vlc.c
感想
クリーンビルドでは差が無いのでは無いかと思っていたが、ninja が倍近く早くてびっくりしました。積極的に ninja を使う理由になります。
ただ、build.ninja は makefile に比べて記述量が増えるのは間違いないです。面倒です。
include文 を駆使することで、 makefile でいうところの wildcard 的な記述ができないか、実験してみたいと思います。まぁ、駄目な気もしますが。設計思想で wildcard は 速度低下の元になるので、あえてサポートしていないそうなんで。