Quais são as vantagens dos arquivos mapeados na memória?


89

Tenho pesquisado arquivos de mapeamento de memória para um projeto e gostaria de receber qualquer opinião de pessoas que os tenham usado antes ou decidiram não usá-los, e por quê?

Em particular, estou preocupado com o seguinte, em ordem de importância:

  • simultaneidade
  • acesso aleatório
  • desempenho
  • fácil de usar
  • portabilidade

Respostas:


56

Acho que a vantagem é realmente que você reduz a quantidade de cópia de dados necessária em relação aos métodos tradicionais de leitura de um arquivo.

Se o seu aplicativo puder usar os dados "no local" em um arquivo mapeado na memória, ele poderá entrar sem ser copiado; se você usar uma chamada de sistema (por exemplo, pread () do Linux), isso normalmente envolve o kernel copiando os dados de seus próprios buffers para o espaço do usuário. Essa cópia extra não só leva tempo, mas diminui a eficácia dos caches da CPU acessando essa cópia extra dos dados.

Se os dados realmente precisam ser lidos do disco (como em E / S física), então o sistema operacional ainda tem que lê-los, uma falha de página provavelmente não é melhor em termos de desempenho do que uma chamada de sistema, mas se eles não (ou seja, já no cache do sistema operacional), o desempenho deveria, em teoria, ser muito melhor.

No lado negativo, não há interface assíncrona para arquivos mapeados na memória - se você tentar acessar uma página que não está mapeada, isso gera uma falha de página e faz com que o thread espere pelo I / O.


A desvantagem óbvia dos arquivos mapeados na memória está em um sistema operacional de 32 bits - você pode facilmente ficar sem espaço de endereço.


4
No Windows, pelo menos, você pode mapear várias visualizações de 32 bits de um arquivo mmap maior - o que pode ser mais eficiente do que tentar lidar com arquivos muito grandes usando a função CRT normal
Martin Beckett

@MarkR Você escreveu "sua cópia extra não só leva tempo, mas diminui a eficácia dos caches da CPU acessando esta cópia extra dos dados. ". ( ênfase minha). Você pode explicar como a cópia extra do buffer no kernel prejudica a eficácia dos caches da CPU?
Geek

4
@Geek acessando duas vezes mais memória = duas vezes mais cache desperdiçado (muito aproximadamente).
user253751

49

Usei um arquivo mapeado de memória para implementar um recurso de 'preenchimento automático' enquanto o usuário está digitando. Tenho bem mais de 1 milhão de números de peças de produtos armazenados em um único arquivo de índice. O arquivo tem algumas informações de cabeçalho típicas, mas a maior parte do arquivo é uma matriz gigante de registros de tamanho fixo classificados no campo-chave.

No tempo de execução, o arquivo é mapeado na memória, lançado em uma matriz de Cestilo structe fazemos uma pesquisa binária para encontrar números de peça correspondentes conforme os tipos de usuário. Apenas algumas páginas da memória do arquivo são realmente lidas do disco - quaisquer que sejam as páginas atingidas durante a pesquisa binária.

  • Simultaneidade - tive um problema de implementação em que às vezes mapeava a memória do arquivo várias vezes no mesmo espaço de processo. Isso foi um problema, pelo que me lembro, porque às vezes o sistema não conseguia encontrar um bloco de memória virtual grande o suficiente para mapear o arquivo. A solução era mapear o arquivo apenas uma vez e converter todas as chamadas para ele. Em retrospecto, usar um serviço completo do Windows seria muito bom.
  • Acesso aleatório - A pesquisa binária é certamente um acesso aleatório e rápido
  • Desempenho - a pesquisa é extremamente rápida. Conforme os usuários digitam, uma janela pop-up exibe uma lista de números de peças de produtos correspondentes, a lista diminui à medida que eles continuam a digitar. Não há atraso perceptível durante a digitação.

1
A busca binária não seria lenta conforme as páginas são lidas em cada tentativa? Ou o sistema operacional é inteligente o suficiente para lidar com isso de maneira eficiente?
jjxtra

1
Suponho que usar E / S mapeada em memória seja um desperdício para a pesquisa binária, pois a pesquisa acessará apenas algumas chaves únicas em locais de memória relativamente distantes, mas o sistema operacional carregará 4k páginas para cada solicitação. Mas, novamente, o arquivo com as partes não muda muito, então o cache ajuda a encobrir isso. Mas, estritamente falando, acredito que a busca / leitura tradicional seria melhor aqui. Finalmente, 1 mil não é muito hoje em dia. Por que não apenas manter tudo na RAM?
o suíno de

