- 主题:现代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.*