Я переношу некоторый код C99, который сильно использует массивы переменной длины (VLA) для С++.
Я заменил VLAs (распределение стека) классом массива, который выделяет память в куче. Снижение производительности было огромным, замедление в 3,2 раза (см. Ниже контрольные показатели). Какую быструю замену VLA можно использовать в С++? Моя цель - свести к минимуму удар производительности при перезаписи кода для С++.
Одна из идей, которые мне предложили, заключалась в том, чтобы написать класс массива, который содержит хранилище фиксированного размера в классе (то есть может быть выделено в стек) и использует его для небольших массивов и автоматически переключается на распределение кучи для больших массивов, Моя реализация этого в конце поста. Он работает достаточно хорошо, но я до сих пор не могу достичь производительности исходного кода C99. Чтобы приблизиться к этому, я должен увеличить это хранилище фиксированного размера (MSL
ниже) до размеров, которые мне не нравятся. Я не хочу выделять слишком большие массивы в стеке даже для множества небольших массивов, которые не нужны, потому что я беспокоюсь, что это вызовет переполнение стека. VLA C99 на самом деле менее подвержен этому, потому что он никогда не будет использовать больше хранилища, чем необходимо.
Я столкнулся с std::dynarray
, но я понимаю, что он не был принят в стандарт (еще?).
Я знаю, что clang и gcc поддерживают VLA на С++, но мне тоже нужно работать с MSVC. На самом деле лучшая переносимость является одной из основных целей переписывания как С++ (другая цель - сделать программу, которая изначально была инструментом командной строки, в библиотеку многократного использования).
Benchmark
MSL
относится к размеру массива, выше которого я переключаюсь на распределение кучи. Я использую разные значения для 1D и 2D массивов.
Оригинальный код C99: 115 секунд.
MSL = 0 (т.е. Распределение кучи): 367 секунд (3.2x).
1D-MSL = 50, 2D-MSL = 1000: 187 секунд (1,63x).
1D-MSL = 200, 2D-MSL = 4000: 143 секунды (1,24x).
1D-MSL = 1000, 2D-MSL = 20000: 131 (1,14x).
Увеличение MSL
еще больше повышает производительность, но в итоге программа начнет возвращать неправильные результаты (я предполагаю из-за).
Эти ориентиры с clang 3.7 на OS X, но gcc 5 показывает очень похожие результаты.
Код
Это текущая реализация "smallvector", которую я использую. Мне нужны 1D и 2D векторы. Я переключаюсь на распределение кучи выше размера MSL
.
template<typename T, size_t MSL=50>
class lad_vector {
const size_t len;
T sdata[MSL];
T *data;
public:
explicit lad_vector(size_t len_) : len(len_) {
if (len <= MSL)
data = &sdata[0];
else
data = new T[len];
}
~lad_vector() {
if (len > MSL)
delete [] data;
}
const T &operator [] (size_t i) const { return data[i]; }
T &operator [] (size_t i) { return data[i]; }
operator T * () { return data; }
};
template<typename T, size_t MSL=1000>
class lad_matrix {
const size_t rows, cols;
T sdata[MSL];
T *data;
public:
explicit lad_matrix(size_t rows_, size_t cols_) : rows(rows_), cols(cols_) {
if (rows*cols <= MSL)
data = &sdata[0];
else
data = new T[rows*cols];
}
~lad_matrix() {
if (rows*cols > MSL)
delete [] data;
}
T const * operator[] (size_t i) const { return &data[cols*i]; }
T * operator[] (size_t i) { return &data[cols*i]; }
};