前天,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. 《数据结构与算法分析》学习笔记(五)——队ADT

    一.队的概念 队列也是一种表,但是是一种受限的表,只允许从一端插入,另一端山粗的表. 二.队列的数组实现 #define QMAXSIZE 100 typedef int Position; type ...

  2. 建模算法(八)——插值

    插值:求过已知有限个数据点的近似函数 拟合:已知有限个数据点,求近似函数,不要求过已知数据点,只要求在某种意义下在这些点的误差最小 (一)插值方法 一.拉格朗日多项式插值 1.插值多项式 就是做出一个 ...

  3. 使用 Docker 建立 Mysql 集群

    软件环境介绍操作系统:Ubuntu server 64bit 14.04.1Docker 版本 1.6.2数据库:Mariadb 10.10 (Mariadb 是 MySQL 之父在 MySQL 被 ...

  4. Windows计数器做性能监控(window server 2008服务器)

    使用Windows计数器 一.创建数据收集器集 二.创建数据收集器 三.使用数据收集器 1.修改数据收集器的属性 2.手动启用.手动停止数据收集器集 3.计划任务 4.在性能监视器中查看 一.性能监视 ...

  5. Winedt打开tex文件报错error reading的解决方案

    我刚装就发现winedt打开一些.tex文件时会出现reading error,然后看不到任何文字(网上有人讨论打开是乱码的问题,但是我的是完全看不到任何东西),我的系统winxp,网上有人说好像是和 ...

  6. Xamarin.Android提示aapt退出,代码为255

    Xamarin.Android提示aapt退出,代码为255 错误信息:”aapt.exe”已退出,代码为255.出现这种问题,通常是由于该项目所使用Android SDK不完整.通过SDK Mana ...

  7. c#知识总结2

    四.C#类型转换 类型转换就是把一种类型转换成为另一种类型. 隐式类型转换:c#默认的以安全方式进行的转换.例如小整数类型转换为大整数类型.派生类转换为基类 显式类型转换:用户使用的预定义的函数显式完 ...

  8. 不用写软件,纯JS 实现QQ空间自动点赞

    这里分享一个自己写的点赞JS,已实现了好友动态.右侧栏猜你喜欢 点赞,有兴趣的朋友可以加上去玩玩.打开浏览器的开发者模式运行就可以看到效果了 var count = 0; var total = 0; ...

  9. 关于配置文件权衡,.config VS .xml

    众所周知,程序的灵活性有一部分就是“配”出来了. 当然,config文件从来就没有让.NET的同学轻松过,至少,我觉得很麻烦. 1.config .NET的配置文件方便,其实最方便的是appSetti ...

  10. 【python游戏编程之旅】第三篇---pygame事件与设备轮询

    本系列博客介绍以python+pygame库进行小游戏的开发.有写的不对之处还望各位海涵. 在上一篇博客中,我们学习了pygame中的IO.数据http://www.cnblogs.com/msxh/ ...