Pergunta

Abaixo está o meu invólucro C para uma rotina Fortran ZHEEVR da conhecida biblioteca LAPACK numérica:

void zheevr(char jobz, char range, char uplo, int n, doublecomplex* a, int lda, double vl, double vu, int il, int iu, double abstol, double* w, doublecomplex* z, int ldz, int* info)
{
    int m;
    int lwork = -1;
    int liwork = -1;
    int lrwork = -1;
    int* isuppz = alloc_memory(sizeof(int) * 2 * n);
    zheevr_(&jobz, &range, &uplo, &n, a, &lda, &vl, &vu, &il, &iu, &abstol, &m, w, z, &ldz, isuppz, small_work_doublecomplex, &lwork, small_work_double, &lrwork, small_work_int, &liwork, &info);
    lwork = (int) small_work_doublecomplex[0].real;
    liwork = small_work_int[0];
    lrwork = (int) small_work_double[0];
    doublecomplex* work = alloc_memory(sizeof(doublecomplex) * lwork);
    double* rwork = alloc_memory(sizeof(double) * lrwork);
    int* iwork = alloc_memory(sizeof(int) * liwork);
    zheevr_(&jobz, &range, &uplo, &n, a, &lda, &vl, &vu, &il, &iu, &abstol, &m, w, z, &ldz, isuppz, work, &lwork, rwork, &lrwork, iwork, &liwork, info);
    free(iwork);
    free(rwork);
    free(work);
    free(isuppz);
}

Esta função é chamada de centenas de milhares de vezes na minha candidatura, diagonalizar a matriz complexa "a" (nomes de parâmetros seguem a convenção Fortran para esta função) para o mesmo tamanho da matriz. Eu acho que os tamanhos de matrizes de trabalho será o mesmo na maioria das vezes, como as matrizes diagonalizado será da mesma estrutura. Minhas perguntas são:

  1. Pode o alloc repetida / livre ( "alloc_memory" é um wrapper simples em torno malloc de glibc) chama desempenho ferido, e como mal?
  2. Será que a ordem de de livres matéria? Devo libertar a última matriz alocada primeiro ou último?
Foi útil?

Solução

  • Você pode usar C99? (Resposta: Sim., Você já está usando notações C99 - declarar variáveis ??quando necessário)
  • Os tamanhos do sane matrizes (não muito grande)?

Se ambas as respostas são 'sim', considere o uso de VLA - matrizes de comprimento variável:

void zheevr(char jobz, char range, char uplo, int n, doublecomplex* a, int lda, double vl, double vu, int il, int iu, double abstol, double* w, doublecomplex* z, int ldz, int* info)
{
    int m;
    int lwork = -1;
    int liwork = -1;
    int lrwork = -1;
    int isuppz[2*n];
    zheevr_(&jobz, &range, &uplo, &n, a, &lda, &vl, &vu, &il, &iu, &abstol, &m, w, z, &ldz, isuppz, small_work_doublecomplex, &lwork, small_work_double, &lrwork, small_work_int, &liwork, &info);
    lwork = (int) small_work_doublecomplex[0].real;
    liwork = small_work_int[0];
    lrwork = (int) small_work_double[0];
    doublecomplex work[lwork];
    double rwork[lrwork];
    int iwork[liwork];
    zheevr_(&jobz, &range, &uplo, &n, a, &lda, &vl, &vu, &il, &iu, &abstol, &m, w, z, &ldz, isuppz, work, &lwork, rwork, &lrwork, iwork, &liwork, info);
}

Uma coisa agradável sobre o uso VLAs é que não há libertação de ser feito por você.

(código não testado!)

Outras dicas

1) Sim, eles podem.

2) Qualquer sane libc não deve se preocupar com a ordem de free (). Em termos de desempenho que não deve importar muito.

Eu recomendo a remoção de gerenciamento de memória desta função - assim chamada será fornecer o tamanho da matriz e buffers temporários alocados. Isso vai reduzir número de mallocs significativamente se esta função é chamada a partir mesmo lugar na matriz do mesmo tamanho.

Ele certamente vai afetar o desempenho - quanto Youb só pode saber com certeza por timing. Para criar uma versão que evita a maioria atribuições, atribuir a um ponteiro estático e lembre-se o tamanho em outro inteiro estático. Se a próxima chamada usa o mesmo tamanho, apenas reutilizar o que foi alocado última vez. Apenas livre qualquer coisa quando você precisa criar uma nova matriz porque o tamanho mudou.

Nota esta solução só é adequada para o código de segmento único.

Tudo bem. Você está indo para obter o profiler resposta em breve. Se você tem uma máquina AMD, eu recomendo fortemente CodeAnalyst a livre da AMD.

Quanto ao seu problema de memória, eu acho que você poderia trabalhar com gerenciamento de memória local neste caso. Apenas determinar o número máximo de memória que você pode alocar para esta função. Em seguida, você declarar um buffer estático e você trabalhar com ele um pouco como como um compilador alças da pilha. Eu fiz um invólucro como esta sobre VirtualAlloc uma vez e é muito rápido.

Se você está alocando os mesmos itens de tamanho centenas de milhares de vezes, então por que não apenas manter um montão de seus objetos (uma vez que estes parecem ser relativamente simples, ou seja, não contêm ponteiros para outra memória alocada) e livre para sua própria pilha (ou pilha na verdade)?

A pilha pode preguiçosamente alocar novos objetos usando o malloc simplista, mas quando liberando apenas empurrar o item para a pilha. Quando você precisa alocar, se há um objeto liberado disponível, pode simplesmente alocar esse.

Este também irá poupar-lhe várias chamadas para alocação (desde que você não vai precisar fazer qualquer alocação e parece que sua rotina faz várias chamadas para malloc) e também irá evitar a fragmentação (até certo ponto), pelo menos na re -memoria usada. É claro que as dotações iniciais (e outras atribuições que o programa está em execução quando ele precisa expandir esta memória) pode causar fragmentação, mas se você está realmente preocupado com isso, você pode executar algumas estatísticas e encontrar o tamanho médio / max / típico de seu pilha durante as corridas e pré-alocar isso de uma vez quando o programa é iniciado, evitando a fragmentação.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top