C++*不*初始化其成员的向量?
C++ vector that *doesn't* initialize its members?
我正在为一段返回大数组的 C 代码制作一个C++包装器,所以我尝试在vector<unsigned char>
中返回数据。
现在的问题是,数据大约是兆字节,vector
不必要地初始化其存储,这基本上将我的速度降低了一半。
我该如何防止这种情况?
或者,如果不可能 - 是否有其他STL容器可以避免这种不必要的工作?还是我最终必须制作自己的容器?
(C++11)
注意:
我正在传递矢量作为我的输出缓冲区。我不是从其他地方复制数据.
它像:
vector<unsigned char> buf(size); // Why initialize??
GetMyDataFromC(&buf[0], buf.size());
对于使用用户提供的默认构造函数(未显式初始化任何内容)的结构的默认值和值初始化,不会对无符号的 char 成员执行初始化:
struct uninitialized_char {
unsigned char m;
uninitialized_char() {}
};
// just to be safe
static_assert(1 == sizeof(uninitialized_char), "");
std::vector<uninitialized_char> v(4 * (1<<20));
GetMyDataFromC(reinterpret_cast<unsigned char*>(&v[0]), v.size());
我认为在严格的别名规则下这甚至是合法的。
当我比较v
与vector<unsigned char>
的构建时间时,我得到了~8 μs与~12 ms。速度提高 1000 倍以上。编译器是带有libc ++和标志的clang 3.2:-std=c++11 -Os -fcatch-undefined-behavior -ftrapv -pedantic -Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-missing-prototypes
C++11 有一个用于未初始化存储的帮助程序 std::aligned_storage。虽然它需要编译时大小。
下面是一个添加的示例,用于比较总使用情况(以纳秒为单位的时间):
版本=1 ( vector<unsigned char>
):
clang++ -std=c++14 -stdlib=libc++ main.cpp -DVERSION=1 -ftrapv -Weverything -Wno-c++98-compat -Wno-sign-conversion -Wno-sign-compare -Os && ./a.out
initialization+first use: 16,425,554
array initialization: 12,228,039
first use: 4,197,515
second use: 4,404,043
版本=2 ( vector<uninitialized_char>
):
clang++ -std=c++14 -stdlib=libc++ main.cpp -DVERSION=2 -ftrapv -Weverything -Wno-c++98-compat -Wno-sign-conversion -Wno-sign-compare -Os && ./a.out
initialization+first use: 7,523,216
array initialization: 12,782
first use: 7,510,434
second use: 4,155,241
#include <iostream>
#include <chrono>
#include <vector>
struct uninitialized_char {
unsigned char c;
uninitialized_char() {}
};
void foo(unsigned char *c, int size) {
for (int i = 0; i < size; ++i) {
c[i] = ' ';
}
}
int main() {
auto start = std::chrono::steady_clock::now();
#if VERSION==1
using element_type = unsigned char;
#elif VERSION==2
using element_type = uninitialized_char;
#endif
std::vector<element_type> v(4 * (1<<20));
auto end = std::chrono::steady_clock::now();
foo(reinterpret_cast<unsigned char*>(v.data()), v.size());
auto end2 = std::chrono::steady_clock::now();
foo(reinterpret_cast<unsigned char*>(v.data()), v.size());
auto end3 = std::chrono::steady_clock::now();
std::cout.imbue(std::locale(""));
std::cout << "initialization+first use: " << std::chrono::nanoseconds(end2-start).count() << 'n';
std::cout << "array initialization: " << std::chrono::nanoseconds(end-start).count() << 'n';
std::cout << "first use: " << std::chrono::nanoseconds(end2-end).count() << 'n';
std::cout << "second use: " << std::chrono::nanoseconds(end3-end2).count() << 'n';
}
我正在使用 clang svn-3.6.0 r218006
抱歉,没有办法避免它。
C++11 添加一个构造函数,该构造函数仅采用大小,但即使这样也会对数据进行值初始化。
最好的办法是在堆上分配一个数组,将其粘贴在unique_ptr
(如果可用)中,然后从那里使用它。
如果你愿意,正如你所说,"入侵STL",你总是可以拿一份EASTL来工作。它是某些 STL 容器的变体,允许更受限制的内存条件。您尝试执行的操作的正确实现是为其构造函数提供一个特殊值,该值表示"默认初始化成员",对于 POD 类型,这意味着不执行任何操作来初始化内存。当然,这需要使用一些模板元编程来检测它是否是 POD 类型。
最佳解决方案是简单地更改分配器,不对零参数construct
执行任何操作。这意味着基础类型是相同的,可以躲避任何类型的讨厌的reinterpret_casting和潜在的别名冲突,并且可以非侵入性地取消初始化任何类型的类型。
template<typename T> struct no_initialize : std::allocator<T> {
void construct(T* p) {}
template<typename... Args> void construct(T* p, Args&&... args) {
new (p) T(std::forward<Args>(args)...);
}
};
1 似乎在您的情况下,使用 std::vector
既不必要也不明智。您只需要某个对象为您管理一些原始内存。这可以通过
std::unique_ptr<void, void(*)(void*)> p(std::malloc(n), std::free);
2 如果你真的想使用std::vector<>
你可以使用这里描述的技巧。
使用 vector.reserve() 只分配存储而不初始化它怎么样?
- 向量成员在管理类指针C++队列时丢失
- C++:更新类的向量成员变量
- C++ 入门 12.1:我可以在类中使用静态向量成员实现 StrBlob<string> 吗?
- 具有向量成员的基类<int>
- C++ 向量::使用类对象迭代器擦除不擦除向量成员
- 无法将类构造函数中新创建的对象复制到 C++ 中的向量成员
- 修改另一个类的向量成员时出错
- 返回对私有向量成员元素的非常量引用是否是一种不好的做法
- 如何在 C++ 中将结构类型向量数据输入到结构向量成员(嵌套结构向量)中
- std::向量成员变量 EXC_BAD_ACCESS
- 如何使用其定义附近的显式长度/值构造函数初始化向量成员变量
- "Empty"数组\向量成员 C++
- 如何使用初始值设定项列表初始化 2D 向量成员
- 如何在类定义中初始化向量成员变量
- 如何初始化结构的向量成员
- 使用向量<向量>成员实例化对象时的隔离错误<int>
- 为向量成员和更新元素指定默认值
- 保持类的向量成员与类实例连续
- C++:在类构造函数中填充向量后,向量成员被销毁
- 通过引用另一个类方法传递向量成员变量