前天,20161012,到望京面试。第四个职位,终于进了二面。好么,结果人力安排完了面试时间竟然没有通知我,也没有收到短信邀请。如果没有短信邀请门口的保安大哥是不让我进去大厦的。然后,我在11号接到了面试官直接打来的电话,问我为啥还没到,我说没人通知我我不知道呀。结果我就直接被他邀请去以访客的身份参加面试了。不知道人力的姑娘是不是认识我,且和我有仇,终于可以报复了。。。

然后,我终于如约到了,面试官带着我去前台登记。前台的妹子更萌。。。认为我是面试官,面试官是才是来面试的。我气质真的那么合吗?

当与前台的姑娘交谈,你就会直面的感受到他们刻意用心经营的企业文化。仅称呼就让我起了一身的鸡皮疙瘩,好想转身逃掉。可是,永远不要与钱为敌。

言归正传,吃面正式开始。两个面试官,一个不修边幅放眼望去就是功力深厚的技术人员,手长的纤细柔软而冰冷,握起来有气无力。但是总有一种随时可以变身成浩克的光环莫名萦绕。另一位年纪轻轻,估计定是某顶尖高校的顶尖生,专业知识想必烂熟于心。坐下的时候,两人默默,专心的浏览简历。于是我先打破沉默,主动做了自我介绍,还没到一半的时候被高才生打断,告诉我光说不清晰画一画。我便自己起身找了找那面墙是用来写字的,又到处找了找白板笔放在哪里,然后开画。边画边讲,还没到一半,我看了一眼,发现高才生在电脑上忙叨,也不知忙叨什么。浩克大哥低着头暗自神伤。两个人都完全没有在听,看来早已失去了兴趣。可能是我为了提高成功率表现的太能说会道了,反而不像个技术人员,倒像个产品销售之类的,这一点确实会让一个纯粹的技术人员产生不信任感和不安全感。

然后我终于把一段在别人家讲起来生动有趣,大部分人都会与我彼此讨论差不多一个小时的架构设计。对着两个心不在焉的人草草简短结束。现在回想起来我还没有弄明白,到底是那一个点让他们如此之快的失去了兴趣。衣服还没脱就已经萎了。。。然后,他们决定开始第二部分。编程测试。给了我一个macbook,开了个console。

好,相声讲完了,下面言归正传。

题如下:

一个60万行的文本文件,每行有逗号分隔的12列。第一列为字符串(为任意字符),后十一列为数组。

写一个python程序,将后11列的数字按列相加,将11个加和的数字按顺序打印,如,第一行的第二列加第二行的第二列加第三行的第二列。。。。

我复制了一个统一样本输出的程序 generator.py ,用以测试如下:

#! /usr/bin/python2

import random
import time

def main():
        maxv = 1<<16 - 1
        f = open('base.txt', 'w')
        random.seed(1476599333)
        for i in range(0, 600000):
                length = random.randint(10, 50)
                a = ""
                for i in range(0, length):
                        v = random.randint(32, 126)
                        a += chr(v)
                for i in range(0, 11):
                        v = random.randint(0, maxv)
                        a += ','
                        a += str(v)
                a += '\n'
                f.write(a)
        f.close()

if '__main__' == __name__:
        main()

使用上述程序,可以得到一个原始输入文件,用于测试。

我的第一份答案是这样的:

#! /usr/bin/python2

def main():
        f = open('base.txt', 'r')
        result = ['HOST', 0,0,0,0,0,0,0,0,0,0,0]
        for line in f.readlines():
                l = line.strip().split(',')
                length = len(l)
                if length < 12 :
                        raise Exception, "HUGE_TONG: Wrong format"
                skip = length - 12
                for i in range(1,12):
                        result[i] += int(l[i+skip])
        f.close()
        print result

if __name__ == '__main__':
        r = main()
        exit(r)

运行结果:

[tong@T7 chimian]$ time ./run.py
['HOST', 9829498952, 9833094366, 9827153757, 9835131709, 9843502266, 9836377986, 9825044768, 9833232152, 9841073437, 9833009489, 9833147503]

real    0m3.787s
user    0m3.747s
sys     0m0.037s

共花了 3.7 秒,顶尖生说这实在太慢了,要优化! 优化个毛线啊,我巴拉巴拉随便说了点思路,他说不对,我就投降了,因为性能不满意就用C好了!从来没想过python还要玩这么高深的优化,其实对于这种狗屁面试题我很生气。他说耗性能的是int()函数,可以用generator什么什么之类的。回来之后和张老师以及胡老师发了发牢骚,他们还挺兴趣的这个题。索性我也就再重新好好研究一下。

