Contexto: Estou usando a memória compartilhada POSIX para fornecer um conjunto de processos com um espaço de memória compartilhada. Eu usei esse esquema já há algum tempo para compartilhar dados e está funcionando bem. No entanto, recentemente encontrei um problema estranho com uma determinada classe de programas.
Problema: Eu escrevi um programa no qual cada processo tem que contribuir com um valor para uma soma compartilhada no espaço de memória compartilhada. A soma foi inicializada para zero quando o objeto compartilhado foi mapeado na memória anteriormente. No entanto, quando cada processo tenta adicionar sua parte à soma compartilhada, ele pode ver o valor mais recente, mas o resultado da adição é sempre como se tivesse adicionado seu próprio valor com zero . Veja abaixo:
[21017] Adding 6 to 0!
[21020] Adding 33 to 0!
[21016] Adding 15 to 0!
[21018] Adding 24 to 0!
[21017] Got access! (0x7fe953fcb000 = 0)
[21017] Done (0x7fe953fcb000 = 6)
[21016] Got access! (0x7fe953fcb000 = 6)
[21016] Done (0x7fe953fcb000 = 15)
[21018] Got access! (0x7fe953fcb000 = 15)
[21018] Done (0x7fe953fcb000 = 24)
[21020] Got access! (0x7fe953fcb000 = 24)
[21020] Done (0x7fe953fcb000 = 33)
Sum = 33
Cada processo "vê" o último valor escrito, mas de alguma forma, depois de adicionar seu próprio componente, parece ter ignorado o valor existente. Você pode ver que cada acesso é ordenado sequencialmente, pois há um sistema de controle de acesso gerenciando quem pode gravar no espaço de memória compartilhada. O programa de teste utilizado é semelhante ao seguinte (embora eu não espere que o leitor o execute):
int main (void) {
int local_sum = 0, gid = -1;
volatile int *sum;
// Fork for four processes.
for (int i = 1; i < 4; i++) {
if (fork() == 0) break;
}
// Initialize the DSM. Set GID.
sum = (int *)dsm_init(&cfg);
gid = dsm_get_gid();
// Compute range.
for (int i = 0; i < 3; i++) {
local_sum += array[(gid * 3) + i];
}
// Add to local sum.
printf("[%d] Adding %d to %d!\n", getpid(), local_sum, *sum);
*sum = *sum + local_sum;
// Barrier.
dsm_barrier();
// Print sum if process zero.
if (gid == 0) printf("Sum = %d\n", *sum);
// Exit.
dsm_exit();
}
Por que cada processo pode "ver" o valor correto no endereço 0x7fe953fcb000
no espaço compartilhado, mas depois de adicionar, se comporta como se o valor naquele endereço durante a adição ainda fosse zero?
Aqui está o que me preocupa sobre esse problema:
- Se fosse um problema de cache, como é possível imprimir o valor correto antes da operação aritmética e ainda está incorreto?
- Estou adicionando a um valor compartilhado no heap do processo. O compilador não poderia ter assumido que o valor ali seria zero e otimizou tudo.
Existe alguma explicação para por que isso pode acontecer sob o capô? Eu tentei usar o GDB com meu programa para ver o que está acontecendo. Mas, até onde eu sei, é simplesmente mover o valor dos endereços de memória para os registradores. Ainda não vejo nenhum problema de otimização.