golang 1.5 比 C语言执行速度快的问题学习查看

之前的测试http://www.simonzhang.net/?p=2789 golang1.5.1比C语言还要快一点。
分析一下golang比C语言快的原因。

先使用strace查看系统的处理
strace -f -F -i -T -v -o strace-testc.txt ./testc
strace -f -F -i -T -v -o strace-testgo.txt ./testgo

C部分:
6008 [ 7f06ae20d297] access(“/etc/ld.so.nohwcap”, F_OK) = -1 ENOENT (No such file or directory) <0.000111>
手动增加这两个文件
touch /etc/ld.so.nohwcap
touch /etc/ld.so.preload
因为有了文件,所以还要读一下。虽然是空的,还要关闭,所以速度反而慢了,应该不到1ms,但是还是把文件删了。

C语言应该是一次加载计算后返回值。
6008 [ 7f06ae1f9306] arch_prctl(ARCH_SET_FS, 0x7f06ae3fd700) = 0 <0.000015>
6008 [ 7f06ae20d477] mprotect(0x7f06ae1ed000, 16384, PROT_READ) = 0 <0.000023>
6008 [ 7f06ae20d477] mprotect(0x7f06ae416000, 4096, PROT_READ) = 0 <0.000019>
6008 [ 7f06ae20d447] munmap(0x7f06ae3ff000, 82308) = 0 <0.000031>
6008 [ 7f06adf3b314] fstat(1, {st_dev=makedev(0, 10), st_ino=5, st_mode=S_IFCHR|0620, st_nlink=1, st_uid=1000, st_gid=5, st_blksize=1024, st_blocks=0, st_rdev=makedev(136, 2), st_atime=2015/11/20-15:22:00, st_mtime=2015/11/20-15:22:00, st_ctime=2015/11/20-10:07:32}) = 0 <0.000017>
6008 [ 7f06adf4436a] mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f06ae413000 <0.000017>
6008 [ 7f06adf3b9d0] write(1, “1 1\n”, 4) = 4 <0.000051>
6008 [ 7f06adf3b9d0] write(1, “2 4\n”, 4) = 4 <0.000036>
6008 [ 7f06adf3b9d0] write(1, “3 9\n”, 4) = 4 <0.000033>
6008 [ 7f06adf3b9d0] write(1, “11 121\n”, 7) = 7 <0.000051>
6008 [ 7f06adf3b9d0] write(1, “22 484\n”, 7) = 7 <0.000032>
6008 [ 7f06adf3b9d0] write(1, “101 10201\n”, 10) = 10 <0.000032>
6008 [ 7f06adf3b9d0] write(1, “111 12321\n”, 10) = 10 <0.000032>
6008 [ 7f06adf3b9d0] write(1, “121 14641\n”, 10) = 10 <0.000032>
.
.
.
6008 [ 7f06adf3b9d0] write(1, “2000002 4000008000004\n”, 22) = 22 <0.000024>
6008 [ 7f06adf3b9d0] write(1, “2001002 4004009004004\n”, 22) = 22 <0.000008>
6008 [ 7f06adf17818] exit_group(0) = ?
结束。

