我的编程空间,编程开发者的网络收藏夹
学习永远不晚

C语言算法举例分析

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

C语言算法举例分析

这篇文章主要介绍“C语言算法举例分析”,在日常操作中,相信很多人在C语言算法举例分析问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C语言算法举例分析”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

最近,我一直在开发Dynvm——一个通用的动态语言运行时。就像其他任何好的语言运行时项目一样,开发是由基准测试程 序驱动的。因此,我一直在用基准测试程序测试各种由不同语言编写的算法,以此对其典型的运行速度有一个感觉上的认识。一个经典的测试就是迭代计算斐波那契 数列。为简单起见,我以2^64为模,用两种语言编写实现了该算法。

用Python语言实现如下:

SZ = 2**64 i = 0 a, b = 1, 0 while i < n:     t = b     b = (b+a) % SZ     a = t     i = i + 1 return b

用C语言实现如下:

#include <stdio.h> #include <stdlib.h> typedef unsigned long ulong;   int main(int argc, char *argv[]) {     ulong n = atoi(argv[1]);     ulong a = 1;     ulong b = 0;     ulong t;       for(ulong i = 0; i < n; i++) {         t = b;         b = a+b;         a = t;     }       printf("%lu\n", b);     return 0; }

用其他语言实现的代码示例,我已放在github上。

Dynvm包含一个基准测试程序框架,该框架可以允许在不同语言之间对比运行速度。在一台Intel i7-3840QM(调频到1.2 GHz)机器上,当 n=1,000,000 时,对比结果如下:

=======================================================   语言                               时间 (秒) =======================================================   Java                               0.133   C Language                         0.006   CPython                            0.534   Javascript V8                      0.284

很明显,C语言是这里的老大,但是java的结果有点误导性,因为大部分的时间是由JIT编译器启动(~120ms)占用的。当n=100,000,000时,结果变得更明朗:

=======================================================   语言                              时间(秒) =======================================================   Java                               0.300   C Language                         0.172   CPython                            47.909   Javascript V8                      24.179

在这里,我们探究下为什么C语言在2013年仍然很重要,以及为什么编程世界不会完全“跳槽”到Python或者 V8/Node。有时你需要原始性能,但是动态语言仍在这方面艰难挣扎着,即使对以上很简单的例子而言。我个人相信这种情况会克服掉,通过几个项目我们能 在这方面看到很大的希望:JVM、V8、PyPy、LuaJIT等等,但在2013年我们还没有到达“目的地”。

然而,我们无法回避这样的问题:为什么差距如此之大?在C语言和Python之间有278.5倍的性能差距!最不可思议的地方是,从语法角度讲,以上例子中的C语言和Python内部循环基本上一模一样。

为了找到问题的答案,我搬出了反汇编器,发现了以下现象:

0000000000400480 <main>: 247   400480:       48 83 ec 08             sub    $0x8,%rsp 248   400484:       48 8b 7e 08             mov    0x8(%rsi),%rdi 249   400488:       ba 0a 00 00 00          mov    $0xa,%edx 250   40048d:       31 f6                   xor    %esi,%esi 251   40048f:       e8 cc ff ff ff          callq  400460 <strtol@plt> 252   400494:       48 98                   cltq 253   400496:       31 d2                   xor    %edx,%edx 254   400498:       48 85 c0                test   %rax,%rax 255   40049b:       74 26                   je     4004c3 <main+0x43> 256   40049d:       31 c9                   xor    %ecx,%ecx 257   40049f:       31 f6                   xor    %esi,%esi 258   4004a1:       bf 01 00 00 00          mov    $0x1,%edi 259   4004a6:       eb 0e                   jmp    4004b6 <main+0x36> 260   4004a8:       0f 1f 84 00 00 00 00    nopl   0x0(%rax,%rax,1) 261   4004af:       00 262   4004b0:       48 89 f7                mov    %rsi,%rdi 263   4004b3:       48 89 d6                mov    %rdx,%rsi 264   4004b6:       48 83 c1 01             add    $0x1,%rcx 265   4004ba:       48 8d 14 3e             lea    (%rsi,%rdi,1),%rdx 266   4004be:       48 39 c8                cmp    %rcx,%rax 267   4004c1:       77 ed                   ja     4004b0 <main+0x30> 268   4004c3:       be ac 06 40 00          mov    $0x4006ac,%esi 269   4004c8:       bf 01 00 00 00          mov    $0x1,%edi 270   4004cd:       31 c0                   xor    %eax,%eax 271   4004cf:       e8 9c ff ff ff          callq  400470 <__printf_chk@plt> 272   4004d4:       31 c0                   xor    %eax,%eax 273   4004d6:       48 83 c4 08             add    $0x8,%rsp 274   4004da:       c3                      retq 275   4004db:       90                      nop

(译注:

  • 不同的编译器版本及不同的优化选项(-Ox)会产生不同的汇编代码。

  • 反汇编方法:gcc -g -O2 test.c -o test;objdumb -d test>test.txt  读者可以自己尝试变换优化选项对比反汇编结果)

