Pergunta

Eu escrevi um conversor que leva arquivos xml OpenStreetMap e os converte para um formato de renderização em tempo de execução binário que é tipicamente cerca de 10% do tamanho original. tamanhos de arquivo de entrada são normalmente 3GB e maior. Os arquivos de entrada não são carregados na memória de uma só vez, mas transmitido como pontos e polígonos são coletados, em seguida, um bsp é executado sobre eles e o arquivo está de saída. Recentemente em arquivos maiores que fique sem memória e matrizes (aquele em questão tem 14 milhões pontos e 1 milhão de polígonos). Normalmente o meu programa está usando cerca de 1 GB para 1,2 GB de RAM quando isso acontece. Eu tentei aumentar a memória virtual de 2 a 8 GB (no XP), mas esta mudança não fez nenhum efeito. Além disso, uma vez que este código é open-source que eu gostaria de tê-lo trabalhar independentemente da RAM disponível (embora mais lento), ele roda em Windows, Linux e Mac.

Que técnicas posso usar para evitar ter que ficar sem memória? Processamento dos dados em pequenos sub-conjuntos e, em seguida, a fusão dos resultados finais? Usando o meu próprio tipo de memória virtual de manipulador? Quaisquer outras ideias?

Foi útil?

Solução

Em primeiro lugar, em um sistema de 32 bits, você será sempre limitada a 4 GB de memória, nenhuma configuração importa arquivo de paginação. (E desses, apenas a 2 GB estará disponível para o seu processo no Windows. No Linux, você normalmente tem em torno de 3 GB disponível)

Assim, a primeira solução óbvia é mudar para um SO de 64 bits, e compilar o seu pedido de 64-bit. Isso dá-lhe um enorme espaço de memória virtual para uso, e o sistema operacional irá trocar dados dentro e fora do arquivo de paginação conforme necessário para manter as coisas funcionando.

Em segundo lugar, alocando pequenos pedaços de memória ao mesmo tempo pode ajudar. Muitas vezes é mais fácil encontrar 4 256 pedaços de memória livre do que um pedaço de 1GB.

Em terceiro lugar, dividir o problema. Não processar todo o conjunto de dados de uma só vez, mas tentar carregar e processar apenas uma pequena seção de cada vez.

Outras dicas

Você verificou para garantir que não estão vazando qualquer lugar memória?

Uma vez que o seu programa é portátil para Linux, eu sugiro que execute-o sob Valgrind para certificar-se.

Parece que você já está fazendo um SAX abordagem ao processamento XML ( carregar o XML como você ir em vez de tudo de uma vez).

A solução é quase sempre a mudar o algoritmo para que ele corta o problema em partes menores. Fisicamente não alocar o máximo de memória de uma só vez, leia em apenas o que você precisa, processá-lo, em seguida, escrevê-lo.

Você pode, por vezes, estender a memória através de usar o disco rígido em vez quando necessário em seu algoritmo.

Se você não pode dividir o seu algoritmo, você provavelmente vai querer algo como arquivos de memória mapeada .

Na pior das hipóteses, você pode tentar usar algo como VirtualAlloc se você estiver em um sistema Windows. Se você estiver em um sistema de 32 bits, você pode tentar usar algo como Physical Address Extension (PAE) .

Você também pode considerar colocando limitações de entrada para o seu programa, e ter um diferente para sistemas de 32 bits e 64 bits.

Eu suspeito que seus problemas de memória são de manter a árvore BSP na memória. Portanto, manter o BSP no disco e manter apenas alguns pedaços na memória. Este deve ser bastante fácil com BSP, como a estrutura presta-se mais do que algumas outras estruturas de árvore, e a lógica deve ser simples. Para ser eficiente e memória amigável você poderia ter um cache w / sinalizador sujo, com o conjunto de tamanho do cache para a memória disponível menos um pouco de espaço para respirar.

Assumindo que você está usando o Windows XP, se você é apenas pouco mais de seu limite de memória e não desejam ou têm tempo para reformular o código como sugerido acima, você pode adicionar a opção / 3GB à sua arquivo boot.ini e, em seguida, apenas uma questão de definir uma opção de vinculador para obter um 1GB adicional da memória.