首先,优化的第一步是找到性能瓶颈。如高才生所言,我把int()函数替换成一般的加法,输出结果如下:

[tong@T7 chimian]$ time ./run-md.py
[, , , , , , , , , , ]

real    0m1.208s
user    0m1.180s
sys     0m0.023s
[tong@T7 chimian]$ diff run.py run-md.py
13c13,
<                       result[i] += int(l[i+skip])
---
>               #       result[i] += int(l[i+skip])
>                       result[i] +=
[tong@T7 chimian]$ 

在未知性能瓶颈的情况下,我们首先需要找到程序中最耗时的部分所在,一般在C语言中,我一般使用工具grof。那么python怎么办?

读了官方文档,关于调试与性能优化的章节 但是输出只能查看到用户逻辑部分,即使使用了高级功能,并不能看见python内部调用的统计,也不能查看逐行的统计,当然或许时因为我没看太懂所以不会。

>>> c=cProfile.Profile()
>>> c.enable()
>>> run.main()
[, , , , , , , , , , ]
>>> c.disable()
>>> ps = pstats.Stats(c)
>>> ps.sort_stats('cumulative').print_stats()
          function calls in 4.283 seconds

   Ordered by: cumulative time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
            (<module>)
            (main)
       0.318    0.000    0.318    0.000 {method 'split' of 'str' objects}
       0.140    0.000    0.140    0.000 {range}
       0.069    0.000    0.069    0.000 {method 'strip' of 'str' objects}
            0.053    0.053    0.053    0.053 {method 'readlines' of 'file' objects}
       0.033    0.000    0.033    0.000 {len}
            /encodings/utf_8.py:(decode)
            0.000    0.000    0.000    0.000 {_codecs.utf_8_decode}
            0.000    0.000    0.000    0.000 {open}
            0.000    0.000    0.000    0.000 {method 'close' of 'file' objects}
            0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

<pstats.Stats instance at 0x7f6ef2c8e6c8>
>>> 

可以看到,根据cprofile模块的统计,耗时最长的是split()函数。

求助google,发现了 line_profiler 可以做行统计

[tong@T7 ~]$ sudo pip2 install line_profiler

用法:

1. 在需要分析的函数前增加修饰行 @profile

@profile
def main():
        f = open('base.txt', 'r')

2. 使用如下命令

[tong@T7 chimian]$ kernprof -v -l run.py
[, , , , , , , , , , ]
Wrote profile results to run.py.lprof
Timer unit: 1e- s

Total time: 10.9936 s
File: run.py
Function: main at line 

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
                                                @profile
                                                def main():
                                8.0      0.0         f = open('base.txt', 'r')
                                ,,,,,,,,,,]
                      0.5      2.9         for line in f.readlines():
                      1.2      6.6                 l = line.strip().split(',')
                      0.5      2.5                 length = len(l)
                      :
                                                                      raise Exception, "HUGE_TONG: Wrong format"

                   ,):
                   0.9     54.2                         result[i] += int(l[i+skip])
                             14.0      0.0         f.close()
                             37.0      0.0         print result

可以看出,第14行占用了54%的运行时间,由于该行符合了多条运算,我门,试着将其展开在进行查看。

[tong@T7 chimian]$ kernprof -v -l run.py
[, , , , , , , , , , ]
Wrote profile results to run.py.lprof
Timer unit: 1e- s

Total time: 20.4831 s
File: run.py
Function: main at line 

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
                                                @profile
                                                def main():
                                7.0      0.0         f = open('base.txt', 'r')
                                ,,,,,,,,,,]
                      0.6      1.7         for line in f.readlines():
                      1.3      3.8                 l = line.strip().split(',')
                      0.5      1.5                 length = len(l)
                      :
                                                                      raise Exception, "HUGE_TONG: Wrong format"

                   ,):
                                                              #       result[i] += int(l[i+skip])
                   0.5     14.9                         index = i + skip
                   0.5     14.7                         value_str = l[index]
                   0.8     27.3                         value_int = int(value_str)
                   0.5     16.8                         result[i] += value_int
                             13.0      0.0         f.close()
                             37.0      0.0         print result

实现了一个C语言版的用于对比:

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

