Linux и Windows: использование больших файлов для экономии физической памяти - PullRequest
1 голос
/ 09 марта 2011

Добрый день, мы реализовали класс C ++ cKeyArray, чтобы проверить, можем ли мы использовать API больших файлов для экономии физической памяти. Во время тестирования Centos Linux мы обнаружили, что Linux File API работает так же быстро, как и использование кучи для обработки произвольного доступа. Вот числа: для базы данных SQL с 2700000 строк, где KeySize для каждой строки составляет 62 байта,

класс cKeyArray с использованием API-интерфейса LINUX File BruteForceComparisons = 197275 BruteForceTimeElapsed = 1 763 504 445 микросекунд Каждое сравнение BruteForce требует два произвольных доступа, то есть среднее время, необходимое для каждого произвольного доступа = 1 763 504 445 микросекунд / (2 * 197275) = 4470 микросекунд

Куча, без класса cKeyArray

BruteForceComparisons = 197275 BruteForceTimeElapsed = 1 708 442 690 микросекунд среднее время, необходимое для каждого произвольного доступа = 4300 микросекунд.

В 32-битных Windows цифры:

класс cKeyArray с использованием Windows File API BruteForceComparisons = 197275 BruteForceTimeElapsed = 9243787 миллисекунд среднее время для каждого произвольного доступа составляет 23,4 миллисекун

Куча, нет класса cKeyArray BruteForceComparisons = 197275 BruteForceTimeElapsed = 2,141,941 миллисек среднее время, необходимое для каждого произвольного доступа, составляет 5,4 миллисекун

Нам интересно, почему числа cKeyArray в Linux так же хороши, как числа кучи Linux, тогда как в 32-битной Windows среднее время произвольного доступа к куче в 4 раза выше, чем в Windows File API cKeyArray. Есть ли какой-нибудь способ ускорить API-интерфейс Windows cKeyArray?

Ранее мы получили много хороших предложений от Stack Overflow по использованию API-интерфейса Windows Memory Mapped File. Основываясь на этих рекомендациях по переполнению стека, мы реализовали класс кэширования MRU для файла с отображенной памятью, который работает правильно.

Поскольку мы хотим разработать кроссплатформенное решение, мы хотим проявить должную осмотрительность, чтобы понять, почему Linux File API так быстр? Спасибо. Мы пытаемся опубликовать часть реализации класса cKeyArray ниже.

#define KEYARRAY_THRESHOLD 100000000   
// Use file instead of memory if requirement is above this number


cKeyArray::cKeyArray(long RecCount_,int KeySize_,int MatchCodeSize_, char* TmpFileName_) {
    RecCount=RecCount_;
    KeySize=KeySize_;
    MatchCodeSize=MatchCodeSize_;
    MemBuffer=0;
    KeyBuffer=0;
    MemFile=0;
    MemFileName[0]='\x0';
    ReturnBuffer=new char[MatchCodeSize + 1];   
    if (RecCount*KeySize<=KEYARRAY_THRESHOLD) {     
        InMemory=true;
        MemBuffer=new char[RecCount*KeySize];
        memset(MemBuffer,0,RecCount*KeySize);
    } else {
        InMemory=false;
        strcpy(MemFileName,TmpFileName_);
        try {
            MemFile=
                              new cFile(MemFileName,cFile::CreateAlways,cFile::ReadWrite);
        }
        catch (cException e)
        {
            throw e;    
        }
        try {
            MemFile->SetFilePointer(
                               (int64_t)(RecCount*KeySize),cFile::FileBegin);
        }
        catch (cException e)
        {
            throw e;    
        }
        if (!(MemFile->SetEndOfFile()))
            throw cException(ERR_FILEOPEN,MemFileName);

        KeyBuffer=new char[KeySize];
    }
}

char *cKeyArray::GetKey(long Record_) {
        memset(ReturnBuffer,0,MatchCodeSize + 1);   
    if (InMemory) {
        memcpy(ReturnBuffer,MemBuffer+Record_*KeySize,MatchCodeSize);
    } else {
        MemFile->SetFilePointer((int64_t)(Record_*KeySize),cFile::FileBegin);
        MemFile->ReadFile(KeyBuffer,KeySize);
        memcpy(ReturnBuffer,KeyBuffer,MatchCodeSize);   
    }
    return ReturnBuffer;
}


uint32_t cKeyArray::GetDupeGroup(long Record_) {
    uint32_t DupeGroup(0);
    if (InMemory) {
         memcpy((char*)&DupeGroup,
                         MemBuffer+Record_*KeySize + MatchCodeSize,sizeof(uint32_t)); 
    } else {
        MemFile->SetFilePointer(
                        (int64_t)(Record_*KeySize + MatchCodeSize) ,cFile::FileBegin);
        MemFile->ReadFile((char*)&DupeGroup,sizeof(uint32_t));
    }
    return DupeGroup;
}

Ответы [ 2 ]

5 голосов
/ 09 марта 2011

В Linux ОС активно кэширует данные файла в основной памяти - поэтому, хотя вы явно не выделяете память для содержимого файла, они тем не менее хранятся в ОЗУ.Вот хорошая ссылка с дополнительной информацией о кеше - в этом описании отсутствует только одна вещь: большинство файловых систем Linux фактически реализуют стандартные интерфейсы ввода / вывода в виде тонких оболочек вокруг кеша страниц,Это означает, что даже если вы явно не отображали в памяти файл, система все равно обрабатывает его так, как если бы он отображался в памяти под прикрытием.Вот почему вы видите примерно одинаковую производительность с любым из этих подходов.

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

2 голосов
/ 09 марта 2011

Ваше решение с отображением памяти должно быть в 10 раз быстрее, чем файловое решение даже в Linux. Это скорость, с которой я сталкиваюсь в своих тестах.

Каждый системный вызов доступа к файлам занимает сотни циклов ЦП. Время, которое ваша программа могла бы использовать для реальной работы.

Одним из объяснений того, почему скорости похожи, может быть то, что ваша карта памяти ранее не использовалась. При первом обращении к отображенной в памяти странице она должна быть назначена физической странице ОЗУ и обнулена, или, если это файл на диске, ее необходимо загрузить с диска в ОЗУ. Все это занимает значительное время.

Если коснуться (прочитать или записать значение) каждые 4 КБ ОЗУ перед его использованием, вы увидите значительное увеличение скорости на карте памяти.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...