Erro de compilação do GCC com> 2 GB de código

108

Tenho um grande número de funções, totalizando cerca de 2,8 GB de código-objeto (infelizmente não há como contornar, computação científica ...)

Quando tento vinculá-los, obtenho relocation truncated to fit: R_X86_64_32Serros (esperados) que esperava contornar especificando o sinalizador do compilador -mcmodel=medium. Todas as bibliotecas vinculadas, além das quais eu tenho controle, são compiladas com o -fpicsinalizador.

Mesmo assim, o erro persiste, e suponho que algumas bibliotecas que vinculo não sejam compiladas com PIC.

Aqui está o erro:

/usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../lib64/crt1.o: In function `_start':
(.text+0x12): relocation truncated to fit: R_X86_64_32S against symbol `__libc_csu_fini'     defined in .text section in /usr/lib64/libc_nonshared.a(elf-init.oS)
/usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../lib64/crt1.o: In function `_start':
(.text+0x19): relocation truncated to fit: R_X86_64_32S against symbol `__libc_csu_init'    defined in .text section in /usr/lib64/libc_nonshared.a(elf-init.oS)
/usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../lib64/crt1.o: In function `_start':
(.text+0x20): undefined reference to `main'
/usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../lib64/crti.o: In function    `call_gmon_start':
(.text+0x7): relocation truncated to fit: R_X86_64_GOTPCREL against undefined symbol      `__gmon_start__'
/usr/lib/gcc/x86_64-redhat-linux/4.1.2/crtbegin.o: In function `__do_global_dtors_aux':
crtstuff.c:(.text+0xb): relocation truncated to fit: R_X86_64_PC32 against `.bss' 
crtstuff.c:(.text+0x13): relocation truncated to fit: R_X86_64_32 against symbol `__DTOR_END__' defined in .dtors section in /usr/lib/gcc/x86_64-redhat-linux/4.1.2/crtend.o
crtstuff.c:(.text+0x19): relocation truncated to fit: R_X86_64_32S against `.dtors'
crtstuff.c:(.text+0x28): relocation truncated to fit: R_X86_64_PC32 against `.bss'
crtstuff.c:(.text+0x38): relocation truncated to fit: R_X86_64_PC32 against `.bss'
crtstuff.c:(.text+0x3f): relocation truncated to fit: R_X86_64_32S against `.dtors'
crtstuff.c:(.text+0x46): relocation truncated to fit: R_X86_64_PC32 against `.bss'
crtstuff.c:(.text+0x51): additional relocation overflows omitted from the output
collect2: ld returned 1 exit status
make: *** [testsme] Error 1

E as bibliotecas do sistema com as quais vinculo:

-lgfortran -lm -lrt -lpthread

Alguma pista de onde procurar o problema?

EDIT: Em primeiro lugar, obrigado pela discussão ... Para esclarecer um pouco, eu tenho centenas de funções (cada uma com aproximadamente 1 MB de tamanho em arquivos de objetos separados) como esta:

