Самый быстрый возможный способ выделения массива строк

#c #arrays #performance #resize #realloc

#c #массивы #Производительность #изменение размера #перераспределить

Вопрос:

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

 temp = (char**) realloc (buffer, newSize * (sizeof(char*)));
if (temp == NULL)
    return false;
else
    buffer = temp;
  

И до сих пор все в порядке. Теперь для каждой новой ячейки я должен вызывать malloc с правильным размером. Обратите внимание, что NewSize всегда четный, а нечетные строки имеют длину, отличную от четных.

 for (i = oldSize; i < newSize; i  ){
    support = (char*) malloc (LENGTH1 * sizeof(char));
    if (support == NULL){
        marker = i;
        failedMalloc = true;
        break;
    }
    else
        buffer[i] = support;

    i  ;

    support = (char*) malloc (LENGTH2 * sizeof(char));
    if (support == NULL){
        marker = i;
        failedMalloc = true;
        break;
    }
    else
        buffer[i] = support;

}
  

Дело в том, что, поскольку я работаю с огромными данными, рано или поздно я закончу память, и перераспределение или один из mallocs завершится неудачей. Проблема в том, что если это один из mallocs, который выходит из строя, существует риск того, что мне придется вызывать миллионы free, чтобы очистить некоторую память. Это занимает много времени. Есть ли какой-нибудь способ ускорить этот процесс или даже лучше избежать его?

 if (failedMalloc){
    for (i = oldRows; i < marker; i  )
        free(buffer[i]);
    temp = (char**) realloc (buffer, oldRows * (sizeof(char*)));
}
  

PS: Да, я знаю, что арифметика указателей выполняется быстрее, чем индексация массива.Я реализую его, когда найду способ решить эту проблему, на данный момент я предпочитаю использовать индексацию массива, потому что я нахожу ее менее подверженной ошибкам. Но в окончательной версии будет использоваться арифметика указателей

Комментарии:

1. Арифметика указателей выполняется не быстрее, чем индексация массива

2. @sth, я обнаружил то же самое в своем бенчмаркинге — архитектура x86 имеет встроенную индексацию на уровне инструкций, поэтому, если вы все равно перебираете индекс, вы получаете его бесплатно.

Ответ №1:

Вместо выделения каждой строки по отдельности, выделяйте их блоками. Вы могли бы, например, malloc 128*(LENGTH1 LENGTH2) и иметь место для 256 последовательных строк. Всякий раз, когда ваш индекс пересекает границу блока, выделите другой большой блок и используйте арифметику по модулю, чтобы получить смещение в нем для начала строки.

PS sizeof(char) гарантированно равен 1.

Комментарии:

1. Вариант этого понятия: malloc((NewSize / 2) * (LENGTH1 LENGTH2) (NewSize amp; 1 ? ДЛИНА1 : 0));

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

3. Не могли бы вы дать мне небольшой пример исходного кода? Я не уверен, что понял, что вы предлагаете.. особенно я не понимаю, как получить доступ к этим блокам строк, потому что то, что я пробовал, разбилось. Кроме того, я не знаю, имеет ли это значение, но я не могу использовать malloc(), я должен перераспределить (), потому что я увеличиваю уже существующий массив

4. @Alex Сохраните realloc часть… но когда вы выделяете новые строки, выделите достаточно места для 256 (но только тогда, когда i % 256 == 0 ). Затем добавьте соответствующие смещения к этому malloc адресу ed при заполнении вашего buffer массива.

Ответ №2:

Выделяйте большие блоки памяти. Чем меньше вызовов malloc, тем лучше. Самым быстрым будет предварительное вычисление требуемого размера и выделение только один раз.

Кроме того, использование арифметики указателей не приведет к какой-либо видимой разнице здесь.

Ответ №3:

Вы могли бы написать свои собственные процедуры выделения и освобождения и использовать их вместо malloc/free строк. Если ваши процедуры malloc обрабатывают один или несколько больших буферов и выделяют из них маленькие кусочки, то вы можете освободить весь массив за один раз, просто вызвав free каждый большой буфер.

Общая идея работает особенно хорошо в случае, когда все выделения имеют одинаковый размер, и в этом случае она называется «распределителем пула». В этом случае для каждого массива или строк у вас может быть один связанный пул для LENGTH1 выделения, а другой для LENGTH2 .

Я говорю: «напишите свой собственный», но, без сомнения, есть простые распределители пула с открытым исходным кодом, которые можно использовать.

Ответ №4:

Один из способов избежать ненужной памяти — каждый раз использовать больший объем памяти, когда вам нужно malloc,

malloc фиксированный размер (выровнять по 2 ^ n), например

 int m_size = 1;

// when need malloc
while (m_size < require_size) m_size * 2;
malloc(m_size);