代码中的无形性能损耗
最近研究了代码底层的一些实现机制,发现很多“真相”,总结了一下,形成本文。下面有点瞎扯(扯完我也不知道要给大家强调什么)如有不同意见,欢迎大家拍砖。
1、编程语言本身带来的损耗
Java语言编写的程序(class文件),程序需要首先运行java虚拟机(先运行java.exe,建立java虚拟机,加载、验证、准备、解析和初始化class等一系列步骤),然后才能运行你的main方法。
同样.net平台运行的程序也一样,另外新兴的ruby,Scala等也是运行在Java虚拟机上面的,所以肯定也存在这样的问题。
这些语言还有另外一个问题,就是这些语言属于解释性语言。尽管有git技术的加速,但仍然逃脱不了其天生的劣势。运行速度和编译性语言相比,还是差不少的。尤其是像JS(Javascript)中这种语言,单线程执行,当一个网页包含的js过多时,会出现客户端无响应的问题。这种情况直到出现chrome浏览器和IE8之后,问题才得到缓解。
2、语言本身插入的"通用代码"
下面几行简单的c代码
int main(void){ return 0; } |
编译成可执行文件(linux64 OS)后,竟然多出12个函数代码(需要查看汇编指令)。也就是说为了运行你的一句return 0,系统需要在你的代码前和代码后面执行多达12次调用。
其实这种情况很普遍,我就拿我很熟悉的Java语言为例,为了支持gc,jvm需要在你的Java代码中种植很多安全点,以方便在gc时,各个线程在安全点停止下来(线程是主动停止下来等待gc)。这些安全点都是语言在你的代码中插入的无形损耗。为了让同步的变量可见,Java会在某些代码中间插入CyclicBarrier,让多个需要立即可见的变量互相等待一下,然后让cpu将变量刷回主存。
3、可执行程序加载
所有的程序都会有这个问题,不可避免。原因是所有程序都在OS的管理下运行的,除非你的程序就是操作系统。
在程序开始运行前,OS要做以下工作:
- 创建进程PCB(进程属于新建态),
- 将exe文件加载到内存
- 映射虚拟内存,分配页表,设置MMU
- 操作系统为程序运行分配必须的资源(如输入输出文件等).
通常,因此这种损耗基本上可以忽略。
4、模式切换
从OS调度来说一般有线程切换和进程切换,现在的OS一般是以线程为单位进行调度,无论是线程切换还是进程切换,都会引起用户模式和内核模式的切换。因此线程切换,虽然损耗比较小,但当线程的数量非常多的时候,其损耗也是不可忽略的。
线程切换需要进行的工作:
step 1. 保存被中断(一般上下文切换都是由中断引起的,比如说时间片用完)线程A的CPU相关寄存器。
step 2. 保存现程控制块(TCB)相关信息,标志现程当前状态(如果是时间片用完,则属于就绪态)
step 3. 根据当前线程的状态,将其放入相关队列(假如是时间片用完,则放入就绪队列)
step 4. 从就绪队列选择另外一个线程B(可能会有一个算法在这里面,因为队列有很多就绪线程,怎么选)
step 5. 修改线程B的线程控制块(TCB),标记其为运行态。
step 6. 恢复线程B的CPU现场
我们的代码中,如果需要大量的io时,这时你要注意了,通常每次调用io函数,都会发生内核模式和用户模式切换。这种切换速度是非常快的,但一般的处理器也有硬件支持。
用户模式切内核模式进行的工作
step1 根据线程(进程)状态,将线程放到挂起队列末尾。
step2 保存用户的CPU的寄存器现场
step2 按照io号去处理。。。。(这个可能会很长)
在linux中,可以通过vmstat 的cs(context switch)列看到上下文切换的次数
5、函数调用
其实我们的程序入口都是从一个main函数开始,然后就是一层又一层的函数调用,这个是不可避免的。这方面的损耗也比较小。其主要损耗在于调用参数及现场保护。
当调用一个参数时,需要进行以下一些列工作
- 参数入栈(将函数调用的参数压入函数栈,参数越多,耗时越长)
- 跳转地址(跳转前还需要保护当前代码地址)
- 函数调用
- 函数调用完毕,逆过程(回跳至调用者代码,释放之前入栈参数空间)
在64位系统,AMD对函数堆栈稍微做了些改变,当函数参数正数不超过6个,浮点数参数不超过8个时,可以将参数直接存放在寄存器中,不用再将参数进行入栈。但这个优化似乎不起太大作用,因为一旦函数调用超过2层,同样还需要将参数入栈。
从上面的过程,函数参数有个入栈工程,通常函数的内的局部变量也在函数站内。一般这个栈的大小是有限制的(Java中默认是512k)。如果你的函数调用太深或者你的函数出现了死循环,cpu就会不断的往函数栈中push参数,直到这个栈满了,就出现了stack overflow的异常。
6、os与用户程序之间的数据传递损耗
比如用户需要读取一个配置文件,首先,文件数据到达操作系统的缓冲区,然后,再传递给用户的目的缓存区(至于为什么这么做,大家可以看看Java nio编程),甚至有时候,我们代码传递到用户缓存区,还没有到达正确的位置,可能还要转换成某个对象,存放到共享数据区。
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->
总结:记得有人说过这样的话,通用即难用。现在想想是有一定道理的,上面的每一种损耗,都是为了让我们的语言更优雅,或者为了降低我们编程的难度而采取的措施。我老觉得苹果手机比android手机快,可能很大一部分原因是android采用的Java引起。尽管上面所述有些损耗都非常小,以至于我们根本无法察觉。但只要执行指令就会耗电,而且冗余指令越多,耗电量越大。
参考资料
http://www.cnblogs.com/ktgu/p/3529144.html
http://baike.baidu.com/view/1760224.htm
附:c代码反编译后代码
0000000000400358 <_init>: 400358: 48 83 ec 08 sub $0x8,%rsp 40035c: e8 5b 00 00 00 callq 4003bc <call_gmon_start> 400361: e8 ea 00 00 00 callq 400450 <frame_dummy> 400366: e8 b5 01 00 00 callq 400520 <__do_global_ctors_aux> 40036b: 48 83 c4 08 add $0x8,%rsp 40036f: c3 retq Disassembly of section .plt: 0000000000400370 <__libc_start_main@plt-0x10>: 400370: ff 35 72 04 20 00 pushq 0x200472(%rip) # 6007e8 <_GLOBAL_OFFSET_TABLE_+0x8> 400376: ff 25 74 04 20 00 jmpq *0x200474(%rip) # 6007f0 <_GLOBAL_OFFSET_TABLE_+0x10> 40037c: 0f 1f 40 00 nopl 0x0(%rax) 0000000000400380 <__libc_start_main@plt>: 400380: ff 25 72 04 20 00 jmpq *0x200472(%rip) # 6007f8 <_GLOBAL_OFFSET_TABLE_+0x18> 400386: 68 00 00 00 00 pushq $0x0 40038b: e9 e0 ff ff ff jmpq 400370 <_init+0x18> Disassembly of section .text: 0000000000400390 <_start>: 400390: 31 ed xor %ebp,%ebp 400392: 49 89 d1 mov %rdx,%r9 400395: 5e pop %rsi 400396: 48 89 e2 mov %rsp,%rdx 400399: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp 40039d: 50 push %rax 40039e: 54 push %rsp 40039f: 49 c7 c0 80 04 40 00 mov $0x400480,%r8 4003a6: 48 c7 c1 90 04 40 00 mov $0x400490,%rcx 4003ad: 48 c7 c7 74 04 40 00 mov $0x400474,%rdi 4003b4: e8 c7 ff ff ff callq 400380 <__libc_start_main@plt> 4003b9: f4 hlt 4003ba: 90 nop 4003bb: 90 nop 00000000004003bc <call_gmon_start>: 4003bc: 48 83 ec 08 sub $0x8,%rsp 4003c0: 48 8b 05 11 04 20 00 mov 0x200411(%rip),%rax # 6007d8 <_DYNAMIC+0x190> 4003c7: 48 85 c0 test %rax,%rax 4003ca: 74 02 je 4003ce <call_gmon_start+0x12> 4003cc: ff d0 callq *%rax 4003ce: 48 83 c4 08 add $0x8,%rsp 4003d2: c3 retq 4003d3: 90 nop 4003d4: 90 nop 4003d5: 90 nop 4003d6: 90 nop 4003d7: 90 nop 4003d8: 90 nop 4003d9: 90 nop 4003da: 90 nop 4003db: 90 nop 4003dc: 90 nop 4003dd: 90 nop 4003de: 90 nop 4003df: 90 nop 00000000004003e0 <__do_global_dtors_aux>: 4003e0: 55 push %rbp 4003e1: 48 89 e5 mov %rsp,%rbp 4003e4: 53 push %rbx 4003e5: 48 83 ec 08 sub $0x8,%rsp 4003e9: 80 3d 18 04 20 00 00 cmpb $0x0,0x200418(%rip) # 600808 <completed.6349> 4003f0: 75 4b jne 40043d <__do_global_dtors_aux+0x5d> 4003f2: bb 38 06 60 00 mov $0x600638,%ebx 4003f7: 48 8b 05 12 04 20 00 mov 0x200412(%rip),%rax # 600810 <dtor_idx.6351> 4003fe: 48 81 eb 30 06 60 00 sub $0x600630,%rbx 400405: 48 c1 fb 03 sar $0x3,%rbx 400409: 48 83 eb 01 sub $0x1,%rbx 40040d: 48 39 d8 cmp %rbx,%rax 400410: 73 24 jae 400436 <__do_global_dtors_aux+0x56> 400412: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1) 400418: 48 83 c0 01 add $0x1,%rax 40041c: 48 89 05 ed 03 20 00 mov %rax,0x2003ed(%rip) # 600810 <dtor_idx.6351> 400423: ff 14 c5 30 06 60 00 callq *0x600630(,%rax,8) 40042a: 48 8b 05 df 03 20 00 mov 0x2003df(%rip),%rax # 600810 <dtor_idx.6351> 400431: 48 39 d8 cmp %rbx,%rax 400434: 72 e2 jb 400418 <__do_global_dtors_aux+0x38> 400436: c6 05 cb 03 20 00 01 movb $0x1,0x2003cb(%rip) # 600808 <completed.6349> 40043d: 48 83 c4 08 add $0x8,%rsp 400441: 5b pop %rbx 400442: c9 leaveq 400443: c3 retq 400444: 66 66 66 2e 0f 1f 84 data32 data32 nopw %cs:0x0(%rax,%rax,1) 40044b: 00 00 00 00 00 0000000000400450 <frame_dummy>: 400450: 48 83 3d e8 01 20 00 cmpq $0x0,0x2001e8(%rip) # 600640 <__JCR_END__> 400457: 00 400458: 55 push %rbp 400459: 48 89 e5 mov %rsp,%rbp 40045c: 74 12 je 400470 <frame_dummy+0x20> 40045e: b8 00 00 00 00 mov $0x0,%eax 400463: 48 85 c0 test %rax,%rax 400466: 74 08 je 400470 <frame_dummy+0x20> 400468: bf 40 06 60 00 mov $0x600640,%edi 40046d: c9 leaveq 40046e: ff e0 jmpq *%rax 400470: c9 leaveq 400471: c3 retq 400472: 90 nop 400473: 90 nop 0000000000400474 <main>: 400474: 55 push %rbp 400475: 48 89 e5 mov %rsp,%rbp 400478: b8 00 00 00 00 mov $0x0,%eax 40047d: c9 leaveq 40047e: c3 retq 40047f: 90 nop 0000000000400480 <__libc_csu_fini>: 400480: f3 c3 repz retq 400482: 66 66 66 66 66 2e 0f data32 data32 data32 data32 nopw %cs:0x0(%rax,%rax,1) 400489: 1f 84 00 00 00 00 00 0000000000400490 <__libc_csu_init>: 400490: 48 89 6c 24 d8 mov %rbp,-0x28(%rsp) 400495: 4c 89 64 24 e0 mov %r12,-0x20(%rsp) 40049a: 48 8d 2d 7b 01 20 00 lea 0x20017b(%rip),%rbp # 60061c <__init_array_end> 4004a1: 4c 8d 25 74 01 20 00 lea 0x200174(%rip),%r12 # 60061c <__init_array_end> 4004a8: 4c 89 6c 24 e8 mov %r13,-0x18(%rsp) 4004ad: 4c 89 74 24 f0 mov %r14,-0x10(%rsp) 4004b2: 4c 89 7c 24 f8 mov %r15,-0x8(%rsp) 4004b7: 48 89 5c 24 d0 mov %rbx,-0x30(%rsp) 4004bc: 48 83 ec 38 sub $0x38,%rsp 4004c0: 4c 29 e5 sub %r12,%rbp 4004c3: 41 89 fd mov %edi,%r13d 4004c6: 49 89 f6 mov %rsi,%r14 4004c9: 48 c1 fd 03 sar $0x3,%rbp 4004cd: 49 89 d7 mov %rdx,%r15 4004d0: e8 83 fe ff ff callq 400358 <_init> 4004d5: 48 85 ed test %rbp,%rbp 4004d8: 74 1c je 4004f6 <__libc_csu_init+0x66> 4004da: 31 db xor %ebx,%ebx 4004dc: 0f 1f 40 00 nopl 0x0(%rax) 4004e0: 4c 89 fa mov %r15,%rdx 4004e3: 4c 89 f6 mov %r14,%rsi 4004e6: 44 89 ef mov %r13d,%edi 4004e9: 41 ff 14 dc callq *(%r12,%rbx,8) 4004ed: 48 83 c3 01 add $0x1,%rbx 4004f1: 48 39 eb cmp %rbp,%rbx 4004f4: 72 ea jb 4004e0 <__libc_csu_init+0x50> 4004f6: 48 8b 5c 24 08 mov 0x8(%rsp),%rbx 4004fb: 48 8b 6c 24 10 mov 0x10(%rsp),%rbp 400500: 4c 8b 64 24 18 mov 0x18(%rsp),%r12 400505: 4c 8b 6c 24 20 mov 0x20(%rsp),%r13 40050a: 4c 8b 74 24 28 mov 0x28(%rsp),%r14 40050f: 4c 8b 7c 24 30 mov 0x30(%rsp),%r15 400514: 48 83 c4 38 add $0x38,%rsp 400518: c3 retq 400519: 90 nop 40051a: 90 nop 40051b: 90 nop 40051c: 90 nop 40051d: 90 nop 40051e: 90 nop 40051f: 90 nop 0000000000400520 <__do_global_ctors_aux>: 400520: 55 push %rbp 400521: 48 89 e5 mov %rsp,%rbp 400524: 53 push %rbx 400525: 48 83 ec 08 sub $0x8,%rsp 400529: 48 8b 05 f0 00 20 00 mov 0x2000f0(%rip),%rax # 600620 <__CTOR_LIST__> 400530: 48 83 f8 ff cmp $0xffffffffffffffff,%rax 400534: 74 19 je 40054f <__do_global_ctors_aux+0x2f> 400536: bb 20 06 60 00 mov $0x600620,%ebx 40053b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1) 400540: 48 83 eb 08 sub $0x8,%rbx 400544: ff d0 callq *%rax 400546: 48 8b 03 mov (%rbx),%rax 400549: 48 83 f8 ff cmp $0xffffffffffffffff,%rax 40054d: 75 f1 jne 400540 <__do_global_ctors_aux+0x20> 40054f: 48 83 c4 08 add $0x8,%rsp 400553: 5b pop %rbx 400554: c9 leaveq 400555: c3 retq 400556: 90 nop 400557: 90 nop Disassembly of section .fini: 0000000000400558 <_fini>: 400558: 48 83 ec 08 sub $0x8,%rsp 40055c: e8 7f fe ff ff callq 4003e0 <__do_global_dtors_aux> 400561: 48 83 c4 08 add $0x8,%rsp 400565: c3 retq
上一篇: dubbo中的Filter顺序
下一篇: 跟散仙学shell命令(二)