Otimizando a leitura de E / S com leitura antecipada, evitando o armazenamento de dados no cache de páginas

3

Eu preciso ser capaz de ler dados sequencialmente de um arquivo sem armazenar os dados que estão sendo lidos no cache de páginas, pois o conteúdo do arquivo não deve ser lido novamente e também porque há pressão de memória na caixa. (deseja usar a memória preciosa para o armazenamento em cache de E / S de disco).

A pergunta que tenho é sobre como eu posso otimizar essas leituras. Desde que eu sei que os dados que estão sendo lidos são sequencialmente colocados no disco (menos a fragmentação), eu quero ser capaz de ler adiante (aumentando / sys / block / sda / queue / read_ahead_kb) mas não tenho certeza se isso levará a qualquer benefício porque eu tenho que impedir que os dados que estão sendo lidos sejam armazenados no cache de páginas usando posix_fadvise (com o sinalizador POSIX_FADV_DONTNEED).

Os dados de leitura antecipada serão simplesmente descartados por causa da dica para descartar os dados do cache de páginas?

    
por Stormshadow 18.10.2016 / 09:57

2 respostas

2

Use direct IO :

Direct I/O is a feature of the file system whereby file reads and writes go directly from the applications to the storage device, bypassing the operating system read and write caches. Direct I/O is used only by applications (such as databases) that manage their own caches.

An application invokes direct I/O by opening a file with the O_DIRECT flag.

Por exemplo:

int fd = open( filename, O_RDONLY | O_DIRECT );

O Direct IO no Linux é peculiar e tem algumas restrições. O buffer de E / S do aplicativo deve ser alinhado à página e alguns sistemas de arquivos exigem que cada solicitação de E / S seja um múltiplo exato do tamanho da página. Essa última restrição pode dificultar a leitura / gravação da última parte de um arquivo.

Uma maneira fácil de codificar para lidar com leitura antecipada em seu aplicativo pode ser feita usando fdopen e definindo um buffer grande alinhado a página usando posix_memalign e setvbuf :

// should really get page size using sysconf()
// but beware of systems with multiple page sizes
#define ALIGNMENT ( 4UL * 1024UL )
#define BUFSIZE ( 1024UL * 1024UL )
char *buffer;
...

int fd = open( filename, O_RDONLY | O_DIRECT );
FILE *file = fdopen( fd, "rb" );

int rc = posix_memalign( &buffer, ALIGNMENT, BUFSIZE );
rc = setvbuf( file, buffer, _IOFBF, BUFSIZE );

Você também pode usar mmap() para obter memória anônima para usar o buffer. Isso tem a vantagem de ser naturalmente alinhado à página:

...
char *buffer = mmap( NULL, BUFSIZE, PROT_READ | PROT_WRITE,
    MAP_ANONYMOUS | MAP_PRIVATE, -1, 0 );
rc = setvbuf( file, buffer, _IOFBF, BUFSIZE );

Em seguida, use apenas fread() / fgets() ou qualquer função de leitura FILE * -type que você deseja ler no fluxo file .

Você precisa verificar usando uma ferramenta como strace que as chamadas do sistema read reais são feitas com um buffer alinhado à página e com tamanho de página - algumas implementações da biblioteca C de FILE * -based processing don Não use o buffer especificado por setvbuf para apenas buffer de E / S, para que o alinhamento e o tamanho possam ser desativados. Eu não acho que o Linux / glibc faz isso, mas se você não verificar e o tamanho e / ou alinhamento estiverem desativados, suas chamadas de IO falharão.

E novamente - Linux IO direto pode ser peculiar. Apenas alguns sistemas de arquivos suportam IO direto, e alguns deles são mais específicos que outros. TESTE completamente se você decidir usá-lo.

O código postado fará uma leitura antecipada de 1 MB sempre que o buffer do fluxo precisar ser preenchido. Você também pode implementar uma leitura antecipada mais sofisticada usando threads - um thread preenche um buffer, outro thread (s) é lido de um buffer completo. Isso evitaria o processamento de "gaguejos" à medida que a leitura antecipada é feita, mas ao custo de uma boa quantidade de código multithreading relativamente complexo.

    
por 18.10.2016 / 12:48
1

Isso é bem respondido nesta pergunta do StackOverflow .

  • keep the track of the file offset before the read()

  • after the read(), call fadvise(POSIX_FADV_DONTNEED) on the range of data [...]

  • for best results, you have to read the data in page-aligned blocks. The I/O cache is page based, and the fadvise() maps the specified data range into the list of pages. The misalignment would cause extra read()s (and harm performance) but otherwise is harmless.

Você não deseja ler adiante se otimizar a memória. Você só quer ler. Um disco irá operar sequencialmente se você ler, sem precisar dizer nada, por favor, transmita os dados.

    
por 18.10.2016 / 10:41

Tags