概述:最近经常听别人的面试题目是读取一个大文件,然后出来读取到的字符串,比如某串的个数了,某串的出现频率之类的,和朋友聊天过程中决定用不同的语言比个赛,分别是c,python,shell。我原本以为c语言一定会获胜(毕竟以效率著称),但结果却是python完胜而且是数量级上的完胜,正当我感叹python大法好时。。。

题目:
现给一个文件400M(该文件是由/etc/passwd生成的),统计其中root字符串出现的次数。

一.c语言实现版
#include <stdio.h>
#include <time.h>
int main(){

    int cnt=0;
    int start,end;
    FILE *fp;
    char a[4]="oot";
    char b[4];

    start = time((time_t*)NULL);
    fp = fopen("/root/bigtest", "r");
    while(!feof(fp)){
        while(!feof(fp)&&fgetc(fp)!='r');
//      if (feof(fp)){
//          break;
//      }
        if(fgetc(fp)=='o'){
            if(fgetc(fp)=='o'){
                if(fgetc(fp)=='t'){
                    ++cnt;
                }
            }
        }else{
            continue;
        }
        //printf("%s----------\n",b);
    }
    end = time((time_t*)NULL);
    printf("%.2fs %ld\n",((end-start)*1.0),cnt);

fclose(fp);
        return 0;
}

c语言版就是直接从文件中一个字符一个读取,然后判断,最终输出时间。我们看下用时。

[root@foundation3 Desktop]# gcc 1.c -o big
[root@foundation3 Desktop]# ./big 
6.00s 745540

用了6s时间,计算出有745540个root

二.shell版本

使用shell的好处就是简洁,但是速度就不敢恭维了

#!/bin/bash
starttime=`date +'%s'`
cat /root/bigtest | grep -o root |wc -l
endtime=`date +'%s'`

echo $((endtime-starttime))
[root@foundation3 Desktop]# bash big_shell.sh 
745540
12

用12s,计算出745540个root

三.python版本

通过python的上下文管理器打开文件,在使用内建的count函数计算每行的root个数,代码简洁程度一点都不比shell差。

# /usr/bin/env python 
import time
sum=0
start=time.time()
with open('/root/bigtest') as f:
    for i in f:
        new=i.count('root')
        sum+=new
print sum,time.time()-start
[root@foundation3 Desktop]# python /root/bigpy.py 
745540 0.781894922256

这个时间让我震惊简直,比c快了将近10倍好不好。
为了探究他读文件的机制我试了很多方法,一开始以为他是运用了pytho的多线程,就写了如下的代码

import threading,time

start=time.time()
sum=0

def count(text):
    global sum
    sum+=text.count('root')
    #   print 'thread end'

f=open('/root/bigtest')
while True:
    text=f.read(1000000)
    t=threading.Thread(target=count,args=(text,))
#   print 'thread begin'
    t.start()
    if text == '':
        break 
t.join()  
print 'thread is ended \ntime is %s num is %s' %(time.time()-start,sum)
[root@foundation3 Desktop]# python multibit.py 
thread is ended 
time is 0.72488617897 num is 745538

多线程版本好像并没有多少提升,但是计算的好像有点问题,应该是切片的时候正好切到了root,导致计算出错。比正常计算的少了两个。

四.python的复制的原理

其实这个发现是一个巧合,大家有没有发现,我是先运行了其他版本的读取程序,最后才运行的python版本的程序。问题就出在这里,刚才我重启了次电脑,优先运行了一下python的程序,我发现速度变慢了。如下:

[root@foundation3 Desktop]# python /root/bigpy.py 
745540 6.27875995636

用了6s多,明显和以前的不一样啊,难道程序被改了,我又运行一遍,发现又是零点几秒了,我恍然大悟,一定是cache的原因。
现在我们清一下cahce

[root@foundation3 Desktop]# echo 3 > /proc/sys/vm/drop_caches
[root@foundation3 Desktop]# echo 3 > /proc/sys/vm/drop_caches 
[root@foundation3 Desktop]# free -m
              total        used        free      shared  buff/cache   available
Mem:           3590        1117        2149         163         323        2131
Swap:          3839           0        3839

现在的cache是323。
然后执行一次python的脚本,先是发现了读取时间又变成了7s多,可以确定是cache的缘故。现在再看一下cache。

[root@foundation3 Desktop]# free -m
              total        used        free      shared  buff/cache   available
Mem:           3590        1117        1741         163         731        2101
Swap:          3839           0        3839
[root@foundation3 Desktop]#

cache明显增加了。证明了我们的猜想,python是会读取cache中的数据,所以会比其他的快很多。