5
@ the swine and PsychoDad, minha resposta original foi de 2008 e a implementação real desse recurso de autocompletar mapeamento de memória foi por volta de 2004-2005. Consumir 800-1000 MB de memória física para carregar o arquivo inteiro não era uma boa solução para nossa base de usuários. A solução de mapeamento de memória foi muito rápida e eficiente. Foi incrível e eu me lembro com carinho dos meus primeiros dias de desenvolvedor júnior. :)
Brian Ensink

@BrianEnsink: ok, isso faz sentido. eu não esperava que cada entrada fosse tanto quanto 1 KB. então, é claro, a abordagem paginada se torna mais eficiente. bom :)
o suíno de

22

Os arquivos mapeados na memória podem ser usados ​​para substituir o acesso de leitura / gravação ou para oferecer suporte ao compartilhamento simultâneo. Quando você os usa para um mecanismo, obtém o outro também.

Em vez de procurar, escrever e ler um arquivo, você o mapeia na memória e simplesmente acessa os bits onde espera que estejam.

Isso pode ser muito útil e, dependendo da interface da memória virtual, pode melhorar o desempenho. A melhoria de desempenho pode ocorrer porque o sistema operacional agora consegue gerenciar esse antigo "arquivo de E / S" junto com todos os outros acessos à memória programática e pode (em teoria) aproveitar os algoritmos de paginação e assim por diante que já está usando para oferecer suporte memória virtual para o resto do programa. No entanto, depende da qualidade do seu sistema de memória virtual subjacente. Eu ouvi dizer que os sistemas de memória virtual Solaris e * BSD podem apresentar melhorias de desempenho melhores do que o sistema VM do Linux - mas não tenho dados empíricos para fazer backup disso. YMMV.

A simultaneidade entra em cena quando você considera a possibilidade de vários processos usando o mesmo "arquivo" por meio da memória mapeada. No modelo de leitura / gravação, se dois processos gravassem na mesma área do arquivo, você poderia ter quase certeza de que um dos dados do processo chegaria no arquivo, sobrescrevendo os dados do outro processo. Você conseguiria um ou outro - mas não uma mistura estranha. Tenho que admitir que não tenho certeza se esse é um comportamento exigido por algum padrão, mas é algo em que você poderia confiar. (Na verdade, é uma boa pergunta de acompanhamento!)

No mundo mapeado, em contraste, imagine dois processos, ambos "escrevendo". Eles fazem isso fazendo "armazenamentos de memória", o que resulta na paginação O / S dos dados para o disco - eventualmente. Mas, enquanto isso, podem ocorrer gravações sobrepostas.

Aqui está um exemplo. Digamos que eu tenha dois processos gravando 8 bytes no deslocamento 1024. O processo 1 está gravando '11111111' e o processo 2 está gravando '22222222'. Se eles usam I / O de arquivo, então você pode imaginar, no fundo do O / S, há um buffer cheio de 1s e um buffer cheio de 2s, ambos indo para o mesmo lugar no disco. Um deles vai chegar lá primeiro, e o outro um segundo. Nesse caso, o segundo ganha. No entanto , se estou usando a abordagem de arquivo mapeado por memória, o processo 1 vai para um armazenamento de memória de 4 bytes, seguido por outro armazenamento de memória de 4 bytes (vamos supor que esse não seja o tamanho máximo de armazenamento de memória). O processo 2 fará a mesma coisa. Com base em quando os processos são executados, você pode esperar ver qualquer um dos seguintes:

11111111
22222222
11112222
22221111

A solução para isso é usar a exclusão mútua explícita - o que provavelmente é uma boa ideia em qualquer caso. De qualquer forma, você estava confiando no O / S para fazer "a coisa certa" no caso de I / O de arquivo de leitura / gravação.

A primitiva de exclusão mútua de classificação é o mutex. Para arquivos mapeados na memória, sugiro que você olhe para um mutex mapeado na memória, disponível usando (por exemplo) pthread_mutex_init ().

Edite com uma pegadinha: Quando você está usando arquivos mapeados, existe a tentação de incorporar ponteiros para os dados no arquivo, no próprio arquivo (pense na lista vinculada armazenada no arquivo mapeado). Você não quer fazer isso, pois o arquivo pode ser mapeado em diferentes endereços absolutos em momentos diferentes ou em processos diferentes. Em vez disso, use deslocamentos dentro do arquivo mapeado.


1

A simultaneidade seria um problema. O acesso aleatório é mais fácil O desempenho vai de bom para ótimo. Fácil de usar. Não tão bom. Portabilidade - não tão quente.

Eu os usei em um sistema solar há muito tempo, e esses são meus pensamentos.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.