首页 > 编程语言 >C语言面向对象风格编程解惑-全局变量性能分析

C语言面向对象风格编程解惑-全局变量性能分析

时间:2024-07-25 11:09:14浏览次数:10  
标签:int res self long C语言 else 解惑 全局变量

C语言面向对象风格编程解惑-全局变量性能分析

如果你是CPP老手,但在软件开发过程中要求采用C语言作为主要语言,首先遇到的是各种设计模式不方便应用了,感到非常困扰,然后就是认命之后走向另外一个极端,常常会有过度使用全局变量和goto语句的问题。
CPP既然是C With Class,自然不会排斥面向对象风格编程,大家可以参考
1.《Object-Oriented Programming With ANSI-C》
2. C语言:春节回家过年,我发现只有我没有对象!

本文先尝试分析全局变量的问题。
程序员为什么常常不由自主的使用全局变量呢?
全局变量主要的优点:

  1. 内存地址固定,方便C语言优化器进行极致优化。
  2. 全局可见,任何一个函数或线程都可以读写全局变量,貌似节省变量声明的代码。

全局变量主要的缺点:

  1. 降低代码的可读性,阅读代码是大脑中需要有一个全局词典,并且一旦查字典就相当一次系统IO中断,并且如果有同名变量就是多进程多线程切换。
  2. 生命周期超长,全局变量存放在静态存储区,系统需要为其分配内存,一直到程序结束。
  3. 多个有依赖关系的全局变量的初始化和释放次序需要仔细斟酌。

现在进入本文重点:C语言全局变量风格会提升性能吗?
尤其是在一个函数中访问多个全局变量的场景中。
直接上代码:
面向对象(结构)风格

struct Student {
    int age;
    int level;
    char name[64] ;
    int sex ;
    time_t birthday ;
};

int CheckCpp(struct Student *self,int limitAge,int limitLevel,int limitLen,long int *checkCount) {
    int res = 0 ;
    if (self->age > limitAge) {
        res = 1 ;
    } else if (self->level < limitLevel){
        res = 2 ;
    } else if (strlen(self->name) > limitLen) {
        res = 3 ;
    } else if (self->sex > 1) {
        res = 4 ;
    } else if (self->birthday > 1000000) {
        res = 5 ;
    }
    // 增加一个计数器防止优化器超级优化
    *checkCount = *checkCount + 1 ;
    return res ;
}

全局变量风格

int self_age;
int self_level;
char self_name[64] ;
int self_sex ;
time_t self_birthday ;

int CheckC(int limitAge,int limitLevel,int limitLen,long int *checkCount) {
    int res = 0 ;
    if (self_age > limitAge) {
        res = 1 ;
    } else if (self_level < limitLevel){
        res = 2 ;
    } else if (strlen(self_name) > limitLen) {
        res = 3 ;
    } else if (self_sex > 1) {
        res = 4 ;
    } else if (self_birthday > 1000000) {
        res = 5 ;
    }
    // 增加一个计数器防止优化器超级优化
    *checkCount = *checkCount + 1 ;
    return res ;
}

两种代码分别运行计时

typedef unsigned long long TimestampTz ;
unsigned long long GetCurrentTimestamp(void)
{
    unsigned long long result = 0;
    struct timeval tp;

    gettimeofday(&tp, NULL);

    result = (unsigned long long) tp.tv_sec ;
    return (result * 1000000) + tp.tv_usec;
}
TimestampTz TestCppCount(struct Student *self,long int count){
    TimestampTz startMs ,useMs;
    int resSum = 0  ;
    startMs = GetCurrentTimestamp();
    for(long int z = 0 ; z < count ; z ++) {
        for( int i = 0 ; i < 20 ; i ++) {
            resSum += CheckCpp(self,60 + i,1,100,&myCheckCpp);
        }
    }
    useMs = GetCurrentTimestamp() - startMs;
    if (resSum <= 0)
        startMs += resSum ;
    return useMs ;
}

TimestampTz TestCount(long int count){
    TimestampTz startMs ,useMs;
    int resSum = 0  ;
    startMs = GetCurrentTimestamp();
    for(long int z = 0 ; z < count ; z ++) {
        for( int i = 0 ; i < 20 ; i ++) {
            resSum += CheckC(60,1,100,&myCheckC);
        }
    }
    useMs = GetCurrentTimestamp() - startMs;
    if (resSum <= 0)
        startMs += resSum ;
    return useMs ;
}

count = 100000000 ,相当于都运行2000,000,000次,使用gcc和g++的O1级别优化器分别编译运行,输出结果如下:

[ansible@pg16 testcpp]$ ./testc
c with class : 8603015  us, times: 2000000000
c : 9735154  us,  times: 2000000000

[ansible@pg16 testcpp]$ ./testcpp
c with class: 8437410 us, times:2000000000
c : 9928332 us, times:2000000000

