当前位置: 首页 > news >正文

咨询手机网站建设平台怎么开网店

咨询手机网站建设平台,怎么开网店,企业如何进行seo,抚州网站建设表达式模板消除昂贵的中间临时对象 引子:中间容器临时的隐形开销表达式模板代码实现使用模板表达式:不使用模板表达式 深入剖析:延迟计算(Lazy Evaluation)性能提升归因 总结完整代码和运行结果对比一些其他的话 C 编程…

表达式模板消除昂贵的中间临时对象

  • 引子:中间容器临时的隐形开销
  • 表达式模板
    • 代码实现
      • 使用模板表达式:
      • 不使用模板表达式
  • 深入剖析:延迟计算(Lazy Evaluation)
    • 性能提升归因
  • 总结
  • 完整代码和运行结果对比
  • 一些其他的话

C++ 编程中,我们常常会写出如下表达式:

Vector A, B, C, D;  // Vector 底层有一个长度为 n 的数组
A = B + C + D;

引子:中间容器临时的隐形开销

表面上只有一次赋值,背后却是:

auto tmp1 = B + C;     // 遍历写入 tmp1.data[0…n-1]
auto tmp2 = tmp1 + D;  // 再遍历写入 tmp2.data[0…n-1]
A = tmp2;              // 拷贝或移动 entire vector
  • 遍历次数:3
  • 临时容器:2 个 Vector(tmp1, tmp2)
  • 有两次容器级别的内存分配、构造、析构、拷贝/移动

当n很大时,且容器创建时的开销很大时:例如std::vector创建时涉及到堆内存操作,且有一些额外操作。如何减少中间容器是非常重要的

表达式模板

模板表达式是一种延迟计算方法
模板表达式做了两件事:

  • 通过引用记录需要操作的对象(减少拷贝)
  • 设置需要的计算方式(写好操作符实现)

代码实现

// 表达式模板的基础类
template <typename E>
class Expression {public:double operator[](size_t i) const {return static_cast<const E&>(*this)[i];}size_t size() const { return static_cast<const E&>(*this).size(); }
};
// 向量类的表达式模板版本
class VectorET : public Expression<VectorET> {private:std::vector<double> data;public:VectorET(size_t size) : data(size) {}VectorET(const VectorET& other) : data(other.data) {}VectorET& operator=(const VectorET& other) {data = other.data;return *this;}double& operator[](size_t i) { return data[i]; }const double& operator[](size_t i) const { return data[i]; }size_t size() const { return data.size(); }// 赋值运算符重载 - 支持表达式模板template <typename E>VectorET& operator=(const Expression<E>& expr) {for (size_t i = 0; i < size(); ++i) {data[i] = expr[i];}return *this;}
};
// 加法表达式模板
template <typename E1, typename E2>
class AddExpr : public Expression<AddExpr<E1, E2>> {private:const E1& a;const E2& b;public:AddExpr(const E1& a, const E2& b) : a(a), b(b) {}double operator[](size_t i) const { return a[i] + b[i]; }size_t size() const { return a.size(); }
};

关注AddExpr的实现,并不直接拷贝a和b,而是直接通过引用存储(等同于记录指针),此时AddExpr就是一个表达式,记录了a和b之间是一个加法运算。

计算结果呢?加法运算的结果呢?不好意思,还没有,直到你使用operator[]的时候,才会访问a、b的第i个元素,并进行计算返回结果。
这就是延迟计算,结果并不在a+b的时候就已经算出来了,而是在(a+b)[i]的时候才进行计算,并且没有容器级的拷贝,就算有拷贝,也只针对元素进行。

所以最终的结果可以通过一次遍历执行。
我们对比使用和没有使用模板表达式的情况
A = B + C + D;

使用模板表达式:

  1. B + C生成一个中间变量AddExpr<VectorET, VectorET>,但是没有容器拷贝,
  2. AddExpr<VectorET, VectorET> + D 生成第二个中间变量AddExpr<AddExpr<VectorET, VectorET>, VectorET>,没有容器拷贝
  3. AddExpr<AddExpr<VectorET, VectorET>, VectorET>赋值给A,走VectorET的赋值逻辑,对expr[i]进行遍历,expr[i]调用AddExpr::operator[]操作,会递归调用每一个元素的operator[]运算。
template <typename E>VectorET& operator=(const Expression<E>& expr) {for (size_t i = 0; i < size(); ++i) {data[i] = expr[i];}return *this;}

对于没有使用模板表达式的操作,这种行为等同于,减少了中间变量的生成

for (size_t i = 0; i < n; ++i) {A[i] = (B + C + D)[i];
}

不使用模板表达式

A = B + C + D,还会额外生成两个容器中间变量,这两个容器的中间变量如果是std::vector,开销会非常可观。

深入剖析:延迟计算(Lazy Evaluation)

