可変長配列 (VLA) を多用する C99 コードを C++ に移植しています。
VLA (スタック割り当て) を、ヒープにメモリを割り当てる配列クラスに置き換えました。パフォーマンスへの影響は大きく、3.2 倍の速度低下でした (以下のベンチマークを参照)。 C++ で使用できる高速な VLA の置き換えは何ですか? 私の目標は、C++ のコードを書き直すときにパフォーマンスへの影響を最小限に抑えることです。
私に提案された 1 つのアイデアは、クラス内に固定サイズのストレージを含む (つまり、スタック割り当て可能) 配列クラスを作成し、それを小さな配列に使用し、大きな配列のヒープ割り当てに自動的に切り替えることでした。これの私の実装は、投稿の最後にあります。これはかなりうまく機能しますが、元の C99 コードのパフォーマンスにはまだ達していません。それに近づくには、この固定サイズのストレージ (MSL以下) を、私が慣れていないサイズに増やす必要があります。スタックオーバーフローを引き起こすのではないかと心配しているため、それを必要としない多くの小さな配列に対しても、スタックに大きすぎる配列を割り当てたくありません。C99 VLA は、必要以上のストレージを使用することはないため、実際にはこの傾向はありません。
私は出くわしましstd::dynarrayたが、私の理解では、それは標準に受け入れられていませんでした (まだ?)。
Clang と gcc が C++ で VLA をサポートしていることは知っていますが、MSVC でも動作させる必要があります。実際、移植性の向上は、C++ として書き直す主な目標の 1 つです (もう 1 つの目標は、元はコマンド ライン ツールであったプログラムを再利用可能なライブラリにすることです)。
基準
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とパフォーマンスが向上しますが、最終的にプログラムは間違った結果を返し始めます (スタックオーバーフローが原因だと思います)。
これらのベンチマークは OS X の clang 3.7 を使用したものですが、gcc 5 は非常に類似した結果を示しています。
コード
これは、私が使用している現在の「smallvector」実装です。1D および 2D ベクトルが必要です。size を超えるヒープ割り当てに切り替えます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]; }
};