[ansible@pg16 testcpp]$ ./testcpp
c with class : 8492479 us, times:2000000000
c : 8739781 us, times:2000000000

几次的结果有一定的偏差,但是级数是稳定的,每次运行都在4纳秒左右,甚至cpp版本还高一点儿,对比生成的汇编代码
汇编比较

可以看到,struct版本采用rdi,rbx 相对寻址,而全部变量风格,则采用rip相对寻址,而全局变量地址在编译时确定,所以访问多个全局变量的Cache命中率是不稳定的,而结构成员变量的地址一般是连续的,所以其多个成员变量寻址时,Cache命中率是可控的。

总结:

  1. 全局变量因为寻址问题,并不能一定产生性能优化
  2. 相对代码可读性带来的困扰,慎用全局变量
  3. 推荐在C语言中使用面向对象风格编程,类似Linux中fopen,fread系列

标签:int,res,self,long,C语言,else,解惑,全局变量
From: https://www.cnblogs.com/fanzhenyong/p/18322528

相关文章

  • Tower Of Hanoi - 汉诺塔问题(C语言)
    ☆WelcometoHouse'sblog!☆本人主页:神王豪斯(重拾基础期)-CSDN博客所属专栏:重拾C语言——神王降世的第一步!_神王豪斯(重拾基础期)的博客-CSDN博客1.游戏规则-有三根柱子(通常分别命名为A、B、C)和若干大小不同的圆盘。-最初,所有圆盘按照从大到小的顺序堆叠在一根柱子(比如......
  • 【 C语言 】 C语言设计模式
    一、C语言和设计模式(继承、封装、多态)C++有三个最重要的特点,即继承、封装、多态。我发现其实C语言也是可以面向对象的,也是可以应用设计模式的,关键就在于如何实现面向对象语言的三个重要属性。(1)继承性[cpp]viewplaincopytypedefstruct_parent{intdata_parent;......
  • C语言程序设计练习(三)
    1.整型数据类型存储空间大小#include<stdio.h>intmain(){printf("Sizeofint:%zubytes\n",sizeof(int));printf("Sizeofshort:%zubytes\n",sizeof(short));printf("Sizeoflong:%zubytes\n",sizeof(l......
  • C语言:数组
    hello,大家好今天我们来讲解c语言中数组的知识。一、数组的概念数组是⼀组相同类型元素的集合;数组中存放的是1个或者多个数据,但是数组元素个数不能为0。数组中存放的多个数据,类型是相同的。数组分为一维数组和多维数组,多维数组一般比较多见的是二维数组。二、一维数组 1......
  • c语言--数组详解
    数组的概念数组是一组相同类型元素的集合;从这个概念我们就可以发现2个有价值的信息:数组中存放的是1个或多个数据,但是数组的元素不能为0。数组中存放的多个数据,类型是相同的。数组分为一维数组和多维数组,多维数组一般比较多见的是二维数组。一维数组1.一维数组的创建和初......
  • C语言学习day03
    变量概念表面:程序运行过程中取值可以改变的数据实质:变量其实代表了一块内存区域/单元/空间。变量名可视为该区域的标识。整个变量分为三部分:  变量名:这个只是变量的一个标识,我们借助变量名来存取数据。  变量空间/内存单元:这个就是内存中分配的一块用来存储数据的......
  • C语言——数据类型
    C语言——数据类型C语言中的数据类型种类整型整型的常量形式整型的变量形式整型类型的分类整型数据在内存中的存储浮点型浮点型的大小浮点型数据的存储浮点数的比较问题字符型C语言中的数据类型种类数据类型可分为基本数据类型(整型,浮点型,字符型,枚举类型),构造数据类......
  • c语言-数组(1)
    5.数组(1)数组的意义:保存多个具有相同数据类型的数据特点:(1)具有相同的数据类型。(2)数据的地址是连续的 数组的表现形式类型标识符[长度];数组的空间大小 数组的空间大小=单个数据的空间大小*长度tip:已知数组table,求该数组的长度?intl=sizeof(table)/sizeof(......
  • C语言 ——— 函数指针数组的讲解及其用法
    目录前言函数指针数组的定义函数指针数组的使用前言数组是存放一组相同类型数据的存储空间关于指针数组的知识请见:C语言———指针数组&指针数组模拟二维整型数组-CSDN博客那么要将多个函数的地址存储到数组中,这个数组该如何定义呢?函数指针数组的定义把多个函数......
  • c语言(7.24)
    今天学习了指针的两个作用#include<stdio.h>voidgetmaxandmin(intarr[],intlen,int*max,int*min);intmain(){ //指针作用2: //函数返回多个值 //练习: //定义一个函数,求数组的最大值和最小值,并进行返回 intarr[]={1,2,3,4,5,6,7,8,9}; intlen=sizeof......