首页 > 编程语言 >如果在循环中不改变vector的大小,C++编译器是否会将.size()优化为常数?

如果在循环中不改变vector的大小,C++编译器是否会将.size()优化为常数?

时间:2024-01-27 14:59:52浏览次数:30  
标签:代码 C++ 编译器 vector 优化 循环 size

  在C++中,可以使用以下代码计算vector<int>中所有元素的和:

vector<int> v = {1, 3, 7, 9};
sums = 0;

for (int i = 0; i < v.size(); i++) {
    sums += v[i];
}

  这是一段很普通的代码,问题在于:在这段代码中,v.size()会在循环开始前仅计算一次?还是会在每次循环中都计算一次?以前我总觉得,这段代码没有改变v的大小,既然我都能看出来,编译器一定也知道,从而会在循环开始前就把v.size()计算出来,之后的每次循环中,直接使用计算出的值,以减少函数调用的开销。但事实果真如此吗?

  使用g++编译上面这段代码,得到的结果如下:

  可以看到,每次循环时会调用v.size()获取vector大小。使用clang编译后的代码也大致类似,也会在每次循环调用v.size()获取大小:

  但这是否就意味着我想错了呢?并非如此。事实上,大家可能忽略了编译器有不同的优化等级,默认情况下,编译器会忠实地按照源代码的描述编译代码。当我们开启更高级别的优化时,就会得到“源代码来了也认不出”的编译结果:

  这里使用clang编译器编译并启用了-O2级别的优化。是的,你没有看错,启用此级别的优化后,循环直接被优化掉了。因为这里的vector<int>中只有4个固定的元素,因此编译器直接使用xmm(一种宽度为128位的加宽寄存器)替代了求和的循环。

  

  为了搞清楚启用优化后.size()是否会被提前计算出,我们创建一个更大的vector:

vector<int> v {};
for (int i = 0; i < 500; i++) {
    v.push_back(i);
}
   
int sums = 0;
for (std::size_t i = 0; i < v.size(); i++) {
    sums += v[i];
}

  启用-O2级别的优化后,与求和循环相关的代码如下图所示:

  可以看到:v.size()会在循环开始前计算出,并保存到寄存器rdx中。在每次循环中,vector的索引(保存在r8中)直接与寄存器rdx中的值进行比较。

由于优化后使用的是128位的xmm寄存器,因此每次循环时r8会加上8.(一个xmm可以保存4个int,以上循环中每轮读取2次数据到xmm2中,因此共4x2=8个int数据)

  因此,在上述代码中,编译器是否会提前计算出.size()取决于启用优化的等级。

标签:代码,C++,编译器,vector,优化,循环,size
From: https://www.cnblogs.com/overxus/p/17991240

相关文章

  • C转C++速成浅入浅出系列——STL之bitset
    本系列为应付考研复试用,知识浅入浅出,很多地方不深究细节原理;如有谬误,欢迎大家指出。bitset【bitset:位集,比特集】理解为比特集。特点是①只能存入0与1②小端存储(可参阅计算机组成原理知识,表现为按b[i]增序输出时会倒序输出)需提供头文件#include<bitset> 创建注:①存储时......
  • c++重载
    函数或运算符重载是指在同一作用域内定义多个具有相同名称但参数类型或数量不同的函数或运算符。重载允许使用相同的名称执行不同的操作,具体的操作根据传递给函数或运算符的参数类型或数量而定。(和Java重载一样直接和Java重载联系到一起)大致分为两类函数和运算符的重载函数重载:......
  • 【C++入门到精通】C++入门 —— list (STL)
    @TOC前言文章绑定了VS平台下std::list的源码,大家可以下载了解一下......
  • 在ubuntu22上使用C++20
    Linux系统ubuntu22.04安装最新版的gcc13.1.0编译器,支持c++20、23_gcc-13.1.0.tar.gz下载-CSDN博客ubantu20安装多个版本的gcc/gc++编译器_ubuntu安装多个gcc-CSDN博客5步在Ubuntu22上使用C++201.安装build-essentialsudoaptinstallbuild-essential安装完检查/us......
  • C++教程——初识c++(循环,判断,跳转语句)
    在程序设计中,循环语句的使用十分重要,不同的需求需要用到不同的循环语句,对各种循环语句的熟练使用是学好程序设计的关键。接下来就来介绍循环语句及其使用。对于while循环来说,注意判断条件的使用,do...while语句要注意,它至少会执行一次do中的代码块,这是需要注意到的,对于for循环来说,括......
  • KY188 哈夫曼树C++
    用(优先队列)小根堆,先构建哈夫曼树,然后在递归遍历输出WPL。 #include<iostream>#include<queue>usingnamespacestd;structnode{intdata;structnode*left;structnode*right;};typedefstructnodetree;booloperator<(treeleft,treeright){......
  • 【C++】前置声明导致的代码含义改变
    真的有这么离谱的事哈哈哈哈。//F.hstructF{};structS:F{};//User.h#include<iostream>structF;structS;structUser{voidf(F*){std::cout<<"F"<<std::endl;}voidf(void*){std::cout<<"void"<......
  • 【C++】 select函数介绍
    在使用C++语言开发网络应用程序时,常常需要用到select函数。select函数是一种多路复用机制,可以同时监听多个文件描述符上可读、可写、异常等事件,从而让程序能够高效地处理多个连接。下面详细介绍C++中的select函数。1.select函数的定义和作用select函数的定义为:intselect(intn......
  • C++ RALL机制理解
    #########################RALL机制(将资源的生命周期与对象的生命周期所绑定(构造获取资源/析构释放资源,利用了栈上的变量在离开作用域的时候会析构的特性)RAII的做法是使用一个对象,在其构造时获取对应的资源,在对象生命期内控制对资源的访问,使之始终保持有效,最后在对象析构的时候,......
  • C++ 单例模式
    单例模式写法:注意:静态成员使用指针的话,程序退出时无法指针类的析构函数,在类内添加回收单例的类,析构的时候将该单例对象析构,就可以了例如:classSingleton{public: staticSingleton*getInstance() { if(m_pInstance==nullptr)//静态成员使用指......