Você tem que entender que a memória virtual é diferente de "RAM" em que a quantidade de memória virtual que você está usando é o montante total que você reservou, enquanto a memória real (no Windows o seu chamado conjunto de trabalho) é a memória que você realmente modificado ou bloqueado.

Como alguém referiu, em plataformas Windows de 32 bits o limite de memória virtual é de 2 gigabytes a menos que você defina o sinalizador especial para 3 gigabytes e pode garantir que todos os ponteiros, tanto em seu código e quaisquer bibliotecas que você usa apenas para uso ponteiros não assinados.

Assim, ou forçar os usuários a 64 bits ou monitorando sua memória virtual e tampar o seu tamanho do bloco máximo a algo que se encaixa confortavelmente dentro dos limites impostos pelos sistemas operacionais de 32 bits seria o meu conselho.

Eu tenho bateu na parede de 32 bits no Windows, mas não têm experiência com o trabalho em torno dessas limitações em Linux, então eu só falei sobre o Windows lado das coisas.

Em 32-bit XP seu espaço máximo endereço de programa é de 2 GB. Então você tem fragmentação devido a DLL do e motoristas carregando-se no seu espaço de endereço. Finalmente, você tem o problema de sua fragmentação heap.

A sua melhor jogada é apenas para acabar com isso e correr como um processo de 64 bits (em um sistema 64-bit). De repente, todos esses problemas desaparecem. Você pode usar um monte melhor para mitigar os efeitos da fragmentação da pilha, e você pode tentar usar VirtualAlloc para agarrar sua memória em um grande bloco contíguo (e então você começa a controlá-lo de lá!) Para desencorajar a DLL / drivers de fragmentá-lo.

Finalmente, você pode dividir o seu BSP entre processos. Complicado e doloroso, e, francamente, apenas colocá-lo no disco seria mais fácil, mas, em teoria, você poderia obter um desempenho melhor por ter um grupo de processos de troca de informações, se você pode manter tudo residente (e supondo que você pode ser mais esperto do que a memória do que o OS pode lidar com o buffer de arquivo ... que é um grande se). Cada processo precisaria muito menos memória e, portanto, não deve correr para o limite de espaço de endereço 2 GB. Claro, você vai queimar através de RAM / trocar muito mais rápido.

Você pode mitigar os efeitos da fragmentação do espaço de endereço através da atribuição de pedaços menores. Isto terá outros efeitos colaterais desagradáveis, mas você poderia seguir uma política de recuo onde você pegar pedaços cada vez menores de memória, se você deixar de alocar com êxito. Frequentemente esta abordagem simples você irá obter um programa que funciona quando de outra forma não faria, mas o resto das executa tempo, bem como poderia.

Boy, não computação de 64 bits apenas som muito melhor do que as outras opções?

Como você está alocando memória para os pontos? Você está alocando um ponto de cada vez (por exemplo, pt = new Point). Então, dependendo do tamanho do ponto, alguma memória pode ficar perdido. Por exemplo, em janelas de memória é alocada nos múltiplos de 16 bytes, por isso mesmo se você perguntar a tentar alocar 1 byte, OS vai realmente alocar 16 bytes.

Se este for o caso, usando um alocador de memória pode ajudar. Você pode fazer uma verificação rápida usando STL alocador. (Mais de carregar o novo operador para a classe Point e usar o alocador STL alocar memória, em vez de 'malloc' ou padrão novo operador).

Você pode não ser alocação e desalocação de memória de uma forma óptima. Como outros apontaram, você pode estar com vazamento de memória e não conhecê-lo. Depurar e otimizar a alocação de memória vai levar tempo.

Se você não quer gastar tempo otimizando o uso de memória, por que não tentar o conservador Garbage Collector ? É um plug-in de substituição para malloc () / novo e livre (). Na verdade, free () é um não-op, assim você pode simplesmente remover as chamadas do seu programa. Se, em vez disso, você mão-otimizar o seu programa e gerenciar um pool de memória, como sugerido anteriormente, você vai acabar fazendo um monte de trabalho que a CGC já faz por você.

Você precisa transmitir a sua saída, bem como a sua entrada. Se o formato de saída não é transmitir-orientado, considere fazer segunda passagem. Por exemplo, se o arquivo de saída começa com soma de verificação / tamanho dos dados, espaço de licença na primeira passagem e buscar / gravação para que o espaço mais tarde.