最主要的部分是计算下一个斐波那契数值的内部循环:

262   4004b0:       48 89 f7                mov    %rsi,%rdi 263   4004b3:       48 89 d6                mov    %rdx,%rsi 264   4004b6:       48 83 c1 01             add    $0x1,%rcx 265   4004ba:       48 8d 14 3e             lea    (%rsi,%rdi,1),%rdx 266   4004be:       48 39 c8                cmp    %rcx,%rax 267   4004c1:       77 ed                   ja     4004b0 <main+0x30>

变量在寄存器中的分配情况如下:

a:  %rsi b:  %rdx t:  %rdi i:  %rcx n:  %rax

262和263行实现了变量交换,264行增加循环计数值,虽然看起来比较奇怪,265行实现了b=a+t。然后做一个简单地比较,***一个跳转指令跳到循环开始出继续执行。

手动反编译以上代码,代码看起来是这样的

loop:   t = a         a = b         ii = i+1         b = a+t         if(n > i) goto loop

整个内部循环仅用六条X86-64汇编指令就实现了(很可能内部微指令个数也差不多。译者注:Intel X86-64处理器会把指令进一步翻译成微指令,所以CPU执行的实际指令数要比汇编指令多)。CPython解释模块对每一条高层的指令字节码至少需要六条甚至更多的指令来解释,相比而言,C语言完胜。除此之外,还有一些其他更微妙的地方。

拉低现代处理器执行速度的一个主要原因是对于主存的访问。这个方面的影响十分可怕,在微处理器设计时,无数个工程时 (engineering  hours)都花费在找到有效地技术来“掩藏”访存延时。通用的策略包括:缓存、推测预取、load-store依赖性预测、乱序执行等等。这些方法确实 在使机器更快方面起了很大作用,但是不可能完全不产生访存操作。

在上面的汇编代码中,从没访问过内存,实际上变量完全存储在CPU寄存器中。现在考虑CPython:所有东西都是堆上 的对象,而且所有方法都是动态的。动态特性太普遍了,以至于我们没有办法知道,a+b执行integer_add(a,  b)、string_concat(a,  b)、还是用户自己定义的函数。这也就意味着很多时间花在了在运行时找出到底调用了哪个函数。动态JIT运行时会尝试在运行时获取这个信息,并动态产生 x86代码,但是这并不总是非常直接的(我期待V8运行时会表现的更好,但奇怪的是它的速度只是Python的0.5倍)。因为CPython是一个纯粹 的翻译器,在每个循环迭代时,很多时间花在了解决动态特性上,这就需要很多不必要的访存操作。

除了以上内存在搞鬼,还有其他因素。现代超标量乱序处理器核一次性可以取好几条指令到处理器中,并且“在最方便时”执行 这些指令,也就是说:鉴于结果仍然是正确的,指令执行顺序可以任意。这些处理器也可以在同一个时钟周期并行执行多条指令,只要这些指令是不相关的。 Intel Sandy Bridge  CPU可以同时将168条指令重排序,并可以在一个周期中发射(即开始执行指令)至多6条指令,同时结束(即指令完成执行)至多4条指令!粗略地以上面斐 波那契举例,Intel这个处理器可以大约把28(译者注:28*6=168)个内部循环重排序,并且几乎可以在每一个时钟周期完成一个循环!这听起来很 霸气,但是像其他事一样,细节总是非常复杂的。

我们假定8条指令是不相关的,这样处理器就可以取得足够的指令来利用指令重排序带来的好处。对于包含分支指令的指令流进 行重排序是非常复杂的,也就是对if-else和循环(译者注:if-else需要判断后跳转,所以必然包含分支指令)产生的汇编代码。典型的方法就是对 于分支指令进行预测。CPU会动态的利用以前分支执行结果来猜测将来要执行的分支指令的执行结果,并且取得那些它“认为”将要执行的指令。然而,这个推测 有可能是不正确的,如果确实不正确,CPU就会进入复位模式(译者注:这里的复位不是指处理器reset,而是CPU流水线的复位),即丢弃已经取得的指 令并且重新开始取指。这种复位操作有可能对性能产生很大影响。因此,对于分支指令的正确预测是另一个需要花费很多工程时的领域。

现在,不是所有分支指令都是一样的,有些可以很***的预测,但是另一些几乎不可能进行预测。前面例子中的循环中的分支指令&mdash;&mdash;就像反汇编代码中267行&mdash;&mdash;是最容易预测的其中一种,这个分支指令会连续向后跳转100,000,000次。

以下是一个非常难预测的分支指令实例:

void main(void) {     for(int i = 0; i < 1000000; i++) {          int n = random();          if(n >= 0) {             printf("positive!\n");         } else {             printf("negative!\n");         }     } }

如果random()是真正随机的(事实上在C语言中远非如此),那么对于if-else的预测还不如随便猜来的准确。幸运的是,大部分的分支指令没有这么顽皮,但是也有很少一部分和上面例子中的循环分支指令一样变态。