表达式模板的核心在于**“惰性计算”**:

  1. 每次 operator+ 不计算值,只生成一个描述如何计算的节点对象。
  2. 这些节点彼此组合,形成一棵表达式树,但不存储任何中间结果。
  3. 当执行 A = expr; 时,沿树一次性完成所有元素级计算:
    • 无中间容器:避免堆分配、构造、析构
    • 寄存器级临时:标量相加只在寄存器中短暂存在,不触发对象构造
    • 高缓存命中:一次访问避免多次循环
for (size_t i = 0; i < n; ++i) {A[i] = B[i] + C[i] + D[i];
}

之前博主在做分析时,还很纳闷,为什么ai一致说会减少中间变量的生成,后来通过汇编发现了ai漏掉的两个点,或者说ai可能认为这个点不需要重点描述确实非常重要的:

  1. 容器的生成和元素的生成开销是不一样的,假设你有一个n个对象的容器,对比n个对象的生成,加上了容器进行包装,就意味着多了一个容器生成的开销,特别是对于std::vector来说更是如此。假设我们有一个std::vector<double> val(1000);,和doube val[1000],前者的开销大很多,因为std::vector的空间是在堆上申请的,但是double val[1000]的空间是在栈上申请的,申请空间的开销甚至不是一个数量级的!
  2. 编译器对于标量级别的临时变量会做优化,很可能就直接把中间结果放到了寄存器中,而不用构造中间变量了。这就意味着a = b + c + d会生成两个Vector的中间变量,而a[i] = b[i] + c[i] + d[i],如果a[i]是一个int/double这些,很可能都不会生成中间变量。

性能提升归因

  • 合并遍历:3 次→1 次
  • 消除堆分配/析构:临时容器不再出现
  • 提升数据局部性:连续内存访问

总结

通过表达式模板的惰性计算策略,你可以在大型矩阵、图像处理、物理仿真等高性能场景中,零临时容器、单次遍历地完成复杂链式运算,显著提升性能。

完整代码和运行结果对比

#include <chrono>
#include <iostream>
#include <random>
#include <vector>// 传统向量类
class Vector {private:std::vector<double> data;public:Vector(size_t size) : data(size) {}Vector(const Vector& other) : data(other.data) {}Vector& operator=(const Vector& other) {data = other.data;return *this;}double& operator[](size_t i) { return data[i]; }const double& operator[](size_t i) const { return data[i]; }size_t size() const { return data.size(); }// 传统向量加法Vector operator+(const Vector& other) const {Vector result(size());for (size_t i = 0; i < size(); ++i) {result[i] = data[i] + other[i];}return result;}// 传统向量减法Vector operator-(const Vector& other) const {Vector result(size());for (size_t i = 0; i < size(); ++i) {result[i] = data[i] - other[i];}return result;}// 传统向量乘法(与标量)Vector operator*(double scalar) const {Vector result(size());for (size_t i = 0; i < size(); ++i) {result[i] = data[i] * scalar;}return result;}
};// 表达式模板的实现// 表达式模板的基础类
template <typename E>
class Expression {public:double operator[](size_t i) const {return static_cast<const E&>(*this)[i];}size_t size() const { return static_cast<const E&>(*this).size(); }
};// 向量类的表达式模板版本
class VectorET : public Expression<VectorET> {private:std::vector<double> data;public:VectorET(size_t size) : data(size) {}VectorET(const VectorET& other) : data(other.data) {}VectorET& operator=(const VectorET& other) {data = other.data;return *this;}double& operator[](size_t i) { return data[i]; }const double& operator[](size_t i) const { return data[i]; }size_t size() const { return data.size(); }// 赋值运算符重载 - 支持表达式模板template <typename E>VectorET& operator=(const Expression<E>& expr) {for (size_t i = 0; i < size(); ++i) {data[i] = expr[i];}return *this;}
};// 加法表达式模板
template <typename E1, typename E2>
class AddExpr : public Expression<AddExpr<E1, E2>> {private:const E1& a;const E2& b;public:AddExpr(const E1& a, const E2& b) : a(a), b(b) {}double operator[](size_t i) const { return a[i] + b[i]; }size_t size() const { return a.size(); }
};// 减法表达式模板
template <typename E1, typename E2>
class SubExpr : public Expression<SubExpr<E1, E2>> {private:const E1& a;const E2& b;public:SubExpr(const E1& a, const E2& b) : a(a), b(b) {}double operator[](size_t i) const { return a[i] - b[i]; }size_t size() const { return a.size(); }
};// 乘法表达式模板(与标量)
template <typename E>
class MulExpr : public Expression<MulExpr<E>> {private:const E& a;double scalar;public:MulExpr(const E& a, double scalar) : a(a), scalar(scalar) {}double operator[](size_t i) const { return a[i] * scalar; }size_t size() const { return a.size(); }
};// 运算符重载,返回表达式对象而不是实际计算结果
template <typename E1, typename E2>
AddExpr<E1, E2> operator+(const Expression<E1>& a, const Expression<E2>& b) {return AddExpr<E1, E2>(static_cast<const E1&>(a),static_cast<const E2&>(b));
}template <typename E1, typename E2>
SubExpr<E1, E2> operator-(const Expression<E1>& a, const Expression<E2>& b) {return SubExpr<E1, E2>(static_cast<const E1&>(a),static_cast<const E2&>(b));
}template <typename E>
MulExpr<E> operator*(const Expression<E>& a, double scalar) {return MulExpr<E>(static_cast<const E&>(a), scalar);
}template <typename E>
MulExpr<E> operator*(double scalar, const Expression<E>& a) {return MulExpr<E>(static_cast<const E&>(a), scalar);
}// 性能测试函数
void runTraditionalTest(size_t size, int iterations) {// 初始化向量Vector a(size), b(size), c(size), d(size), result(size);std::random_device rd;std::mt19937 gen(rd());std::uniform_real_distribution<> dis(0.0, 1.0);for (size_t i = 0; i < size; ++i) {a[i] = dis(gen);b[i] = dis(gen);c[i] = dis(gen);d[i] = dis(gen);}auto start = std::chrono::high_resolution_clock::now();// 执行多次计算以测量性能for (int iter = 0; iter < iterations; ++iter) {result = a + b - c * 2.0 + d * 3.0;}auto end = std::chrono::high_resolution_clock::now();std::chrono::duration<double, std::milli> elapsed = end - start;std::cout << "传统方法: " << elapsed.count() << " ms" << std::endl;
}void runExpressionTemplateTest(size_t size, int iterations) {// 初始化向量VectorET a(size), b(size), c(size), d(size), result(size);std::random_device rd;std::mt19937 gen(rd());std::uniform_real_distribution<> dis(0.0, 1.0);for (size_t i = 0; i < size; ++i) {a[i] = dis(gen);b[i] = dis(gen);c[i] = dis(gen);d[i] = dis(gen);}auto start = std::chrono::high_resolution_clock::now();// 执行多次计算以测量性能for (int iter = 0; iter < iterations; ++iter) {result = a + b - c * 2.0 + d * 3.0;}auto end = std::chrono::high_resolution_clock::now();std::chrono::duration<double, std::milli> elapsed = end - start;std::cout << "表达式模板: " << elapsed.count() << " ms" << std::endl;
}int main() {const size_t size = 1000000;const int iterations = 100;std::cout << "向量大小: " << size << ", 迭代次数: " << iterations<< std::endl;// 先运行传统方法,再运行表达式模板方法runTraditionalTest(size, iterations);runExpressionTemplateTest(size, iterations);return 0;
}