double func1(std::tr1::unordered_map<int, double> & csc, 
             std::vector<EvaluationNode::Ptr> & ti, 
             ProcessVars & s)
{
    double sum, prefactor, expr;

    prefactor = +s.ds8*s.ds10*ti[0]->value();
    expr =       ( - 5/243.*(s.x14*s.x15*csc[49300] + 9/10.*s.x14*s.x15*csc[49301] +
           1/10.*s.x14*s.x15*csc[49302] - 3/5.*s.x14*s.x15*csc[49303] -
           27/10.*s.x14*s.x15*csc[49304] + 12/5.*s.x14*s.x15*csc[49305] -
           3/10.*s.x14*s.x15*csc[49306] - 4/5.*s.x14*s.x15*csc[49307] +
           21/10.*s.x14*s.x15*csc[49308] + 1/10.*s.x14*s.x15*csc[49309] -
           s.x14*s.x15*csc[51370] - 9/10.*s.x14*s.x15*csc[51371] -
           1/10.*s.x14*s.x15*csc[51372] + 3/5.*s.x14*s.x15*csc[51373] +
           27/10.*s.x14*s.x15*csc[51374] - 12/5.*s.x14*s.x15*csc[51375] +
           3/10.*s.x14*s.x15*csc[51376] + 4/5.*s.x14*s.x15*csc[51377] -
           21/10.*s.x14*s.x15*csc[51378] - 1/10.*s.x14*s.x15*csc[51379] -
           2*s.x14*s.x15*csc[55100] - 9/5.*s.x14*s.x15*csc[55101] -
           1/5.*s.x14*s.x15*csc[55102] + 6/5.*s.x14*s.x15*csc[55103] +
           27/5.*s.x14*s.x15*csc[55104] - 24/5.*s.x14*s.x15*csc[55105] +
           3/5.*s.x14*s.x15*csc[55106] + 8/5.*s.x14*s.x15*csc[55107] -
           21/5.*s.x14*s.x15*csc[55108] - 1/5.*s.x14*s.x15*csc[55109] -
           2*s.x14*s.x15*csc[55170] - 9/5.*s.x14*s.x15*csc[55171] -
           1/5.*s.x14*s.x15*csc[55172] + 6/5.*s.x14*s.x15*csc[55173] +
           27/5.*s.x14*s.x15*csc[55174] - 24/5.*s.x14*s.x15*csc[55175] +
           // ...
           ;

        sum += prefactor*expr;
    // ...
    return sum;
}

O objeto sé relativamente pequeno e mantém as constantes necessárias x14, x15, ..., ds0, ..., etc. enquanto tiapenas retorna um duplo de uma biblioteca externa. Como você pode ver, csc[]é um mapa pré-computado de valores que também é avaliado em arquivos de objetos separados (novamente centenas com cerca de 1 MB de tamanho cada) da seguinte forma:

void cscs132(std::tr1::unordered_map<int,double> & csc, ProcessVars & s)
{
    {
    double csc19295 =       + s.ds0*s.ds1*s.ds2 * ( -
           32*s.x12pow2*s.x15*s.x34*s.mbpow2*s.mWpowinv2 -
           32*s.x12pow2*s.x15*s.x35*s.mbpow2*s.mWpowinv2 -
           32*s.x12pow2*s.x15*s.x35*s.x45*s.mWpowinv2 -
           32*s.x12pow2*s.x25*s.x34*s.mbpow2*s.mWpowinv2 -
           32*s.x12pow2*s.x25*s.x35*s.mbpow2*s.mWpowinv2 -
           32*s.x12pow2*s.x25*s.x35*s.x45*s.mWpowinv2 +
           32*s.x12pow2*s.x34*s.mbpow4*s.mWpowinv2 +
           32*s.x12pow2*s.x34*s.x35*s.mbpow2*s.mWpowinv2 +
           32*s.x12pow2*s.x34*s.x45*s.mbpow2*s.mWpowinv2 +
           32*s.x12pow2*s.x35*s.mbpow4*s.mWpowinv2 +
           32*s.x12pow2*s.x35pow2*s.mbpow2*s.mWpowinv2 +
           32*s.x12pow2*s.x35pow2*s.x45*s.mWpowinv2 +
           64*s.x12pow2*s.x35*s.x45*s.mbpow2*s.mWpowinv2 +
           32*s.x12pow2*s.x35*s.x45pow2*s.mWpowinv2 -
           64*s.x12*s.p1p3*s.x15*s.mbpow4*s.mWpowinv2 +
           64*s.x12*s.p1p3*s.x15pow2*s.mbpow2*s.mWpowinv2 +
           96*s.x12*s.p1p3*s.x15*s.x25*s.mbpow2*s.mWpowinv2 -
           64*s.x12*s.p1p3*s.x15*s.x35*s.mbpow2*s.mWpowinv2 -
           64*s.x12*s.p1p3*s.x15*s.x45*s.mbpow2*s.mWpowinv2 -
           32*s.x12*s.p1p3*s.x25*s.mbpow4*s.mWpowinv2 +
           32*s.x12*s.p1p3*s.x25pow2*s.mbpow2*s.mWpowinv2 -
           32*s.x12*s.p1p3*s.x25*s.x35*s.mbpow2*s.mWpowinv2 -
           32*s.x12*s.p1p3*s.x25*s.x45*s.mbpow2*s.mWpowinv2 -
           32*s.x12*s.p1p3*s.x45*s.mbpow2 +
           64*s.x12*s.x14*s.x15pow2*s.x35*s.mWpowinv2 +
           96*s.x12*s.x14*s.x15*s.x25*s.x35*s.mWpowinv2 +
           32*s.x12*s.x14*s.x15*s.x34*s.mbpow2*s.mWpowinv2 -
           32*s.x12*s.x14*s.x15*s.x35*s.mbpow2*s.mWpowinv2 -
           64*s.x12*s.x14*s.x15*s.x35pow2*s.mWpowinv2 -
           32*s.x12*s.x14*s.x15*s.x35*s.x45*s.mWpowinv2 +
           32*s.x12*s.x14*s.x25pow2*s.x35*s.mWpowinv2 +
           32*s.x12*s.x14*s.x25*s.x34*s.mbpow2*s.mWpowinv2 -
           32*s.x12*s.x14*s.x25*s.x35pow2*s.mWpowinv2 -
           // ...

       csc.insert(cscMap::value_type(192953, csc19295));
    }

    {
       double csc19296 =      // ... ;

       csc.insert(cscMap::value_type(192956, csc19296));
    }

    // ...
}

É sobre isso. A etapa final então consiste apenas em chamar todos aqueles func[i]e somar o resultado.

Quanto ao fato de que este é um caso bastante especial e incomum: Sim, é. É com isso que as pessoas precisam lidar ao tentar fazer cálculos de alta precisão para a física de partículas.

EDIT2: Devo também acrescentar que x12, x13, etc. não são realmente constantes. Eles são definidos com valores específicos, todas essas funções são executadas e o resultado retornado e, em seguida, um novo conjunto de x12, x13, etc. é escolhido para produzir o próximo valor. E isso deve ser feito 10 ^ 5 a 10 ^ 6 vezes ...

EDIT3: Obrigado pelas sugestões e pela discussão até agora ... Vou tentar rolar os loops na geração de código de alguma forma, não tenho certeza de como fazer isso exatamente, para ser honesto, mas esta é a melhor aposta.

BTW, eu não tentei me esconder atrás de "isso é computação científica - nenhuma maneira de otimizar". É que a base desse código é algo que sai de uma "caixa preta" à qual não tenho acesso real e, além disso, a coisa toda funcionou muito bem com exemplos simples, e principalmente me sinto sobrecarregado com o que acontece em uma realidade aplicação mundial ...

EDIT4: Então, consegui reduzir o tamanho do código das cscdefinições em cerca de um quarto, simplificando as expressões em um sistema de álgebra computacional ( Mathematica ). Vejo agora também uma maneira de reduzi-lo em outra ordem de magnitude ou mais, aplicando alguns outros truques antes de gerar o código (o que reduziria essa parte para cerca de 100 MB) e espero que essa ideia funcione.

Agora relacionado às suas respostas: Estou tentando rolar os loops de novo no funcs, onde um CAS não vai ajudar muito, mas já tenho algumas idéias. Por exemplo, classificando as expressões por variáveis ​​como x12, x13,..., analise os cscs com Python e gere tabelas que os relacionem entre si. Então, posso pelo menos gerar essas partes como loops. Como esta parece ser a melhor solução até agora, eu a considero a melhor resposta.

No entanto, gostaria de dar crédito também a VJo. O GCC 4.6 realmente funciona muito melhor, produz código menor e é mais rápido. Usar o modelo grande funciona no código como está. Então, tecnicamente, essa é a resposta correta, mas mudar todo o conceito é uma abordagem muito melhor.

Obrigado a todos por suas sugestões e ajuda. Se alguém tiver interesse, postarei o resultado final assim que estiver pronto.

OBSERVAÇÕES: Apenas algumas observações para algumas outras respostas: O código que estou tentando executar não se origina em uma expansão de funções / algoritmos simples e estúpidos desdobramentos desnecessários. O que realmente acontece é que começamos com objetos matemáticos bastante complicados e trazê-los para uma forma computável numericamente gera essas expressões. O problema está, na verdade, na teoria física subjacente. A complexidade das expressões intermediárias escala fatorialmente, o que é bem conhecido, mas quando combinamos tudo isso com algo fisicamente mensurável - um observável - ela se reduz a apenas um punhado de funções muito pequenas que formam a base das expressões. (Definitivamente, há algo "errado" a esse respeito com o geral e apenas disponívelansatz que é chamado de "teoria da perturbação". Tentamos trazer este ansatz para outro nível, que não é mais viável analiticamente e onde a base das funções necessárias não é conhecida. Então, tentamos usar a força bruta assim. Não é a melhor maneira, mas espero que ajude no nosso entendimento da física em questão no final ...

ÚLTIMA EDIÇÃO: Obrigado a todas as suas sugestões, eu consegui reduzir o tamanho do código consideravelmente, usando o Mathematica e uma modificação do gerador de código para os funcs um pouco ao longo das linhas da resposta principal :)

Simplifiquei as cscfunções com o Mathematica, diminuindo para 92 MB. Esta é a parte irredutível. As primeiras tentativas demoraram uma eternidade, mas depois de algumas otimizações, isso agora é executado em cerca de 10 minutos em uma única CPU.

O efeito sobre os funcs foi dramático: o tamanho do código inteiro para eles caiu para aproximadamente 9 MB, então o código agora totaliza na faixa de 100 MB. Agora faz sentido ativar as otimizações e a execução é bastante rápida.

Mais uma vez, obrigado a todos por suas sugestões, aprendi muito.

bbtrb
fonte
17
Se você tiver tantos dados, deve removê-los dos arquivos de origem e, em vez disso mmap, retirá- los de um binário externo no tempo de execução.
R .. GitHub PARAR DE AJUDAR O ICE
3
Você poderia dar um exemplo de uma (ou duas) dessas funções? Isso realmente parece estranho. Você também pode carregar essas funções dinamicamente com a função dl *.
Patrick Schlüter
7
@bbtrb: Meu primeiro instinto é semelhante ao de R .., parece um problema de design. Admito, não sei o que é comum nos círculos de computação científica, mas nunca ouvi falar de alguém tentando vincular um arquivo de objeto de 2,8 GB ou qualquer coisa remotamente próxima a ele, e não tenho certeza se o GCC realmente apoiaria isso. Francamente, eu esperaria que qualquer blob de código desse tamanho fosse puro espaguete.
Nicholas Knight
46
não há absolutamente nenhuma maneira de a solução ideal para o problema envolver 2 GB de arquivo objeto.
David Heffernan
35
não coloque seus dados no código
David Heffernan

Respostas:

53

Então, você já tem um programa que produz este texto:

prefactor = +s.ds8*s.ds10*ti[0]->value();
expr = ( - 5/243.*(s.x14*s.x15*csc[49300] + 9/10.*s.x14*s.x15*csc[49301] +
       1/10.*s.x14*s.x15*csc[49302] - 3/5.*s.x14*s.x15*csc[49303] -...

e

double csc19295 =       + s.ds0*s.ds1*s.ds2 * ( -
       32*s.x12pow2*s.x15*s.x34*s.mbpow2*s.mWpowinv2 -
       32*s.x12pow2*s.x15*s.x35*s.mbpow2*s.mWpowinv2 -
       32*s.x12pow2*s.x15*s.x35*s.x45*s.mWpowinv2 -...

certo?

Se todas as suas funções tiverem um "formato" semelhante (multiplique n números m vezes e adicione os resultados - ou algo semelhante), então acho que você pode fazer isso:

  • mude o programa gerador para produzir offsets em vez de strings (ou seja, em vez da string "s.ds0" ele irá produzir offsetof(ProcessVars, ds0)
  • criar uma série de tais compensações
  • escreva um avaliador que aceite o array acima e os endereços de base dos ponteiros da estrutura e produza um resultado

O avaliador array + representará a mesma lógica de uma de suas funções, mas apenas o avaliador será código. A matriz é "dados" e pode ser gerada em tempo de execução ou salva em disco e ler pedaços i ou com um arquivo mapeado na memória.

Para o seu exemplo particular em func1 imaginar como você iria reescrever a função através de um avaliador se você tivesse acesso ao endereço de base se csce também um vector como representação das constantes e os deslocamentos que você precisa para adicionar aos endereços base para chegar a x14, ds8ecsc[51370]

Você precisa criar uma nova forma de "dados" que descreverá como processar os dados reais que você passa para seu grande número de funções.

Andrei
fonte
45

O x86-64 ABI usado pelo Linux define um "modelo grande" especificamente para evitar tais limitações de tamanho, que inclui tipos de realocação de 64 bits para GOT e PLT. (Consulte a tabela na seção 4.4.2 e as sequências de instruções em 3.5.5 que mostram como são usadas.)

Como suas funções ocupam 2,8 GB, você está sem sorte, porque o gcc não oferece suporte a modelos grandes. O que você pode fazer é reorganizar seu código de forma que permita dividi-lo em bibliotecas compartilhadas que você vincularia dinamicamente.

Se isso não for possível, como alguém sugeriu, em vez de colocar seus dados em código (compilando e vinculando), por ser enorme, você pode carregá-lo em tempo de execução (tanto como um arquivo normal, ou você pode mmapá-lo).

EDITAR

Parece que o modelo grande é compatível com o gcc 4.6 (consulte esta página ). Você pode tentar isso, mas o acima ainda se aplica à reorganização de seu código.

BЈовић
fonte
Então, o que você está dizendo é que, quando agruparia os arquivos-objeto em várias pequenas bibliotecas compartilhadas, superaria as limitações?
bbtrb
3
@bbtrb Certo. Mas eu ainda procuraria outra forma de implementar suas funções. Aposto que sua compilação leva uma eternidade
BЈовић
18
WTF? Este código deve ser gerado por algum script; ninguém escreve megabytes de código manualmente! A mesma lógica que gera o código também pode ser usada para executar o cálculo.
zvrba
6
Eu recomendo fortemente tentar o gcc 4.6, é muito provável que produza um código superior para este programa do que o gcc 4.1; pode até ser capaz de comprimir a coisa toda em 2 GB sem que você tenha que fazer nada inteligente, eliminando o problema (tente combinações de -Os, -fwhole-program e -flto - com este volume de código, otimizar o tamanho é otimizando para velocidade). No entanto, se isso não ajudar o suficiente, você também deve estar ciente de que para o modelo grande funcionar, você terá que reconstruir pelo menos parte da biblioteca C no modelo grande (crt * .o, libc_nonshared.a e libpthread_nonshared.a).
zwol
1
@bdonlan O link estático também é uma possibilidade.
zvrba
37

Com um programa desse lado, as perdas de cache para o código muito provavelmente excederão os custos de loop em tempo de execução. Eu recomendo que você volte ao seu gerador de código e faça com que ele gere alguma representação compacta para o que deseja avaliar (ou seja, uma que provavelmente caiba no D-cache) e, em seguida, execute-a com um interpretador em seu programa. Você também pode ver se pode fatorar kernels menores que ainda têm um número significativo de operações e, em seguida, usá-los como 'instruções' no código interpretado.

bdonlan
fonte
21

O erro ocorre porque você tem muito CODE, não dados! Isso é indicado, por exemplo __libc_csu_fini(que é uma função) sendo referenciado de _starte a realocação é truncada para caber. Isso significa que _start(o verdadeiro ponto de entrada do programa) está tentando chamar essa função por meio de um deslocamento SIGNED de 32 bits, que possui apenas um intervalo de 2 GB. Como a quantidade total de seu código-objeto é de ~ 2,8 GB, verifique os fatos.

Se você pudesse reprojetar suas estruturas de dados, muito do seu código poderia ser "compactado" reescrevendo as grandes expressões como loops simples.

Além disso, você pode calcular csc[]em um programa diferente, armazenar os resultados em um arquivo e apenas carregá-los quando necessário.

zvrba
fonte
Você poderia fornecer um exemplo de como você reescreveria as funções com loops simples? Eu não te sigo exatamente. csc[]tem que ser calculado com muita frequência e eu gostaria de evitar E / S de disco.
bbtrb
4
@bbtr: Por exemplo, para func1 cima, algo como: for (int i = 0; i < N; ++i) expr += constants[i].*s.x14*s.x15*csc[49300 + i];.
HighCommander4
@ HighCommander4: absolutamente, eu concordo. Está bem acima da minha cabeça como gerar algo assim automaticamente. Talvez com uma matriz separada que armazena os índices ...
bbtrb
2
@bbtrb: Já que não há nenhuma maneira estranha de alguém escrever fonte suficiente para produzir 2,8 GB de código de objeto manualmente, especialmente com esses nomes de símbolo não mnemônicos, um gerador de código deve ter sido usado. Trabalhe com isso.
Donal Fellows de
15

Acho que todos concordam que deve haver uma maneira diferente de fazer o que você deseja. Compilar centenas de megabytes (gigabytes?) De código, vinculá-lo a um executável de vários gigabytes e executá-lo parece muito ineficiente.

Se entendi seu problema corretamente, você usa algum tipo de gerador de código, G, para gerar um monte de funções func1...Nque recebem um monte de mapas csc1...Mcomo entrada. O que você quer fazer é calcular csc1...Me executar um loop de 1.000.000 de vezes para diferentes entradas e cada vez que encontrar s = func1 + func2 + ... + funcN. Você não especificou como fucn1...Nestão relacionados ao csc1...Membora.

Se tudo isso for verdade, parece que você deve ser capaz de virar o problema de cabeça para baixo de uma maneira que pode ser potencialmente muito mais gerenciável e até possivelmente mais rápida (ou seja, permitindo que o cache de sua máquina realmente funcione).

Além do problema prático dos tamanhos dos arquivos de objeto, seu programa atual não será eficiente, uma vez que não localiza o acesso aos dados (muitos mapas enormes) e não tem execução de código localizado (muitas funções muito longas).

Que tal dividir seu programa em 3 fases: Fase 1, construir csc1...Me armazená-los. A fase 2 constrói um de funccada vez, executa 1.000.000 de vezes com cada entrada e armazena os resultados. A Fase 3 encontre a soma dos resultados dos func1...Nresultados armazenados para cada execução de 1.000.000 vezes. A parte boa dessa solução é que ela pode ser facilmente feita em paralelo em várias máquinas independentes.

Edit: @bbtrb, você poderia disponibilizar uma função e um csc em algum lugar? Eles parecem ser altamente regulares e compressíveis. Por exemplo, func1 parece ser apenas uma soma de expressões, cada uma consistindo em 1 coeficiente, 2 índices para as variáveis ​​em s e 1 índice em csc. Portanto, pode ser reduzido a um belo loop. Se você disponibilizar exemplos completos, tenho certeza de que podem ser encontradas maneiras de compactá-los em loops em vez de em expressões longas.

AlefSin
fonte
Sim, você entendeu corretamente :) No entanto, há vários problemas com sua sugestão: 1. os piores funcs dependem de quase todos os cscs e esses números também devem ser calculados 10 ^ 6 vezes. 2. A entrada será obtida de um integrador Monte Carlo adaptativo, o que significa que o integrador deve saber o resultado completo em cada ponto para poder reduzir o erro resultante refinando a malha nas proximidades do ponto, se necessário. 3. As expressões grandes para cscpersist ...
bbtrb de
1
Então, isso significa que você não pode calcular cada uma cscem cada iteração independentemente das outras? Se eles fossem independentes, você ainda poderia executar cada um 10 ^ 6 vezes e armazenar os resultados. No entanto, se houver dependências entre eles, talvez você precise descobrir qual está relacionada a qual, algo como um gráfico de dependência, e então tentar ver se você pode dividi-lo em vários subgráficos independentes. Em suma, acho que a chave é dividir o problema em subproblemas múltiplos e independentes.
AlefSin de
5

Se eu ler seus erros corretamente, o que o faz ultrapassar o limite é a seção de dados inicializados (se fosse o código, você teria muito mais erros IMHO). Você tem grandes matrizes de dados globais? Se for o caso, reestruturarei o programa para que sejam alocados dinamicamente. Se os dados forem inicializados, eu os leria de um arquivo de configuração.

BTW vendo isso:

(.text + 0x20): referência indefinida para `principal '

Acho que você tem outro problema.

AProgrammer
fonte
1
Sim, você está certo, erro estúpido, mas não resolve os outros erros.
bbtrb
3

Parece-me que o código está fazendo integração numérica usando algum tipo de método de profundidade adaptativo. Infelizmente, o gerador de código (ou melhor, o autor do gerador de código) é tão estúpido a ponto de gerar uma função por patch em vez de uma por tipo de patch. Como tal, ele produziu muito código para ser compilado e, mesmo que pudesse ser compilado, sua execução seria dolorosa porque nada é compartilhado em qualquer lugar. (Você pode imaginar a dor resultante de ter que carregar cada página de código-objeto do disco porque nada é compartilhado e, portanto, é sempre um candidato para o sistema operacional despejar. Para não falar dos caches de instrução, que serão inúteis.)

A solução é parar de desenrolar tudo; para este tipo de código, você deseja maximizar o compartilhamento já que a sobrecarga de instruções extras para acessar dados em padrões mais complexos será absorvida pelo custo de lidar com o (presumivelmente) grande conjunto de dados subjacente de qualquer maneira. Também é possível que o gerador de código até faça isso por padrão, e que o cientista tenha visto algumas opções para desenrolar (com a observação de que isso às vezes melhora a velocidade) e ligue-as todas de uma vez e agora está insistindo que essa bagunça resultante seja aceita pelo computador, em vez de aceitar as restrições reais da máquina e usar a versão numericamente correta que é gerada por padrão. Mas se o gerador de código não funcionar, obtenha um que o faça (ou hackear o código existente).

O resultado final:Resumindo compilar e vincular 2.8 GB de código não funciona e não deve ser forçado a funcionar. Encontre outra maneira.

Donal Fellows
fonte
3

Algumas sugestões: - Otimize para o tamanho (-Os). Faça suas chamadas de função inline, chamadas de função normais. Habilite o agrupamento de strings.

Tente dividir as coisas em diferentes DLLs (objetos compartilhados, .so para linux, .dylib para Mac OS X). Certifique-se de que eles podem ser descarregados. Em seguida, implemente algo para carregar coisas sob demanda e liberte-as quando não for necessário.

Se não, divida seu código em diferentes executáveis ​​e use algo para se comunicar entre eles (pipes, sockets, até mesmo escrever / ler em arquivo). Desajeitado, mas quais opções você tem?

Totalmente alternativo: - Use uma linguagem dinâmica com JIT . Bem em cima da minha cabeça - usar LuaJIT - e reescrever (regenerar?) Muitas dessas expressões em Lua , ou outras linguagens e tempos de execução que permitem que o código seja coletado como lixo.

LuaJIT é bastante eficiente, às vezes superando C / C ++ em certas coisas, mas geralmente muito próximo (às vezes pode ser lento devido à coleta de lixo deficiente que ainda existe). Verifique você mesmo:

http://luajit.org/performance_x86.html

Baixe o scimark2.luaarquivo de lá e compare-o com a versão "C" (google it) - geralmente os resultados são muito próximos.

Malkia
fonte
2

O vinculador está tentando gerar deslocamentos de realocação de 32 bits em um binário que de alguma forma excedeu essas limitações. Tente reduzir os requisitos de espaço de endereço do programa principal.

Você pode dividir parte / a maioria do código-objeto em uma ou mais bibliotecas (também compiladas com -fpic / -fPIC)? Em seguida, gere um binário não estático vinculado a essas bibliotecas. As bibliotecas viverão em blocos de memória discretos e seus deslocamentos de realocação serão dinâmicos / absolutos (64 bits) em vez de relativos (32 bits).

ajklbahu8geag
fonte
2

Essas expressões parecem muito com uma série alternada para mim. Não sei como o resto do código se parece, mas não parece que seja tão difícil derivar a expressão geradora. Provavelmente valeria a pena em tempo de execução também, especialmente se você tiver 2,8 GB de código desenrolado de 2 KB.

Brian
fonte
1

Isso parece o resultado da geração de código que deu errado, talvez por álgebra simbólica e / ou desenrolamento manual. As manipulações simbólicas são bem conhecidas por crescer exponencialmente na profundidade da árvore de expressão ou gráfico computacional. É provável que a diferenciação automática possa ser usada aqui, o que tornaria o tamanho do código bastante pequeno e também aceleraria drasticamente a execução.

Jed
fonte