The efficiency of different C++ constructs4

简介: The efficiency of different C++ constructs4

这章主要介绍c++语言中各种特性对性能的影响。


不同的变量存储位置


stack


众所周知,函数中的临时变量或对象一般存储在内存空间中的stack区。每当调用函数时,参数和临时变量进栈,当函数返回时,参数和临时变量出。s由于stack可被不断重复使用,栈是内存空间中最高效的存储方式。当临时变量中没有大对象时,访问栈上的临时变量也基本能用上L1 data cache.


global or static


在函数体之外声明的变量称之为global变量,可被任何函数访问。被static修饰的变量称为static变量。


global和static变量在程序运行期间会被放置于内存空间中的静态数据区。静态数据区域分为三个部分:一部分存储const类型的global/static变量,一部分存储已被初始化的global/static变量,最后一部分存储未被初始化的global/static变量


使用静态数据区的好处是,global/static变量在程序启动前就有专门的存储位置,坏处是在程序的生命周期内,这些存储位置将被一直占据,可能会降低data cache的效率。


所以建议尽量不要使用global变量


register


register变量存储在cpu寄存器中,函数中的临时变量特别适合放到register中。优点很明显,访问register变量比访问RAM快得多,但是cpu寄存器大小是非常有限的,在64位x86架构中,有:


  • 14个整数寄存器


  • 16个浮点寄存器


volatile


volatile用于声明一个变量可被其他线程改变,阻止编译器依赖变量始终具有代码中先前分配的值的假设来进行优化 。

volatile int seconds; // incremented every second by another thread
void DelayFiveSeconds()
{
  seconds = 0;
while (seconds < 5)
  {
// do nothing while seconds count to 5
  }
}

上面的代码如果不声明为volatile, 编译器将任务while条件一直成立,即使别的线程中改变了seconds的值。



thread-local


大多数编译器可以使用关键字 __thread__declspec(thread) 来实现静态变量和全局变量的线程本地存储。这样的变 量对于每个线程都有一个实例 。


线程本地存储是低效的,因为它是通过存储在线程访问块中的指针进行访问的。因此建议尽量避免线程本地存储,代之以stack存储。


dynamic memory allocation


c++中通过new/malloc动态分配存储,通过delete/free释放动态分配的的存储,动态分配的存储放在内存空间的heap区中。


优点:使用相对stack存储更加灵活

缺点:动态分配和释放很耗时,容易出现野指针、悬垂指针、内存泄露、内存碎片等问题。


variables declared inside a class


类中声明的变量按照在类中的顺序存储,存储位置由类的对象在哪里定义的决定。static修饰的类成员变量将存储在静态数据区,只有一个实例。


将变量存储在类中的好处是保证了空间局部性,对cpu data cache更友好



整型变量和运算符


整数大小


对于不同的平台,不同整数类型(char/short/int/long)的大小可能不同。


无论大小如何,整数运算基本都很快,除非使用了大于cpu寄存器大小的类型,比如在32位系统中使用64位整数。


建议在与大小无关且没有溢出风险的情况下使用默认整数大小,例如简单变量、循环计数器等。 在大型数组中,为了更好地 使用数据缓存,最好使用对于特定用途来说足够大的最小整数大小。


无符号整形数 vs. 有符号整形数


在大多数情况下,使用有符号整数和无符号整数在速度上没有区别。 除了

  1. 除以常数:当你将一个整数除以一个常数时,无符号要快于有符号


  1. 对于大多数指令集,有符号整数比无符号整数转换成浮点数要快


  1. 有符号变量和无符号变量的溢出行为不同。



整数运算符


整数运算非常快。加减和位操作只需一个时钟周期,乘法需要3-4个时钟周期,除法需要40-80个。



自增和自减运算符


当仅用于递增整数变量时,使用递增前或递增后都没有区别。效果完全相同。例如,for (i=0; i<n; i++)和for (i=0; i<n; ++i)是一样的。但是当使用表达式的结果时,效率可能会有所不同。例如,x = array[i++] 比 x = array[++i] 速度更快,因为在后一种情况下,数组元素的地址的计算必须等待 i 的新值,这将使 x 的可用性延迟大约两个时钟周期。

相关文章
|
存储 缓存 并行计算
The efficiency of different C++ constructs5
The efficiency of different C++ constructs5
115 0
|
缓存 安全 编译器
The efficiency of different C++ constructs3
The efficiency of different C++ constructs3
125 0
|
存储 缓存 Unix
The efficiency of different C++ constructs2
The efficiency of different C++ constructs2
113 0
|
缓存 编译器 C++
The efficiency of different C++ constructs
The efficiency of different C++ constructs
124 0
|
29天前
|
C++ 容器
C++中自定义结构体或类作为关联容器的键
C++中自定义结构体或类作为关联容器的键
30 0
|
29天前
|
存储 算法 搜索推荐
【C++】类的默认成员函数
【C++】类的默认成员函数
|
8天前
|
存储 编译器 C++
C ++初阶:类和对象(中)
C ++初阶:类和对象(中)
|
8天前
|
C++
C++(十六)类之间转化
在C++中,类之间的转换可以通过转换构造函数和操作符函数实现。转换构造函数是一种单参数构造函数,用于将其他类型转换为本类类型。为了防止不必要的隐式转换,可以使用`explicit`关键字来禁止这种自动转换。此外,还可以通过定义`operator`函数来进行类型转换,该函数无参数且无返回值。下面展示了如何使用这两种方式实现自定义类型的相互转换,并通过示例代码说明了`explicit`关键字的作用。
|
8天前
|
存储 设计模式 编译器
C++(十三) 类的扩展
本文详细介绍了C++中类的各种扩展特性,包括类成员存储、`sizeof`操作符的应用、类成员函数的存储方式及其背后的`this`指针机制。此外,还探讨了`const`修饰符在成员变量和函数中的作用,以及如何通过`static`关键字实现类中的资源共享。文章还介绍了单例模式的设计思路,并讨论了指向类成员(数据成员和函数成员)的指针的使用方法。最后,还讲解了指向静态成员的指针的相关概念和应用示例。通过这些内容,帮助读者更好地理解和掌握C++面向对象编程的核心概念和技术细节。
|
29天前
|
存储 安全 编译器
【C++】类和对象(下)
【C++】类和对象(下)
【C++】类和对象(下)