- CREATED

ch1 preview

GIL (Global Interpreter Lock)

multi thread 프로그래밍은 그다지 효율적이지 않지만 I/O bound되는 프로그램의 경우는 효과를 볼 수 있다. multi processing 을 사용하여 성능 향상을 볼 수 있다.


ch2 bottleneck

profling 기법

IPython의 %timeit, time.time(), timing decorator, cProfile, line_profiler, heapy, dowser, memory_profiler

https://github.com/kchhero/suker_python_project/tree/master/highperfbook/test2_1.py

  • unix의 time 명령어를 사용하는 방법
    $ /usr/bin/time -p python test2_1.py
    Length of x: 1000
    Total elements: 1000000
    calculate_z_serial_purepythontook 5.960104942321777 seconds
    real 6.51
    user 6.47
    sys 0.04
    

    more detail : –verbose 사용

    • Major (requiring I/O) page faults : OS RAM에서 필요한 데이터를 찾을 수 없어서 disk에서 page를 불러 왔는지의 여부를 나타낸다.


  • 프로파일링하기 전에 가설을 먼저 검증하는 습관을 들이는것이 좋다. 감으로 프로파일링하는 습관은 피하도록 한다.

  • cProfile

    $ python -m cProfile -s cumulative test2_1.py 
    ('Length of x:', 1000)
    ('Total elements:', 1000000)
    ('calculate_z_serial_purepythontook', 7.633919954299927, 'seconds')
           36221992 function calls in 8.216 seconds
    
     Ordered by: cumulative time
    
     ncalls  tottime  percall  cumtime  percall filename:lineno(function)
          1    0.023    0.023    8.216    8.216 test2_1.py:1(<module>)
          1    0.480    0.480    8.193    8.193 test2_1.py:7(calc_pure_python)
          1    5.791    5.791    7.634    7.634 test2_1.py:40(calculate_z_serial_purepython)
     34219980    1.832    0.000    1.832    0.000 {abs}
    2002000    0.075    0.000    0.075    0.000 {method 'append' of 'list' objects}
          1    0.012    0.012    0.012    0.012 {range}
          1    0.004    0.004    0.004    0.004 {sum}
          2    0.000    0.000    0.000    0.000 {time.time}
          4    0.000    0.000    0.000    0.000 {len}
          1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
    


  • line_proofiler Robert Kern의 line_profiler, 개별 함수를 한 줄씩 프로파일링할 수 있는 강력한 도구.

  • https://github.com/rkern/line_profiler

    $ pip install line_profiler
    ...
    $ kernprof -l -v test2_1.py
    Length of x: 1000
    Total elements: 1000000
    calculate_z_serial_purepythontook 70.00200009346008 seconds
    Wrote profile results to test2_1.py.lprof
    Timer unit: 1e-06 s
    ...
    Total time: 39.5841 s
    File: test2_1.py
    Function: calculate_z_serial_purepython at line 40
    ...
    Line #      Hits         Time  Per Hit   % Time  Line Contents
    =============================================================
      40                                           @profile
      41                                           def calculate_z_serial_purepython(maxiter, zs, cs):
      42         1       1788.0   1788.0      0.0      output = [0] * len(zs)
      43   1000001     305261.0      0.3      0.8      for i in range(len(zs)):
      44   1000000     291756.0      0.3      0.7          n = 0
      45   1000000     335763.0      0.3      0.8          z = zs[i]
      46   1000000     315128.0      0.3      0.8          c = cs[i]
      47  34219980   15392219.0      0.4     38.9          while abs(z) < 2 and n < maxiter:
      48  33219980   12199147.0      0.4     30.8              z = z * z + c
      49  33219980   10407960.0      0.3     26.3              n += 1
      50   1000000     335111.0      0.3      0.8          output[i] = n
      51
      52         1          1.0      1.0      0.0      return output
    


  • memory_profiler $ pip install psutil $ pip install memory_profiler
    $ python3 -m memory_profiler test2_1.py
    Length of x: 1000
    Total elements: 100000
    


  • 그외 heapy, dowser 등등


CPython 내부 동작, bytecode의 이해

  • dis 모듈
    #test2_dis_byte.py
    import dis
    import test2_1
    ...
    dis.dis(test2_1.calculate_z_serial_purepython)
    
    $ python test2_dis_byte.py
     41           0 LOAD_CONST               1 (0)
                3 BUILD_LIST               1
                6 LOAD_GLOBAL              0 (len)
                9 LOAD_FAST                1 (zs)
               12 CALL_FUNCTION            1
               15 BINARY_MULTIPLY     
               16 STORE_FAST               3 (output)
    ...
     42          19 SETUP_LOOP             123 (to 145)
               22 LOAD_GLOBAL              1 (range)
    


  • 방식에 따른 복잡도
    def fn_expressive(upper=100000):
      total = 0
      for n in range(upper):
          total += n
      return total
    ...
    def fn_terse(upper=100000):
      return sum(range(upper))
    ...
    print(fn_expressive())
    print(fn_terse())
    
    import dis
    import test2_11
    ...
    print("fn_expressive")
    dis.dis(test2_11.fn_expressive)
    ...
    print("fn_terse")
    dis.dis(test2_11.fn_terse)
    
    $ python test2_11_dis_byte.py 
    4999950000
    4999950000
    fn_expressive
    2           0 LOAD_CONST               1 (0)
                3 STORE_FAST               1 (total)
    ...
    3           6 SETUP_LOOP              30 (to 39)
                9 LOAD_GLOBAL              0 (range)
               12 LOAD_FAST                0 (upper)
               15 CALL_FUNCTION            1
               18 GET_ITER            
          >>   19 FOR_ITER                16 (to 38)
               22 STORE_FAST               2 (n)
    ...
    4          25 LOAD_FAST                1 (total)
               28 LOAD_FAST                2 (n)
               31 INPLACE_ADD         
               32 STORE_FAST               1 (total)
               35 JUMP_ABSOLUTE           19
          >>   38 POP_BLOCK           
    ...
    5     >>   39 LOAD_FAST                1 (total)
               42 RETURN_VALUE        
    fn_terse
    9           0 LOAD_GLOBAL              0 (sum)
                3 LOAD_GLOBAL              1 (range)
                6 LOAD_FAST                0 (upper)
                9 CALL_FUNCTION            1
               12 CALL_FUNCTION            1
               15 RETURN_VALUE
    

내장 함수를 사용하는 경우가 더 간결하다. 그렇지 않은 경우는 루프가 반복될 때마다 변수 타입을 검사한다.


kchhero

If you do not act, there is no change.