golang部分:
设置运行环境的体系结构,为64位模式。
6025 [ 4569fb] arch_prctl(ARCH_SET_FS, 0x5b80e8) = 0 <0.000104>
6025 [ 456a46] sched_getaffinity(0, 8192, {ff, 0, 0, 0, 0, 0, 0, 0}) = 64 <0.000105>
6025 [ 456894] mmap(0xc000000000, 65536, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xc000000000 <0.000107>
6025 [ 4568c3] munmap(0xc000000000, 65536) = 0 <0.000061>
6025 [ 4569c3] sigaltstack({ss_sp=0xc820002000, ss_flags=0, ss_size=32672}, NULL) = 0 <0.000020>
6025 [ 456677] gettid() = 6025 <0.000020>
6025 [ 4567ea] rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0 <0.000045>

rt_sigaction是排队的基带信号
6025 [ 45681b] rt_sigaction(SIGSYS, NULL, {SIG_DFL, [], 0}, 8) = 0 <0.000017>
6025 [ 45681b] rt_sigaction(SIGSYS, {0x456840, ~[], SA_RESTORER|SA_STACK|SA_RESTART|SA_SIGINFO, 0x456860}, NULL, 8) = 0 <0.000017>

6025 [ 45681b] rt_sigaction(SIGRT_32, NULL, {SIG_DFL, [], 0}, 8) = 0 <0.000017>
6025 [ 45681b] rt_sigaction(SIGRT_32, {0x456840, ~[], SA_RESTORER|SA_STACK|SA_RESTART|SA_SIGINFO, 0x456860}, NULL, 8) = 0 <0.000018>

使用了select的多进程模型
6026 [ 45665d] <... select resumed> ) = 0 (Timeout) <0.000103>
6025 [ 49d154] write(1, “3 9\n”, 4
6026 [ 45665d] select(0, NULL, NULL, NULL, {0, 20}
6025 [ 49d154] <... write resumed> ) = 4 <0.000026>
6025 [ 49d154] write(1, “11 121\n”, 7) = 7 <0.000024>
6026 [ 45665d] select(0, NULL, NULL, NULL, {0, 20}) = 0 (Timeout) <0.000093>
6026 [ 45665d] select(0, NULL, NULL, NULL, {0, 20}) = 0 (Timeout) <0.000092>
6026 [ 45665d] select(0, NULL, NULL, NULL, {0, 20}) = 0 (Timeout) <0.000095>
6026 [ 45665d] select(0, NULL, NULL, NULL, {0, 20}) = 0 (Timeout) <0.000092>
6026 [ 45665d] select(0, NULL, NULL, NULL, {0, 20}) = 0 (Timeout) <0.000093>
6026 [ 45665d] select(0, NULL, NULL, NULL, {0, 20}) = 0 (Timeout) <0.000094>
6026 [ 45665d] select(0, NULL, NULL, NULL, {0, 20}) = 0 (Timeout) <0.000093>

个人理解,golang程序编译后,自身已经包含了内存管理(见参考文档),在多进程循环的情况下,下一个进程进行结算时,上一个内存取消或进程销毁就并行处理了。所以缩减了时间。如果这个理解没有错。golang使用的内存应该比C大很多倍。做个简单实验。golang的内存垃圾回收机制2分钟一次。现在让程序运行完sleep 10秒钟,看一下内存使用(不考虑已释放因素)。
结果golang语言只输出“hi”的情况下也要5M,以上程序运行完使用14M。C语言共使用4M多一点。

通过反汇编部分
golang语言
go tool objdump testgo
C语言
gcc -S -O3 testc.c

也能看出,golang的优化后还是非常复杂。golang的开发人员做了大量工作。从开发来看golang确实做到了开发简单、运行高效。但是用于比较低端的设备,如单片机、驱动等还是C语言优势大,占用资源少比较高效。

附件1
函数简要说明
fstat
用来将参数filedes 所指向的文件状态复制到参数buf。
mmap
将一个文件或者其它对象映射进内存或取消。
mprotect
高级的内存管理。内存区域设置保护属性。
arch_prctl
设置架构相关的线程状态(应该是根据cpu和位数)
sched_getaffinity
根据cpu设置进程数。

附件2.
Strace 命令参数
-c 统计每一系统调用的所执行的时间,次数和出错的次数等.
-d 输出strace关于标准错误的调试信息.
-f 跟踪由fork调用所产生的子进程.
-ff 如果提供-o filename,则所有进程的跟踪结果输出到相应的filename.pid中,pid是各进程的进程号.
-F 尝试跟踪vfork调用.在-f时,vfork不被跟踪.
-h 输出简要的帮助信息.
-i 输出系统调用的入口指针.
-q 禁止输出关于脱离的消息.
-r 打印出相对时间关于,,每一个系统调用.
-t 在输出中的每一行前加上时间信息.
-tt 在输出中的每一行前加上时间信息,微秒级.
-ttt 微秒级输出,以秒了表示时间.
-T 显示每一调用所耗的时间.
-v 输出所有的系统调用.一些调用关于环境变量,状态,输入输出等调用由于使用频繁,默认不输出.
-V 输出strace的版本信息.
-x 以十六进制形式输出非标准字符串
-xx 所有字符串以十六进制形式输出.
-a column
设置返回值的输出位置.默认为40.
-e expr
指定一个表达式,用来控制如何跟踪.格式如下:
[qualifier=][!]value1[,value2]
qualifier只能是 trace,abbrev,verbose,raw,signal,read,write其中之一.value是用来限定的符号或数字.默认的qualifier是 trace.感叹号是否定符号.例如:
-eopen等价于 -e trace=open,表示只跟踪open调用.而-etrace!=open表示跟踪除了open以外的其他调用.有两个特殊的符号 all 和 none.
注意有些shell使用!来执行历史记录里的命令,所以要使用\\\\.
-e trace=set
只跟踪指定的系统调用.例如:-e trace=open,close,rean,write表示只跟踪这四个系统调用.默认的为set=all.
-e trace=file
只跟踪有关文件操作的系统调用.
-e trace=process
只跟踪有关进程控制的系统调用.
-e trace=network
跟踪与网络有关的所有系统调用.
-e strace=signal
跟踪所有与系统信号有关的系统调用
-e trace=ipc
跟踪所有与进程通讯有关的系统调用
-e abbrev=set
设定strace输出的系统调用的结果集.-v 等与 abbrev=none.默认为abbrev=all.
-e raw=set
将指定的系统调用的参数以十六进制显示.
-e signal=set
指定跟踪的系统信号.默认为all.如signal=!SIGIO(或者signal=!io),表示不跟踪SIGIO信号.
-e read=set
输出从指定文件中读出的数据.例如:
-e read=3,5
-e write=set
输出写入到指定文件中的数据.
-o filename
将strace的输出写入文件filename
-p pid
跟踪指定的进程pid.
-s strsize
指定输出的字符串的最大长度.默认为32.文件名一直全部输出.
-u username
以username的UID和GID执行被跟踪的命令.

附件3
time命令返回值的解释
Real, User and Sys process time statistics
One of these things is not like the other. Real refers to actual elapsed time; User and Sys refer to CPU time used only by the process.
*Real is wall clock time – time from start to finish of the call. This is all elapsed time including time slices used by other processes and time the process spends blocked (for example if it is waiting for I/O to complete).
*User is the amount of CPU time spent in user-mode code (outside the kernel) within the process. This is only actual CPU time used in executing the process. Other processes and time the process spends blocked do not count towards this figure.
*Sys is the amount of CPU time spent in the kernel within the process. This means executing CPU time spent in system calls within the kernel, as opposed to library code, which is still running in user-space. Like ‘user’, this is only CPU time used by the process. See below for a brief description of kernel mode (also known as ‘supervisor’ mode) and the system call mechanism.

参考文档:
https://deferpanic.com/blog/understanding-golang-memory-usage/
http://stackoverflow.com/questions/556405/what-do-real-user-and-sys-mean-in-the-output-of-time1

发表评论

电子邮件地址不会被公开。 必填项已用*标注