矢量与动态数组基准测试 - 瓶颈是什么?
vector vs dynamic array benchmarks - what is the bottleneck?
鼓励在动态数组上使用 STL,但我很好奇瓶颈是什么? MAX_PACKET_LENGTH
是 32768
.
---------------------------------------------------------------------------
Benchmark Time CPU Iterations
---------------------------------------------------------------------------
BM_VectorInit 1335 ns 1193 ns 497778
BM_RawInit 58 ns 55 ns 10000000
static void BM_VectorInit(benchmark::State &state)
{
for (auto _ : state)
{
std::vector<char> a(MAX_PACKET_LENGTH);
}
}
static void BM_RawInit(benchmark::State &state)
{
for (auto _ : state)
{
auto a = new char[MAX_PACKET_LENGTH];
delete[] a;
}
}
您正在比较构建 std::vector
的成本与在堆上分配数组的成本。当您创建一个 std::vector
时,它会在内部分配一个堆上的数组,但是会有额外的开销,因为 std::vector
本身是一个需要构造和存储的对象(可能在堆栈上或在堆)。因此,创建 std::vector
的时间应该比创建 new char[]
.
的时间长
也就是说,BM_RawInit
和 BM_VectorInit
之间还有另一个区别。当你用一个整数参数构造一个 std::vector
时,如 std::vector<char> a(MAX_PACKET_LENGTH)
中,会发生两件事。 Space 将在堆上分配 MAX_PACKET_LENGTH
个项目 和 这些项目也将被默认构造。另一方面,当您执行 new char[MAX_PACKET_LENGTH]
时,只会发生分配。
为了更好的比较,尝试创建仅分配 space 的第三个基准,如下所示:
static void BM_VectorReserve(benchmark::State &state)
{
for (auto _ : state)
{
std::vector<char> a;
a.reserve(MAX_PACKET_LENGTH);
}
}
虽然这不是一个完美的比较,因为在我们声明 a
的第一行将分配少量内存,然后当我们调用 reserve
时,初始内存将被释放。之后,std::vector
将为 MAX_PACKET_LENGTH
项分配足够的 space。在实际代码中,这通常可以忽略不计,但为了您的基准,它可以部分解释为什么 BM_VectorReserve
比 BM_RawInit
.
花费更长的时间
首先,正如@juanchopanza 所建议的那样——如果您不提供适当的测试程序,我们将无法复制您的图形;你刚刚给了我们两个函数,它们什么都不做,也不会在二进制文件中产生任何结果。
无论如何,开销似乎是由于 std::vector
对 char
值进行了零初始化。
如果您想避免这种情况发生,或者只是为了公平地进行基准测试,请使用 a struct which wraps a char and doesn't initialize it 作为矢量元素类型,然后再次 运行 您的测试。不过,我不建议在生产代码中实际使用这种愚蠢的结构——使用您实际需要的类型。
说到使用你需要的东西——完全可以使用:
auto buffer_ptr = std::make_unique<char[]>(MAX_PACKET_LENGTH);
然后也许:
auto buffer = std::span<char>(raw_buffer.get(), MAX_PACKET_LENGTH);
而且几乎所有可以使用 std::vector
的地方都可以使用 。 (不过请注意,std::span
仅在 C++20 中出现,现在您需要 GSL 实现)。
鼓励在动态数组上使用 STL,但我很好奇瓶颈是什么? MAX_PACKET_LENGTH
是 32768
.
---------------------------------------------------------------------------
Benchmark Time CPU Iterations
---------------------------------------------------------------------------
BM_VectorInit 1335 ns 1193 ns 497778
BM_RawInit 58 ns 55 ns 10000000
static void BM_VectorInit(benchmark::State &state)
{
for (auto _ : state)
{
std::vector<char> a(MAX_PACKET_LENGTH);
}
}
static void BM_RawInit(benchmark::State &state)
{
for (auto _ : state)
{
auto a = new char[MAX_PACKET_LENGTH];
delete[] a;
}
}
您正在比较构建 std::vector
的成本与在堆上分配数组的成本。当您创建一个 std::vector
时,它会在内部分配一个堆上的数组,但是会有额外的开销,因为 std::vector
本身是一个需要构造和存储的对象(可能在堆栈上或在堆)。因此,创建 std::vector
的时间应该比创建 new char[]
.
也就是说,BM_RawInit
和 BM_VectorInit
之间还有另一个区别。当你用一个整数参数构造一个 std::vector
时,如 std::vector<char> a(MAX_PACKET_LENGTH)
中,会发生两件事。 Space 将在堆上分配 MAX_PACKET_LENGTH
个项目 和 这些项目也将被默认构造。另一方面,当您执行 new char[MAX_PACKET_LENGTH]
时,只会发生分配。
为了更好的比较,尝试创建仅分配 space 的第三个基准,如下所示:
static void BM_VectorReserve(benchmark::State &state)
{
for (auto _ : state)
{
std::vector<char> a;
a.reserve(MAX_PACKET_LENGTH);
}
}
虽然这不是一个完美的比较,因为在我们声明 a
的第一行将分配少量内存,然后当我们调用 reserve
时,初始内存将被释放。之后,std::vector
将为 MAX_PACKET_LENGTH
项分配足够的 space。在实际代码中,这通常可以忽略不计,但为了您的基准,它可以部分解释为什么 BM_VectorReserve
比 BM_RawInit
.
首先,正如@juanchopanza 所建议的那样——如果您不提供适当的测试程序,我们将无法复制您的图形;你刚刚给了我们两个函数,它们什么都不做,也不会在二进制文件中产生任何结果。
无论如何,开销似乎是由于 std::vector
对 char
值进行了零初始化。
如果您想避免这种情况发生,或者只是为了公平地进行基准测试,请使用 a struct which wraps a char and doesn't initialize it 作为矢量元素类型,然后再次 运行 您的测试。不过,我不建议在生产代码中实际使用这种愚蠢的结构——使用您实际需要的类型。
说到使用你需要的东西——完全可以使用:
auto buffer_ptr = std::make_unique<char[]>(MAX_PACKET_LENGTH);
然后也许:
auto buffer = std::span<char>(raw_buffer.get(), MAX_PACKET_LENGTH);
而且几乎所有可以使用 std::vector
的地方都可以使用 std::span
仅在 C++20 中出现,现在您需要 GSL 实现)。