É soar como você está fazendo txt a conversa binário então por que você precisa ter todos os dados na memória ?.
Você não pode simplesmente ler um primitivo do txt (xml), em seguida, salvar para binarystream?

Se você quer ser memória de tamanho independente, você precisa de um algoritmo independente do tamanho. Não importa o tamanho de sua memória RAM é, se você não tem uso de memória sob controle, você vai topar com a fronteira.

Dê uma olhada no menos pedaço de informação que você pode eventualmente usar para produzir um pouco de saída. Então, pense em uma maneira de dividir a entrada em pedaços deste tamanho.

Agora que soa fácil, não é? (Ainda bem que eu não tenho que fazê-lo :))

Você não precisa mudar para máquinas de 64 bits, nem você mais precisa das 1000 coisas sugeridas por outros. O que você precisa é um algoritmo mais pensativo.

Aqui estão algumas coisas que você pode fazer para ajudar com esta situação:

  • Se você estiver no Windows, utilizar Mapas arquivo ( código de exemplo ). Isto lhe dará acesso ao arquivo através de um único ponteiro reserva como se você ler o arquivo inteiro na memória, só que sem realmente fazendo isso. As versões recentes do Linux Kernel tem um mecanismo semelhante.
  • Se você puder, e parece que você poderia, digitalizar o arquivo sequencialmente e evitam criar uma DOM na memória. Isto irá diminuir consideravelmente a sua carga em tempo, bem como os requisitos de memória.
  • Use memória pool! Você provavelmente terá muitos objetos pequenos, como nós, pontos e outros enfeites. Use uma memória pool para ajudar (eu estou supondo que você está usando uma linguagem não gerenciada. Pesquise Pooled de alocação de memória e piscinas).
  • Se você estiver usando uma linguagem gerenciada, pelo menos mover esta parte específica para uma linguagem não gerenciada e assumir o controle da leitura de memória e arquivo. gerenciado idiomas têm uma sobrecarga de não-trivial, tanto no consumo de memória e desempenho. (Sim, eu sei que isso é com a tag "C ++" ...)
  • Tente desenhar um algoritmo no local, onde você ler e processar apenas a quantidade mínima de dados de cada vez, para que seus requisitos de memória iria para baixo.

Por fim, deixe-me salientar que as tarefas complexas exigem medidas complexas. Se você acha que pode pagar uma máquina de 64 bits com 8 GB de RAM, então é só usar "ler o arquivo na memória, os dados do processo, saída de gravação" algoritmo, mesmo que demore um dia ao fim.

há uma boa técnica para isso, é para armazenar alguns casos em arquivos, e depois de obter-los quando precisar usá-los.

esta técnica é utilizada por muitos software de código aberto como Doxygen para ser escalável quando é necessária uma grande quantidade de memória.

Esta é uma questão de idade, mas, desde que eu fiz recentemente a mesma coisa ....

Não há uma resposta simples. Em um mundo ideal você pode usar uma máquina com enorme espaço de endereço (ou seja, 64 bits), e grandes quantidades de memória física. espaço de endereço grande por si só não é suficiente ou ele só vai thrash. Nesse caso, analisar o arquivo XML em um banco de dados, e com consultas apropriadas, puxe o que você precisa. Muito provavelmente este é o que em si OSM faz (eu acredito que o mundo é de cerca de 330GB).

Na realidade eu ainda estou usando 32bit XP por razões de conveniência.

É um trade off entre o espaço e velocidade. Você pode fazer praticamente qualquer coisa em qualquer quantidade de memória fornecendo você não se importa quanto tempo leva. Usando estruturas STL você pode analisar o que quiser, mas em breve você vai ficar sem memória. Você pode definir suas próprias allocators que swap, mas, novamente, ele vai ser ineficiente porque a mapas, vetores, conjuntos etc realmente não sei o que você está fazendo.

A única maneira que eu encontrei para fazer tudo funcionar em um espaço reduzido em uma máquina de 32 bits foi pensar muito cuidadosamente sobre o que eu estava fazendo e que era necessário quando e quebrar a tarefa em pedaços. Memória eficiente (nunca usa mais de ~ 100 MB), mas não maciçamente rápido, mas, em seguida, não importa - como muitas vezes é que uma pessoa tem que analisar os dados XML

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