int main()
{
        FILE* fp = fopen("./base.txt", "r");
        );
        size_t len = ;
        ;
        ] = {,,,,,,,,,,};

        ) {
                int r = getline(&buf, &len, fp);
                ) {
                        )
                                perror("HUGE_TONG read(): ");
                        break;
                }
                slen = strlen(buf);
                ] != '\n') {
                        printf(]);
                }
                char* str;
                buf[slen-] = ;
                ;
                ; i >= ; i--) {
                        if (buf[i] == ',') {
                                str = buf + (i+);
                                buf[i] = ;
                                int value = atoi(str);
                                array[index--] += value;
                                ) {
                                        break;
                                }
                        }
                }
        }
        free(buf);
        fclose(fp);
        ; i < ; i++) {
                printf("%ld\t", array[i]);
        }
        printf("\n");
        ;
}

用时0.28秒,相差10倍之多。

[tong@T7 chimian]$ gcc -Wall -O3 crun.c
[tong@T7 chimian]$ time ./a.out 

real    0m0.288s
user    0m0.277s
sys     0m0.010s

尝试将py编译,后执行,效率无任何变化

[tong@T7 chimian]$ python2 -O3 -m py_compile run.py
[tong@T7 chimian]$ time python2 run.pyo
[, , , , , , , , , , ]

real    0m3.865s
user    0m3.833s
sys     0m0.030s

如前文,再次将类型转换函数替换为普通的赋值语句

[tong@T7 chimian]$ diff run.py run-md.py
17c17
<                       value_int = int(value_str)
---
>                       value_int =
[tong@T7 chimian]$ vim run-md.py
[tong@T7 chimian]$ vim run-md.py
[tong@T7 chimian]$ time ./run-md.py
[, , , , , , , , , , ]

real    0m1.449s
user    0m1.420s
sys     0m0.027s
[tong@T7 chimian]$ vim run-md.py
[tong@T7 chimian]$ kernprof -v -l run-md.py
[, , , , , , , , , , ]
Wrote profile results to run-md.py.lprof
Timer unit: 1e- s

Total time: 17.2632 s
File: run-md.py
Function: main at line 

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
                                                @profile
                                                def main():
                                7.0      0.0         f = open('base.txt', 'r')
                                ,,,,,,,,,,]
                      0.6      1.9         for line in f.readlines():
                      1.2      4.2                 l = line.strip().split(',')
                      0.5      1.7                 length = len(l)
                      :
                                                                      raise Exception, "HUGE_TONG: Wrong format"

                   ,):
                                                              #       result[i] += int(l[i+skip])
                   0.4     17.2                         index = i + skip
                   0.4     17.0                         value_str = l[index]

                   0.5     19.3                         result[i] += value_int
                             12.0      0.0         f.close()
                             39.0      0.0         print result

再测,将内层for循环的操作,只保留为一次加赋值操作。

[tong@T7 chimian]$ time ./run-md.py
[, , , , , , , , , , ]

real    0m0.960s
user    0m0.923s
sys     0m0.033s
[tong@T7 chimian]$ cat run-md.py
#! /usr/bin/python2

#@profile
def main():
        f = open('base.txt', 'r')
        result = [,,,,,,,,,,]
        for line in f.readlines():
                l = line.strip().split(',')
                length = len(l)
                 :
                        raise Exception, "HUGE_TONG: Wrong format"
                skip = length -
                a =
                ,):
        #               result[i] += int(l[i+skip])
        #               index = i + skip
        #               value_str = l[index]
        #               value_int =
        #               result[i] += value_int
        #               result[i] +=
                        a +=
        f.close()
        print result

if __name__ == '__main__':
        r = main()
#       exit(r)
[tong@T7 chimian]$ 

再测,仅做6600000次加赋值操作

[tong@T7 chimian]$ time ./run-test.py 

real    0m0.372s
user    0m0.293s
sys     0m0.077s
[tong@T7 chimian]$ cat run-test.py
#! /usr/bin/python2

def main():
        a = ;
        , ):
                a += 

if __name__ == '__main__':
        main()
[tong@T7 chimian]$ 

并没有太多的思路,查到一篇 文章,写的很好。

看完这篇文章,我觉得我被他们耍了;这是一个典型的python特有的优化问题,根本没有跨语言优化的意义。

----------

Update @ 20161111

然而,就这个问题本身,即使不考虑python特性,其实仍然还是可以继续优化的,比较python+C混编。看看性能如何。

然而,对于这样一个毫无实际意义的问题,我不打算再继续了,结贴!