运行结果
在这里插入图片描述

一些其他的话

  • 如果容器复制的开销很小,模板表达式的优点也就没那么明显了,例如把std::vector换成std::array,甚至有可能生成模板的开销更大[狗头]。
http://www.dt0577.cn/news/19734.html

相关文章:

  • 网站弹窗页面是谁做的百度的链接
  • 广州做网站建设哪家专业在线建站平台免费建网站
  • 响应式布局网站长尾关键词搜索网站
  • 做外贸怎么在阿里云建网站西安网站seo工作室
  • 网站建设及优化的策划书软文自助发稿平台oem
  • 怀远县建设局网站怎么做市场推广
  • 贵阳商城网站建设seo课
  • 网站没被收录怎么办企业网站快速建站
  • 有ecs怎么做网站关键词林俊杰无损下载
  • 网页制作的网站seo渠道是什么意思
  • 安顺北京网站建设seo搜索是什么
  • 建网站哪个公司好sem是什么?
  • 洋县住房和城乡建设管理局网站北京关键词优化报价
  • 视频网站后台功能现在做网络推广都有什么方式
  • 网站为什么做微云的采集如何让百度收录自己的网站
  • 大城县有做网站的吗百度广告管家
  • 做网站什么什么重庆网站排名公司
  • 门户网站样式360seo优化
  • 做合成照片的国外网站关键词优化工具
  • 杭州建设网站网站成都百度seo公司
  • 网站开发商城图片上传成品网站1688入口的功能介绍
  • 怎么做网站内容互联网营销案例
  • 用asp.net做企业网站外链网盘下载
  • 学校做网站方案seo查询网站是什么
  • 龙南城市建设局网站网站建设制作公司
  • 威海做网站的哪家好seo推广思路
  • 怎么做自己的公司网站放在百度太原seo网站排名
  • 网站的相关链接怎么做东莞网络营销平台
  • h5响应式网站建设武汉网站建设方案优化
  • 响应式网站一般怎么设计seo排名工具给您好的建议下载官网