Eu tenho este pequeno programa C89 - o que ele faz, como funciona e o comportamento indefinido importante, apenas que faz alguma memória longa & operações matemáticas em apenas um thread:
x, c, * b, m, t, i, a;
g (n) {
for (b = malloc(0); c < n; b[c - 1] = x++, t = 1) {
char s[9];
for (i = m = 0; i < sprintf(s, "%d", x); m += a, t *= a) a = s[i++] - 48;
b = m * t ? x % m + x % t ? b : realloc(b, 4 * ++c) : b;
}
return b[c - 1];
}
main (j) {
printf("%d\n", g(--j));
}
Compile assim: gcc -std=c89 tt.c -o tt -O3
.
Em seguida, se eu usar um script de shell para executá-lo em um loop, para ter uma ideia do tempo de execução:
#!/bin/bash
echo "using input $1"
for _ in 'seq 1 10'; do
( time ./tt $(seq 1 $1) ) 3>&1 1>/dev/null 2>&3 \
| grep real \
| cut -f2
# sleep 5
done
Eu vejo a saída assim:
$ ./tt.sh 50
using input 50
0m0.016s
0m0.008s
0m0.008s
0m0.007s
0m0.007s
0m0.007s
0m0.008s
0m0.008s
0m0.007s
0m0.007s
Ou assim:
$ ./tt.sh 34
using input 34
0m0.007s
0m0.004s
0m0.004s
0m0.004s
0m0.005s
0m0.004s
0m0.003s
0m0.003s
0m0.003s
0m0.004s
Há uma aceleração inicial no tempo de execução real
do programa após a primeira chamada e, em seguida, todas as chamadas subseqüentes são executadas nesse falso speedup.
Se eu descomentar a linha # sleep 5
no script de shell, vemos estes resultados:
using input 50
0m0.008s
0m0.020s
0m0.018s
0m0.012s
0m0.009s
0m0.006s
0m0.013s
0m0.012s
0m0.009s
0m0.012s
using input 34
0m0.006s
0m0.007s
0m0.004s
0m0.007s
0m0.008s
0m0.003s
0m0.004s
0m0.004s
0m0.005s
0m0.007s
Os tempos parecem mais esperados e precisos, e as discrepâncias devem ser atribuídas ao estado aleatório do processador naquele momento (ou seja, são pequenas variações naturais).
Se eu quiser ter um tempo médio de execução para o meu programa, eu deveria calcular a média desses números, mas sleep 5
entre cada chamada, enquanto é a única maneira que posso encontrar para parar esse comportamento, adiciona até 50 segundos 10 testes em vez de alguns segundos para 20 testes.
Eu já vi esse comportamento "replay caching" antes com programas single-threaded que fazem operações longas repetidas vezes (tight loops), e eu entendo que é desejável em 99,9% de todos os casos.
Supondo que isso não seja uma consequência de algum Intel Magic ™ no nível do hardware, isso é algo que o kernel do Linux ou Bash está fazendo intencionalmente, e como posso fazer isso parar?
Eu gostaria de tempos de execução reproduzíveis para meus programas, incluindo carregamento e paginação de bibliotecas a partir de "cold start", sem sleep 5
ing, porque os horários afetados pelo cache não representam uma inicialização a frio a cada vez.