[daily][optimize] 一个小python程序的性能优化 (python类型转换函数引申的性能优化)的更多相关文章

  1. 在windows中:双击运行Python程序、后台运行Python程序

    在windows中:双击运行Python程序.后台运行Python程序 安装Python解释器的windows环境,如果双击运行*.py的文件,会闪退.怎样避免闪退呢? 我们用python的日志输出程 ...

  2. [python]通过微信公众号“Python程序员”,编写python代码

    今天发现微信公众号中,居然可以编写python代码,很是惊喜,觉得蛮有趣的. 步骤如下: 1.关注微信公众号“Python程序员” 2.关注成功后,点击右下角的“潘多拉”->"Pyth ...

  3. 一个小makefile程序

    刚刚开始学习linux下的程序,总需要自己写一些小型的makefile文件,这里给出一个makefile的例子,方便以后借鉴. 程序包含了main.c(需要头文件input.h 和 display.h ...

  4. 编写一个小Servlet程序

    1.编写一个java类,此类继承HttpServlet 创建完工程(见上一篇随笔:使用eclipse创建在myeclipse中运行的web工程),在src中新建一个包,包名字叫servlet:再新建一 ...

  5. 一个小的程序--实现中英文切换(纯css)

    <!doctype html><html lang="en"> <head> <meta charset="UTF-8" ...

  6. 【转】为 OSCHINA 聚会搞的一个小抽奖程序

    http://www.oschina.net/code/snippet_12_7605 在线演示: http://www.oschina.net/r.html

  7. python 教程 第八章、 第一个python程序

    第八章. 第一个python程序 #!/usr/bin/env python import os import sys import time source = [r'G:\s1', r'G:\s2' ...

  8. 成为python程序员,对疫情过后的毕业生来说,真是一个不错的方向吗?

    Python最近几年,一直被炒得很火,这其中有商业因素,但更重要的是即将到来的人工智能时代,而python就恰好是最适合的编程语言. 所以无论是在职的人,还是在校的学生,都想着跟上这一趋势,但,在今年 ...

  9. 谈谈 Python 程序的运行原理

    因为我的个人网站 restran.net 已经启用,博客园的内容已经不再更新.请访问我的个人网站获取这篇文章的最新内容,谈谈 Python 程序的运行原理 这篇文章准确说是『Python 源码剖析』的 ...

随机推荐

  1. 电赛总结(二)——AD芯片总结之AD7705

    1.特性参数 (1)16位无丢失代码性AD转化器 (2)只需要3.3V供电(即只需要单片机即可供电) (3)双通道差分输入 (4)基准电压为2.5V时,在单极性信号下,输入范围是0到2.5V,在双极性 ...

  2. CDH 的Cloudera Manager免费与收费版的对比表

    CDH 特性 免费版 付费版 Deployment, Configuration & Management 系统管理 Automated Deployment & Hadoop Rea ...

  3. TFS2012团队管理基本配置及基础使用方法

    本文介绍如何在VS2012中使用微软提供的TFS2012服务器进行团队协作开发,免费默认只支持5用户,主要分为两大步服务器配置跟客户端配置. 转载请标注:http://www.kwstu.com/Ar ...

  4. 模拟 ACdream 1196 KIDx's Pagination

    题目传送门 /* 简单模拟:考虑边界的情况输出的是不一样的,还有思维好,代码能短很多 */ #include <cstdio> #include <iostream> #inc ...

  5. java中的String设计原理

    首先,必须强调一点:String Pool不是在堆区,也不是在栈区,而是存在于方法区(Method Area) 解析: String Pool是常量池(Constant  Pool)中的一块. 我们知 ...

  6. TYVJ P1008 传球游戏

      时间: 1000ms / 空间: 131072KiB / Java类名: Main 背景 NOIP2008复赛普及组第三题 描述 上体育课的时候,小蛮的老师经常带着同学们一起做游戏.这次,老师带着 ...

  7. 移动互联网终端的touch事件,touchstart, touchend, touchmove

    如果我们允许用户在页面上用类似桌面浏览器鼠标手势的方式来控制WEB APP,这个页面上肯定是有很多可点击区域的,如果用户触摸到了那些可点击区域怎么办呢??诸如智能手机和平板电脑一类的移动设备通常会有一 ...

  8. 【BZOJ】1038: [ZJOI2008]瞭望塔

    http://www.lydsy.com/JudgeOnline/problem.php?id=1038 题意:给出n个x轴各不相同的二维整点,且升序,n<=300,坐标绝对值<=10^6 ...

  9. 【BZOJ】1507: [NOI2003]Editor(Splay)

    http://www.lydsy.com/JudgeOnline/problem.php?id=1507 当练splay模板了,发现wjmzbmr的splay写得异常简介,学习了.orzzzzzzzz ...

  10. Shell 小技巧的问题 mysql -e ,字符串替换telnet命令检测

    api_url= #echo ${api_url/:/ }a #echo `echo $api_url|sed -i 's/:/ /' ` #|sed -i 's/:/ /' ` #sed 'as/: ...