回到我们的例子上:C代码实现的斐波那契数列,只产生一个非常容易预测的分支指令。相反地,CPython代码就非常糟糕。首先,所有纯粹的翻译器有一个“分配”循环,就像下面的例子:

void exec_instruction(instruction_t *inst) {     switch(inst->opcode) {         case ADD:    // do add         case LOAD:   // do load         case STORE:  // do store         case GOTO:   // do goto     } }

编译器无论如何处理以上代码,至少有一个间接跳转指令是必须的,而这种间接跳转指令是比较难预测的。

到此,关于“C语言算法举例分析”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

C语言算法举例分析

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

C语言算法举例分析

这篇文章主要介绍“C语言算法举例分析”,在日常操作中,相信很多人在C语言算法举例分析问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C语言算法举例分析”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!最近,我一
2023-06-17

C++语言举例分析

这篇文章主要介绍“C++语言举例分析”,在日常操作中,相信很多人在C++语言举例分析问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C++语言举例分析”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!因为依赖开
2023-06-17

C语言简单题目举例分析

本篇内容主要讲解“C语言简单题目举例分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“C语言简单题目举例分析”吧!题目题目描述针对等额本金还款模式的客户,写一个程序按顺序输入贷款总额(单位为万元
2023-06-03

c语言排序算法案例分析

本文小编为大家详细介绍“c语言排序算法案例分析”,内容详细,步骤清晰,细节处理妥当,希望这篇“c语言排序算法案例分析”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。在归并算法中,合并两个数列需要消耗m+n的空间,排
2023-06-17

C语言排序算法实例分析

这篇文章主要讲解了“C语言排序算法实例分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C语言排序算法实例分析”吧!1、直接插入排序基本思想:当插入第i(i>=1)个元素时,前面的array
2023-06-29

Python语法举例分析

这篇文章主要介绍“Python语法举例分析”,在日常操作中,相信很多人在Python语法举例分析问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Python语法举例分析”的疑惑有所帮助!接下来,请跟着小编一起来
2023-06-02

C语言中数据的存储举例分析

这篇文章主要介绍“C语言中数据的存储举例分析”,在日常操作中,相信很多人在C语言中数据的存储举例分析问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C语言中数据的存储举例分析”的疑惑有所帮助!接下来,请跟着小编
2023-06-25

C语言指针运算实例分析

这篇文章主要介绍“C语言指针运算实例分析”,在日常操作中,相信很多人在C语言指针运算实例分析问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C语言指针运算实例分析”的疑惑有所帮助!接下来,请跟着小编一起来学习吧
2023-06-30

C语言中枚举和联合体的示例分析

这篇文章主要介绍了C语言中枚举和联合体的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。枚举什么是枚举?顾名思义,就是一一列举,把所有的情况,所有的取值,一一列举出来。
2023-06-25

C#枚举类型举例分析

本篇内容主要讲解“C#枚举类型举例分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“C#枚举类型举例分析”吧!C#枚举类型实例演示/* * Created by SharpDevelop.
2023-06-17

c语言中putchar的用法举例

putchar() 函数用于向标准输出设备写入单个字符。其用法步骤如下:包含 头文件。定义一个表示要写入字符的整数变量。使用 putchar() 函数向控制台打印字符。putchar() 函数在 C 语言中的用法putchar() 函数
c语言中putchar的用法举例
2024-05-02

C++代码举例分析

这篇文章主要介绍“C++代码举例分析”,在日常操作中,相信很多人在C++代码举例分析问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C++代码举例分析”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!所以 v
2023-06-17

c语言中fun函数用法举例

c语言中fun函数是计算双曲正切的函数,用法为:传入要计算的弧度角x。返回该角度的双曲正切值。例如,计算角度1.23弧度的双曲正切值,返回1.32。C语言中fun函数用法举例fun函数是一个C语言库函数,用于计算双曲正切函数。其原型为:
c语言中fun函数用法举例
2024-05-10

Python语言的面向对象举例分析

本篇内容介绍了“Python语言的面向对象举例分析”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!首先,我们需要定义一个新的HTMLParse
2023-06-17

C++软件举例分析

这篇文章主要讲解了“C++软件举例分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C++软件举例分析”吧!C++软件不同于C的一个关键地方就在于,C++在完全保留有C的高效的基础上,增添了
2023-06-17

C语言一维数组算法问题的示例分析

这篇文章给大家分享的是有关C语言一维数组算法问题的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。问题1:将数组中的数逆序存放本题要求编写程序,将给定的n个整数存入数组中,将数组中的这n个数逆序存放, 再按
2023-06-25

C语言的dp算法LeetCode炒股习题案例分析

本文小编为大家详细介绍“C语言的dp算法LeetCode炒股习题案例分析”,内容详细,步骤清晰,细节处理妥当,希望这篇“C语言的dp算法LeetCode炒股习题案例分析”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧
2023-06-29

C语言运算符的重载实例分析

本篇内容介绍了“C语言运算符的重载实例分析”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!写一个Add函数我们先讨论下面代码,并复习前面的内容
2023-06-26

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录