- 主题:现代C++中,比较大的结构体一般怎么使用容器?
gpt回答的真好。
我再确认下, vec.push_back(TradeRecode{ })这种写法,是直接在vector已分配的内存上构建对象,不会发生复制吧?
如果是这样,用值就很好了。
【 在 gfkid 的大作中提到: 】
: gpt4推荐直接使用值
: 在现代 C++ 中,确实鼓励避免直接使用裸指针,主要是为了提高代码的安全性和可维护性。对于您提到的 `TradeRecord` 结构,可以使用智能指针(如 `std::unique_ptr` 或 `std::shared_ptr`)来管理动态分配的对象,或者直接使用值语义(即直接存储对象本身,而不是指针)。
: ### 使用智能指针
: ...................
--
FROM 223.72.40.*
按之前的说法,取决于TradeRecode默认构造函数的实现,你这个我理解应该会新建的吧。
【 在 finlab 的大作中提到: 】
: gpt回答的真好。
: 我再确认下, vec.push_back(TradeRecode{ })这种写法,是直接在vector已分配的内存上构建对象,不会发生复制吧?
: 如果是这样,用值就很好了。
: ...................
--
FROM 114.94.8.*
感觉把字节对齐一下比较好
编译器很强大,也许不需要考虑那么多了,担心的话就把copy构造和copy赋值标记为delete。
【 在 finlab 的大作中提到: 】
: gpt回答的真好。
: 我再确认下, vec.push_back(TradeRecode{ })这种写法,是直接在vector已分配的内存上构建对象,不会发生复制吧?
: 如果是这样,用值就很好了。
: ...................
--
FROM 114.249.28.*
嗯,按照8字节对齐了。
c++11后,vector提供emplace_back方法,支持就地创建,拷贝、移动都不会发生。
不过这个函数直接接受构造函数的参数,不接受初始化列表,不发生隐式类型转换。
所以就有个小限制
m_records.push_back({
.bar_id = bar,
.trade_count = c,
.trade_price = price,
.total_count = rec0.total_count + c,
.total_cost = rec0.total_cost + c * price * (1 + fee),
.profit = 0,
.cash = rec0.cash - c * price * (1 + fee),
});
这样的用法就不能用了。如果要改成emplace_back,就只能按顺序提供参数,无法按照字段名称,不过问题也不大
m_records.emplace_back({
bar,
c,
price,
rec0.total_count + c,
rec0.total_cost + c * price * (1 + fee),
0,
rec0.cash - c * price * (1 + fee),
});
性能问题解决了,看着不太完美。
【 在 gfkid 的大作中提到: 】
: 感觉把字节对齐一下比较好
: 编译器很强大,也许不需要考虑那么多了,担心的话就把copy构造和copy赋值标记为delete。
--
FROM 223.72.40.*
加上这句:m_records.emplace_back(0, 0, .0, 0, .0, .0, Capital );
编译报错:“TradeRecord::TradeRecord(TradeRecord &&)”: 尝试引用已删除的函数
把TradeRecord的移动构造函数delete之后,emplace_back 编译通不过
定位到一个叫construct_at的内部函数,难道emplace_back还是要调用到移动构造?
那这个函数就没有意义了。
【 在 finlab 的大作中提到: 】
: 嗯,按照8字节对齐了。
: c++11后,vector提供emplace_back方法,支持就地创建,拷贝、移动都不会发生。
: 不过这个函数直接接受构造函数的参数,不接受初始化列表,不发生隐式类型转换。
: ...................
--
修改:finlab FROM 223.72.40.*
FROM 223.72.40.*
[size][7]是aos, array of structures
如果遍历的时候不会使用每个元素的所有数据,
用[7][size],即soa, structure of arrays性能更好,可以提高缓存命中率
【 在 finlab 的大作中提到: 】
: 我原来的方案就是直接用二维数组(double[size][7])放全部数据,结构体都没用
: 一行就是一个记录,应该没有比这个开销最小了。
: 这不是想在不太影响性能的情况下,让程序更“现代”些。
: ...................
--
修改:dilemma FROM 114.250.192.*
FROM 114.250.192.*
这里按行访问和按列访问的情况都有,还不好说什么方式更合适。
【 在 dilemma 的大作中提到: 】
: [size][7]是aos, array of structures
: 如果遍历的时候不会使用每个元素的所有数据,
: 用[7][size],即soa, structure of arrays性能更好,可以提高缓存命中率
: ...................
--
FROM 223.72.40.*
你这才几个字节,100都不到,没1K字节别说自己大。
现代CPU处理能力高得很,你这点开销跟白送一样。用指针反而多搞出一次引用,搞不好更影响性能。
花功夫前先profile一下,确定这个是不是瓶颈,再说怎么优化。
【 在 finlab 的大作中提到: 】
: 比如下面这样的:
: struct TradeRecord
: {
: ...................
--
FROM 36.112.198.*
【 在 finlab 的大作中提到: 】
: 我原来的方案就是直接用二维数组(double[size][7])放全部数据,结构体都没用
: 一行就是一个记录,应该没有比这个开销最小了。
: 这不是想在不太影响性能的情况下,让程序更“现代”些。
: ...................
那使用std::array好了
--
FROM 61.48.14.*
gpt4认为这个性能差异微不足道。
对于您的 `TradeRecord` 结构体,使用具名初始化和使用参数列表初始化的性能应该是非常接近的,因为在这两种情况下,涉及的操作基本相同。
1. **具名初始化**:
```cpp
m_records.emplace_back(TradeRecord {
.bar_id = bar,
.trade_count = c,
.trade_price = price,
.total_count = rec0.total_count + c,
.total_cost = rec0.total_cost + c * price * (1 + fee),
.profit = 0,
.cash = rec0.cash - c * price * (1 + fee),
});
```
这里,您使用了聚合初始化(具名初始化),创建了一个 `TradeRecord` 的临时对象。由于 `TradeRecord` 是一个简单的聚合类型,这种初始化是非常高效的。
2. **参数列表初始化**:
```cpp
m_records.emplace_back({
bar,
c,
price,
rec0.total_count + c,
rec0.total_cost + c * price * (1 + fee),
0,
rec0.cash - c * price * (1 + fee),
});
```
在这种情况下,您提供了一个初始化列表,它被用来直接在 `vector` 的内部构造 `TradeRecord` 对象。这也是一个高效的操作,因为它涉及的是基本数据类型的直接赋值。
### 性能比较
- 由于 `TradeRecord` 只包含基本数据类型,这两种方法都涉及到直接赋值这些类型的值。基本数据类型的赋值操作是非常高效的,所以这两种方法的性能差异应该是微不足道的。
- 两种情况下,都不涉及复杂的内存操作或资源管理,这意味着性能上几乎没有差别。
- 如果使用的是 C++17 或更高版本,具名初始化(结构化绑定)的语法更清晰,提高了代码的可读性。但从性能角度来看,两者之间的差异几乎可以忽略不计。
### 结论
对于您的场景,无论是使用具名初始化还是参数列表初始化,对于 `std::vector<TradeRecord>` 的 `emplace_back` 方法来说,性能上的差异几乎可以忽略不计。您可以根据代码可读性和个人偏好来选择使用哪种方法。
【 在 finlab 的大作中提到: 】
: 嗯,按照8字节对齐了。
: c++11后,vector提供emplace_back方法,支持就地创建,拷贝、移动都不会发生。
: 不过这个函数直接接受构造函数的参数,不接受初始化列表,不发生隐式类型转换。
: ...................
--
FROM 114.249.28.*