背景
Makefileをタスクランナーとして使用する記事が色々あります。
そうすると、パラメータチューニングなどで、全探索をしたくなります。
本記事では、Makefileの並列処理の能力を生かせるようにターゲットを構成してみます。
方法
Makefileのワイルドカードや関数を用いて、テストケースの通し番号に紐づくパラメータの組み合わせを求めます。
LIST_OF_VAR_A = $(shell seq -f "%03.1f" 0 0.1 0.2)
LIST_OF_VAR_B = $(shell seq -f "%01.0f" 2)
LIST_OF_VAR_C = $(shell seq -f "%02.0f" 10 12)
LEN_A = $(words $(LIST_OF_VAR_A))
LEN_B = $(words $(LIST_OF_VAR_B))
LEN_C = $(words $(LIST_OF_VAR_C))
LAST_INDEX := 1
LAST_INDEX := $(shell echo "$(LEN_A) * $(LAST_INDEX)" | bc)
LAST_INDEX := $(shell echo "$(LEN_B) * $(LAST_INDEX)" | bc)
LAST_INDEX := $(shell echo "$(LEN_C) * $(LAST_INDEX)" | bc)
LAST_INDEX := $(shell echo "$(LAST_INDEX) - 1" | bc)
OUTPUTS = $(foreach INDEX, $(shell seq -f "%02.0f" 0 $(LAST_INDEX)), test_$(INDEX))
all: $(OUTPUTS)
test_%:
$(eval INDEX:=$(subst test_,,$@))
$(eval VAR_A = $(word $(shell echo "$(INDEX) % $(LEN_A) / 1 + 1" | bc), $(LIST_OF_VAR_A)))
$(eval VAR_B = $(word $(shell echo "$(INDEX) % ($(LEN_A)*$(LEN_B)) / $(LEN_A) + 1" | bc), $(LIST_OF_VAR_B)))
$(eval VAR_C = $(word $(shell echo "$(INDEX) % ($(LEN_A)*$(LEN_B)*$(LEN_C)) / ($(LEN_A)*$(LEN_B)) +1 " | bc), $(LIST_OF_VAR_C)))
@echo Check $@ with VAR_A: $(VAR_A), VAR_B: $(VAR_B), VAR_C: $(VAR_C)
結果
$ make all
Check test_00 with VAR_A: 0.0, VAR_B: 1, VAR_C: 10
Check test_01 with VAR_A: 0.1, VAR_B: 1, VAR_C: 10
Check test_02 with VAR_A: 0.2, VAR_B: 1, VAR_C: 10
Check test_03 with VAR_A: 0.0, VAR_B: 2, VAR_C: 10
Check test_04 with VAR_A: 0.1, VAR_B: 2, VAR_C: 10
Check test_05 with VAR_A: 0.2, VAR_B: 2, VAR_C: 10
Check test_06 with VAR_A: 0.0, VAR_B: 1, VAR_C: 11
Check test_07 with VAR_A: 0.1, VAR_B: 1, VAR_C: 11
Check test_08 with VAR_A: 0.2, VAR_B: 1, VAR_C: 11
Check test_09 with VAR_A: 0.0, VAR_B: 2, VAR_C: 11
Check test_10 with VAR_A: 0.1, VAR_B: 2, VAR_C: 11
Check test_11 with VAR_A: 0.2, VAR_B: 2, VAR_C: 11
Check test_12 with VAR_A: 0.0, VAR_B: 1, VAR_C: 12
Check test_13 with VAR_A: 0.1, VAR_B: 1, VAR_C: 12
Check test_14 with VAR_A: 0.2, VAR_B: 1, VAR_C: 12
Check test_15 with VAR_A: 0.0, VAR_B: 2, VAR_C: 12
Check test_16 with VAR_A: 0.1, VAR_B: 2, VAR_C: 12
Check test_17 with VAR_A: 0.2, VAR_B: 2, VAR_C: 12
解説
3通りの値を取るVAR_A、2通りの値を取るVAR_B、3通りの値を取るVAR_Cの全探索なので、3x2x3=18(通り)あります。
そこで、00から18までのインデックスを各場合に割り当て、生成すべきターゲット名をtest_00などのように名付けます。
ターゲット名から、インデックス$I$を取得するのが以下の行です。
$(eval INDEX:=$(subst test_,,$@))
インデックス$I$に基づいて、パラメータを決定します。
VAR_A, VAR_B, VAR_Cのインデックス$I_A$, $I_B$, $I_C$は以下で決定します。
$$ I_A = (I \% 3) // 1$$
$$ I_B = (I \% (3*2)) //3 $$
$$ I_C = (I \% (3*2*3)) //(3*2) $$
ここで、//は商を、%は「あまり」を求める演算子です。この計算で以下の数列を得ます。
ただし、Makefileの関数であるword
は、最初の要素のインデックスがゼロではなく1であるので、上記の計算で求めたI_A, I_B, I_Cに1を足して、パラメータを取得しています。
I | I_A | I_B | I_C |
---|---|---|---|
0 | 0 | 0 | 0 |
1 | 1 | 0 | 0 |
2 | 2 | 0 | 0 |
3 | 0 | 1 | 0 |
4 | 1 | 1 | 0 |
5 | 2 | 1 | 0 |
6 | 0 | 0 | 1 |
7 | 1 | 0 | 1 |
8 | 2 | 0 | 1 |
9 | 0 | 1 | 1 |
10 | 1 | 1 | 1 |
11 | 2 | 1 | 1 |
12 | 0 | 0 | 2 |
13 | 1 | 0 | 2 |
14 | 2 | 0 | 2 |
15 | 0 | 1 | 2 |
16 | 1 | 1 | 2 |
17 | 2 | 1 | 2 |