• 第二次结对编程


    第二次结对编程

    Github 链接:https://github.com/eastOffice/WordFrequency

    合作方式

    我们这一次的合作方式是 pair coding 和 separate coding 相结合。我们先一起讨论这个项目整体设计方案,功能怎么分配,然后建立 github 仓库,两人各自开发自己的功能。遇到问题和两个人都要使用的功能,会采取 pair coding 的方式来解决。

    关于讨论

    1. Design guideline:这次的项目设计其实比较简单,按照功能分开即可,我们用了很短的时间就搭建了代码的整体框架,并且后面没有改动。
    2. Coding convention: 编程规范在第一次讨论的时候确定,并且一起写了一些样板函数的接口,之后的函数都参照样板函数来写。
    3. Reach agreement: 这个也很简单,谁的方案更好,时间更快,就用谁的想法。

    Dealing with Time Constraint

    我们一开始就给自己估计了项目的时间,而且给了一个非常短的预算:10月28号开始写,并且争取一天基本写完。事实上,我们完成的效果还是符合预期的,可以从 github 仓库 commit 的时间点中看到,10 月 28 号一天我们基本上完成了框架中的内容,之后两天都在进行测试,debug,优化。

    时间的确是一个比较大的限制。我们只有周末的时候比较有空来写这个项目,平时都是晚上抽两个小时出来。不过这个项目本身也不难,我们还是很快实现了全部的内容并且成功的测试,优化。

    队友

    优点:

    • 沟通积极
    • 精益求精
    • 编程速度快

    缺点:

    • 可以提高编程规范

    单元测试和回归测试

    单元测试:我们的代码结构中,每一个功能都由 modes.py 中的一个模式函数负责,每一个模式函数由逻辑代码和 utils.py 里面的支持函数组成。因此,单元测试非常简单,我们首先从 utils.py 里面的支持函数开始测试,然后测试模式函数。

    回归测试:每次新加入一个函数,都会新加入测试代码,运行所有的测试代码,保证各种参数的组合都正确运行(事实上这一点也很简单,只要控制模式函数的入口参数就行)。

    因此,每一阶段的测试代码都是在同一个测试文件中。为了方便阅读和进行覆盖率测试,我们把最后的测试代码做了压缩和整理,去掉了最基础的 utils.py 里面的支持函数的测试,整合到了 coverage_test.py 中。

    覆盖率测试

    在完成所有功能之后,我们在测试文件的基础上整合出 coverage_test.py,利用 python 的 coverage 包:

    coverage run coverage_test.py
    coverage report
    

    得到结果:

    Name               Stmts   Miss  Cover
    --------------------------------------
    coverage_test.py      36      0   100%
    modes.py              94      0   100%
    utils.py              68      0   100%
    --------------------------------------
    TOTAL                198      0   100%
    

    结果记录在 coverage_test.txt 中。

    效能分析和优化

    我们使用了 python 的 cProfile 来进行效能分析,这里打印了每个函数 internal time 的最高的前十个:

    优化前:

    Tue Oct 30 20:14:19 2018    profile.stats
    
             697390 function calls (690360 primitive calls) in 0.650 seconds
    
       Ordered by: internal time
       List reduced from 2079 to 10 due to restriction <10>
    
       ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        22391    0.141    0.000    0.141    0.000 C:Usersv-yizzhaDesktopWordFrequencymodes.py:102(<listcomp>)
         1375    0.061    0.000    0.061    0.000 {built-in method nt.stat}
        22391    0.060    0.000    0.074    0.000 C:Usersv-yizzhaDesktopWordFrequencyutils.py:14(get_phrases)
            1    0.045    0.045    0.382    0.382 C:Usersv-yizzhaDesktopWordFrequencymodes.py:83(mode_p)
        27395    0.039    0.000    0.039    0.000 {method 'split' of 're.Pattern' objects}
          306    0.023    0.000    0.023    0.000 {built-in method marshal.loads}
        12/11    0.020    0.002    0.023    0.002 {built-in method _imp.create_dynamic}
          306    0.017    0.000    0.027    0.000 <frozen importlib._bootstrap_external>:914(get_data)
        27798    0.011    0.000    0.062    0.000 C:Usersv-yizzhaAppDataLocalContinuumanaconda3envs
    ltklib
    e.py:271(_compile)
    1067/1064    0.010    0.000    0.039    0.000 {built-in method builtins.__build_class__}
    
    

    发现用时最长的是 modes.py 102行的 list 比较操作:

    pre_list = [word for word in pre_list if word not in stop_words]
    

    于是我把 stop_words 这个 list 变成 set,优化之后:

    Tue Oct 30 20:23:31 2018    profile.stats
    
             697516 function calls (690485 primitive calls) in 0.510 seconds
    
       Ordered by: internal time
       List reduced from 2094 to 10 due to restriction <10>
    
       ncalls  tottime  percall  cumtime  percall filename:lineno(function)
         1379    0.060    0.000    0.060    0.000 {built-in method nt.stat}
        22391    0.058    0.000    0.072    0.000 C:Usersv-yizzhaDesktopWordFrequencyutils.py:14(get_phrases)
            1    0.040    0.040    0.234    0.234 C:Usersv-yizzhaDesktopWordFrequencymodes.py:83(mode_p)
        27395    0.037    0.000    0.037    0.000 {method 'split' of 're.Pattern' objects}
          304    0.023    0.000    0.023    0.000 {built-in method marshal.loads}
        12/11    0.018    0.002    0.020    0.002 {built-in method _imp.create_dynamic}
          308    0.018    0.000    0.028    0.000 <frozen importlib._bootstrap_external>:914(get_data)
        22391    0.011    0.000    0.011    0.000 C:Usersv-yizzhaDesktopWordFrequencymodes.py:102(<listcomp>)
    1067/1064    0.010    0.000    0.039    0.000 {built-in method builtins.__build_class__}
        27798    0.010    0.000    0.058    0.000 C:Usersv-yizzhaAppDataLocalContinuumanaconda3envs
    ltklib
    e.py:271(_compile)
    

    可以看到 list comp 的时间从 0.141 秒变成了0.011秒。

    下面是队友的一次优化成果,优化前:

    Thu Nov  1 18:20:35 2018    proflie.status
    
             1714748 function calls (1701302 primitive calls) in 1.118 seconds
    
       Ordered by: internal time
       List reduced from 3945 to 10 due to restriction <10>
    
       ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        22391    0.179    0.000    0.238    0.000 C:Usersv-qiyaoDocumentsWordFrequencyutils.py:14(get_phrases)
         3163    0.111    0.000    0.111    0.000 {built-in method nt.stat}
       100/78    0.059    0.001    0.085    0.001 {built-in method _imp.create_dynamic}
          741    0.052    0.000    0.052    0.000 {built-in method marshal.loads}
            1    0.041    0.041    0.455    0.455 C:Usersv-qiyaoDocumentsWordFrequencymodes.py:83(mode_p)
        27395    0.040    0.000    0.040    0.000 {method 'split' of '_sre.SRE_Pattern' objects}
       105354    0.035    0.000    0.035    0.000 C:Usersv-qiyaoAppDataLocalContinuumanaconda3libsite-packages
    ltkprobability.py:127(__setitem__)
          743    0.035    0.000    0.054    0.000 <frozen importlib._bootstrap_external>:830(get_data)
        992/1    0.032    0.000    1.119    1.119 {built-in method builtins.exec}
            1    0.030    0.030    0.065    0.065 {built-in method _collections._count_eleme
    

    时间最长的是 get_phrases 函数,用时 0.179 秒,看到原来的 get_phrases 函数是这样的:

    while(len(pre_list) >= n):
            target_phrase = []
            for i in range(n):
                if not_word(pre_list[i]):
                    for j in range(i+1):
                        pre_list.pop(0)
                    break
                else:
                    target_phrase.append(pre_list[i])
            if len(target_phrase) == n :
                target_str = target_phrase[0]
                for i in range(n-1):
                    target_str += " "+target_phrase[i+1] 
                result.append(target_str)
                pre_list.pop(0)
        return result     
    

    其中的 pop 操作都是不必要的,优化后:

    for j in range(len(pre_list)+1-n):
            target_phrase = ""
            for i in range(n):
                if not_word(pre_list[i+j]):
                    j += i
                    break
                elif target_phrase == "":
                    target_phrase += pre_list[i+j]
                else :
                    target_phrase += (' ' + pre_list[i+j])
                if i == n-1:
                    result.append(target_phrase)
    

    优化后的 profile:

    Thu Nov  1 18:22:38 2018    proflie.status
    
             1187845 function calls (1174399 primitive calls) in 0.972 seconds
    
       Ordered by: internal time
       List reduced from 3945 to 10 due to restriction <10>
    
       ncalls  tottime  percall  cumtime  percall filename:lineno(function)
         3163    0.109    0.000    0.109    0.000 {built-in method nt.stat}
        22391    0.095    0.000    0.118    0.000 C:Usersv-qiyaoDocumentsWordFrequencyutils.py:14(get_phrases)
       100/78    0.055    0.001    0.081    0.001 {built-in method _imp.create_dynamic}
          741    0.052    0.000    0.052    0.000 {built-in method marshal.loads}
            1    0.040    0.040    0.336    0.336 C:Usersv-qiyaoDocumentsWordFrequencymodes.py:83(mode_p)
        27395    0.039    0.000    0.039    0.000 {method 'split' of '_sre.SRE_Pattern' objects}
       105544    0.036    0.000    0.036    0.000 C:Usersv-qiyaoAppDataLocalContinuumanaconda3libsite-packages
    ltkprobability.py:127(__setitem__)
          743    0.034    0.000    0.053    0.000 <frozen importlib._bootstrap_external>:830(get_data)
            1    0.033    0.033    0.068    0.068 {built-in method _collections._count_elements}
        992/1    0.030    0.000    0.973    0.973 {built-in method builtins.exec}
    

    可以看到 get_phrases 函数用时变成了0.095秒。

    这是使用了 cProfile 之后的两次优化,效果都十分好。到此为止,用时最长的函数都已经是一些内建函数了。我们在第一遍写代码的时候就比较注重代码的整洁和效率,曾因为改进了算法以及去掉不必要的文件读写,把一个20秒的命令优化到了1秒以内。

  • 相关阅读:
    MySQL报错【innoDB is limited to rowlogging when transaction isolation level is READ COMMITTED or READ UNCOMMITTED】
    【转】linux下解压命令大全
    java学习笔记
    【转】Linux安装MySQL tar文件
    Android部署及安装
    正则表达式各个符合含义
    [转]Linux下crontab命令的用法
    XTABLE学习笔记
    MySQL的mysqldump工具的基本用法
    java开发注意事项
  • 原文地址:https://www.cnblogs.com/yizhuoz/p/9900466.html
Copyright © 2020-2023  润新知