O tópico resolve () do tatu é seguro?

86

Em meu código, tenho um loop no qual construo um sistema linear determinado e tento resolvê-lo:

#pragma omp parallel for
for (int i = 0; i < n[0]+1; i++) {
    for (int j = 0; j < n[1]+1; j++) {
        for (int k = 0; k < n[2]+1; k++) {
            arma::mat A(max_points, 2);
            arma::mat y(max_points, 1);
            // initialize A and y

            arma::vec solution = solve(A,y);
        }
    }
}

Às vezes, de forma bastante aleatória, o programa trava ou os resultados no vetor de solução são NaN. E se eu colocar isso:

arma::vec solution;
#pragma omp critical 
{
    solution = solve(weights*A,weights*y);
}

então, esses problemas parecem não acontecer mais.

Quando ele trava, isso acontece porque alguns threads estão esperando na barreira OpenMP:

Thread 2 (Thread 0x7fe4325a5700 (LWP 39839)):
#0  0x00007fe44d3c2084 in gomp_team_barrier_wait_end () from /usr/lib64/gcc-4.9.2/lib64/gcc/x86_64-redhat-linux-gnu/4.9.2/libgomp.so.1
#1  0x00007fe44d3bf8c2 in gomp_thread_start () at ../.././libgomp/team.c:118
#2  0x0000003f64607851 in start_thread () from /lib64/libpthread.so.0
#3  0x0000003f642e890d in clone () from /lib64/libc.so.6

E os outros fios estão presos dentro do Armadillo:

Thread 1 (Thread 0x7fe44afe2e60 (LWP 39800)):
#0  0x0000003ee541f748 in dscal_ () from /usr/lib64/libblas.so.3
#1  0x00007fe44c0d3666 in dlarfp_ () from /usr/lib64/atlas/liblapack.so.3
#2  0x00007fe44c058736 in dgelq2_ () from /usr/lib64/atlas/liblapack.so.3
#3  0x00007fe44c058ad9 in dgelqf_ () from /usr/lib64/atlas/liblapack.so.3
#4  0x00007fe44c059a32 in dgels_ () from /usr/lib64/atlas/liblapack.so.3
#5  0x00007fe44f09fb3d in bool arma::auxlib::solve_ud<double, arma::Glue<arma::Mat<double>, arma::Mat<double>, arma::glue_times> >(arma::Mat<double>&, arma::Mat<double>&, arma::Base<double, arma::Glue<arma::Mat<double>, arma::Mat<double>, arma::glue_times> > const&) () at /usr/include/armadillo_bits/lapack_wrapper.hpp:677
#6  0x00007fe44f0a0f87 in arma::Col<double>::Col<arma::Glue<arma::Glue<arma::Mat<double>, arma::Mat<double>, arma::glue_times>, arma::Glue<arma::Mat<double>, arma::Mat<double>, arma::glue_times>, arma::glue_solve> >(arma::Base<double, arma::Glue<arma::Glue<arma::Mat<double>, arma::Mat<double>, arma::glue_times>, arma::Glue<arma::Mat<double>, arma::Mat<double>, arma::glue_times>, arma::glue_solve> > const&) ()
at /usr/include/armadillo_bits/glue_solve_meat.hpp:39

Como você pode ver no rastreamento de pilha, minha versão do Armadillo usa o atlas. E de acordo com este atlas de documentação parece ser thread-safe: ftp://lsec.cc.ac.cn/netlib/atlas/faq.html#tsafe

Atualização 11/09/2015

Finalmente consegui algum tempo para fazer mais testes, com base nas sugestões de Vladimir F.

Quando eu compilo tatu com o BLAS do ATLAS, ainda consigo reproduzir o trava e o NaNs. Quando ele trava, a única coisa que muda no stacktrace é a chamada para o BLAS:

#0  0x0000003fa8054718 in ATL_dscal_xp1yp0aXbX@plt () from /usr/lib64/atlas/libatlas.so.3
#1  0x0000003fb05e7666 in dlarfp_ () from /usr/lib64/atlas/liblapack.so.3
#2  0x0000003fb0576a61 in dgeqr2_ () from /usr/lib64/atlas/liblapack.so.3
#3  0x0000003fb0576e06 in dgeqrf_ () from /usr/lib64/atlas/liblapack.so.3
#4  0x0000003fb056d7d1 in dgels_ () from /usr/lib64/atlas/liblapack.so.3
#5  0x00007ff8f3de4c34 in void arma::lapack::gels<double>(char*, int*, int*, int*, double*, int*, double*, int*, double*, int*, int*) () at /usr/include/armadillo_bits/lapack_wrapper.hpp:677
#6  0x00007ff8f3de1787 in bool arma::auxlib::solve_od<double, arma::Glue<arma::Mat<double>, arma::Mat<double>, arma::glue_times> >(arma::Mat<double>&, arma::Mat<double>&, arma::Base<double, arma::Glue<arma::Mat<double>, arma::Mat<double>, arma::glue_times> > const&) () at /usr/include/armadillo_bits/auxlib_meat.hpp:3434

Compilando sem ATLAS, apenas com netlib BLAS e LAPACK, consegui reproduzir os NaNs, mas não os trava.

Em ambos os casos, rodeando solve()com #pragmaomp crítico, não tenho nenhum problema

maxdebayser
fonte
1
/Usr/lib64/libblas.so.3 faz parte do atlas? Por que ele não está localizado em / usr / lib64 / atlas?
Vladimir F
1
Não, no opensuse, faz parte do pacote liblas3 e no redhat faz parte do pacote blas.
maxdebayser
2
Então você não pode usar nenhuma garantia do ATLAS quando usar o BLAS padrão.
Vladimir F
2
Você resolveu isso? Se não, quais pacotes estão instalados e você poderia postar o comando usado para compilar o programa?
vindvaki
3
Você também pode tentar usar o OpenBLAS em vez do Atlas.
mtall

Respostas:

2

Tem certeza de que seus sistemas estão sobredeterminados? solve_udem seu rastreamento de pilha diz o contrário. Embora você solve_odtambém tenha , e provavelmente isso não tem nada a ver com o problema. Mas não custa descobrir por que isso está acontecendo e consertar se você acha que os sistemas deveriam estar errados.

O tópico resolve () do tatu é seguro?

Que eu acho que depende de sua versão LAPACK, veja também esta . Olhando para o código de solve_odtodas as variáveis ​​acessadas parece ser local. Observe o aviso no código:

NOTA: a função dgels () na biblioteca lapack fornecida pelo ATLAS 3.6 parece ter problemas

Assim, parece que só lapack::gelspode causar problemas para você. Se consertar o lapack não for possível, uma solução alternativa é empilhar seus sistemas e resolver um único sistema grande. Isso provavelmente seria ainda mais eficiente se seus sistemas individuais fossem pequenos.

Formiga-de-fogo
fonte
1

A segurança de thread da solve()função do Armadillo depende (apenas) da biblioteca BLAS que você usa. As implementações LAPACK são thread-safe quando o BLAS é. A solve()função Armadillo não é thread-safe ao vincular à biblioteca BLAS de referência . No entanto, é seguro para threads ao usar o OpenBLAS . Além disso, o ATLAS fornece uma implementação BLAS que também menciona que é thread-safe , e o Intel MKL também é thread-safe , mas não tenho experiência com Armadillo vinculado a essas bibliotecas.

Claro, isso só se aplica quando você executa a solve()partir de vários threads com dados diferentes.

André Offringa
fonte