Параллельное чтение / запись файла в c - PullRequest
3 голосов
/ 19 января 2012

Проблема состоит в том, чтобы прочитать файл размером около 20 ГБ одновременно n процессами. Файл содержит одну строку в каждой строке, и длина строк может быть или не быть одинаковой. Длина строки может быть не более 10 байтов.

У меня есть кластер из 16 узлов. Каждый узел является однопроцессорным и имеет 6 ГБ ОЗУ. Я использую MPI для записи параллельных кодов.

Как эффективно разбить этот большой файл, чтобы можно было использовать все ресурсы?

Примечание. Ограничения для разделов - это чтение файла как фрагмента с фиксированным количеством строк. Предположим, файл содержит 1600 строк (например, 1600 строк). затем первый процесс должен прочитать с 1-й строки до 100-й, второй процесс должен выполнить с 101-й строки до 200-й и т. д. ...

Поскольку я думаю, что невозможно прочитать файл более чем одним процессом одновременно, потому что у нас есть только один обработчик файла, который указывает где-то только на одну строку. тогда как другие процессы могут читать параллельно из разных кусков?

Ответы [ 4 ]

4 голосов
/ 19 января 2012

Итак, когда вы обнаруживаете, форматы текстовых файлов плохи для работы с большими объемами данных; они не только больше, чем двоичные форматы, но вы также сталкиваетесь с проблемами форматирования, как здесь (поиск новых строк), и все происходит на намного медленнее (данные должны быть преобразованы в строки). Разница в скорости ввода-вывода между текстовыми форматами и двоичными форматами для числовых данных может быть легко 10-кратной. Но мы пока предположим, что вы застряли в формате текстового файла.

Предположительно, вы делаете это разбиение для скорости. Но если у вас нет параллельной файловой системы, то есть нескольких серверов, обслуживающих несколько дисков, и FS, которая может их координировать, маловероятно, что вы получите значительное ускорение от чтения нескольких MPI-задач из одного файла. поскольку в конечном итоге все эти запросы все равно будут сериализованы на уровне сервера / контроллера / диска.

Кроме того, чтение в больших блоках данных будет происходить гораздо быстрее, чем fseek (), выполняющее небольшие операции чтения в поисках новых строк.

Таким образом, мое предложение состояло бы в том, чтобы один процесс (возможно, последний) прочитал все данные за минимально возможное время и отправил соответствующие строки для каждой задачи (включая, наконец, саму себя). Если вы знаете, сколько строк имеет файл в начале, это довольно просто; Считайте, скажем, 2 ГБ данных, найдите в памяти конец N / P-й строки и отправьте это заданию 0, отправьте заданию 0 сообщение «завершены ваши данные» и продолжайте.

0 голосов
/ 26 марта 2012

Вот функция в python, использующая mpi и расширение pypar для чтения количества строк в большом файле, используя mpi для разделения обязанностей между несколькими хостами.

def getFileLineCount( file1 ):
    import pypar, mmap, os
    """
    uses pypar and mpi to speed up counting lines
    parameters:
        file1 - the file name to count lines
    returns:
        (line count)
    """

    p1 = open( file1, "r" )
    f1 = mmap.mmap( p1.fileno(), 0, None, mmap.ACCESS_READ )

    #work out file size
    fSize = os.stat( file1 ).st_size
    #divide up to farm out line counting
    chunk = ( fSize / pypar.size() ) + 1

    lines = 0
    #set start and end locations
    seekStart = chunk * ( pypar.rank() )
    seekEnd = chunk * ( pypar.rank() + 1 )
    if seekEnd > fSize:
        seekEnd = fSize

    #find start of next line after chunk
    if pypar.rank() > 0:
        f1.seek( seekStart )
        l1 = f1.readline()
        seekStart = f1.tell()

    #tell previous rank my seek start to make their seek end
    if pypar.rank() > 0:
#        logging.info( 'Sending to %d, seek start %d' % ( pypar.rank() - 1, seekStart ) )
        pypar.send( seekStart, pypar.rank() - 1 )
    if pypar.rank() < pypar.size() - 1:
        seekEnd = pypar.receive( pypar.rank() + 1 )
#        logging.info( 'Receiving from %d, seek end %d' % ( pypar.rank() + 1, seekEnd ) )

    f1.seek( seekStart )

    logging.info( 'Calculating line lengths and positions from file byte %d to %d' % ( seekStart, seekEnd ) )

    l1 = f1.readline()
    prevLine = l1

    while len( l1 ) > 0:
        lines += 1

        l1 = f1.readline()
        if f1.tell() > seekEnd or len( l1 ) == 0:
            break

        prevLine = l1
    #while
    f1.close()
    p1.close()

    if pypar.rank() == 0:
        logging.info( 'Receiving line info' )
        for p in range( 1, pypar.size() ):
            lines += pypar.receive( p )
    else:
        logging.info( 'Sending my line info' )
        pypar.send( lines, 0 )

    lines = pypar.broadcast( lines )
    return ( lines )
0 голосов
/ 19 января 2012

Я думаю, что было бы лучше написать кусок кода, который бы получал длины строк и распределял строки по процессам.Эта распределительная функция будет работать не с самими строками, а только с их длинами.

Найти алгоритм равномерного распределения источников фиксированного размера не проблема.

И после этого распределительная функция сообщит другим процессам, какие кусочки они должны получить для работы.Процесс 0 (дистрибьютор) будет читать строку.Уже известно, что строка num.1 должен обрабатываться процессом 1. ... P.0 читает строку номер.N и знает, какой процесс должен работать с ним.

О!Нам не нужно оптимизировать распределение с самого начала.Просто процесс распространителя считывает новую строку из ввода и передает ее свободному процессу.Вот и все.

Итак, у вас есть даже два решения: сильно оптимизированное и простое.

Мы могли бы достичь еще большей оптимизации, если бы процесс распределителя время от времени повторно оптимизировал непрочитанные, но все же строки.

0 голосов
/ 19 января 2012

Вы не указываете, есть ли какие-либо ограничения на разделы, поэтому я предполагаю, что нет никаких.Я также предполагаю, что вы хотите, чтобы размеры разделов были как можно ближе к размеру.

Наивный подход - разделить файл на куски размером 20GB/n.Начальная позиция чанка i будет i*20GB/n для i=0..n-1.

Проблема в том, что, конечно, нет гарантии того, что границы чанков будут попадать между строками входного файла.В общем, они не будут.

К счастью, есть простой способ исправить это.Установив границы, как указано выше, слегка сдвиньте их так, чтобы каждый из них (кроме i=0) был помещен после следующей новой строки.

Это будет включать чтение 15 небольших фрагментов файла, но приведет кочень равномерное разбиение.

Фактически, коррекция может быть сделана каждым узлом индивидуально, но, вероятно, не стоит усложнять объяснение этим.

...