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
129 0
|
缓存 安全 编译器
The efficiency of different C++ constructs3
The efficiency of different C++ constructs3
141 0
|
存储 缓存 Unix
The efficiency of different C++ constructs2
The efficiency of different C++ constructs2
130 0
|
缓存 编译器 C++
The efficiency of different C++ constructs
The efficiency of different C++ constructs
136 0
|
5天前
|
存储 编译器 C语言
【c++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
19 2
|
11天前
|
存储 编译器 C++
【c++】类和对象(下)(取地址运算符重载、深究构造函数、类型转换、static修饰成员、友元、内部类、匿名对象)
本文介绍了C++中类和对象的高级特性,包括取地址运算符重载、构造函数的初始化列表、类型转换、static修饰成员、友元、内部类及匿名对象等内容。文章详细解释了每个概念的使用方法和注意事项,帮助读者深入了解C++面向对象编程的核心机制。
37 5
|
17天前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
48 4
|
18天前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
45 4
|
2月前
|
存储 编译器 对象存储
【C++打怪之路Lv5】-- 类和对象(下)
【C++打怪之路Lv5】-- 类和对象(下)
28 4
|
2月前
|
编译器 C语言 C++
【C++打怪之路Lv4】-- 类和对象(中)
【C++打怪之路Lv4】-- 类和对象(中)
25 4