Share @ LinkedIn Facebook  profiling, scalene, cpu-profiling, memory-profiling
Scalene - CPU and Memory Profiler for Python Code

Scalene - CPU and Memory Profiler for Python Code

The scalene is a high-performance CPU usage and memory profiler for python code. It works comparatively faster than other python profilers like cProfile, pprofile, memory_profiler, line_profiler, etc. Apart from that, the majority of profilers either provide CPU profiling or memory profiling but not both whereas scalene provides both types of profiling. The scalene uses sampling for profiling, unlike other profiling tools that use python tracing functionality to profile. Scalene is like line_profiler which provides CPU usage per line for lines which are taking most of the time, unlike other python profilers which provides function level information. The scalene separates time profiling of python code and native code (C, C++ code, python libraries which comes with python installation, etc.) which let us concentrate on our code which we can optimize further. The scalene adds quite less overhead to the running time of python code compared to other python profilers available. We'll be explaining the usage of scalene as a part of this tutorial through various examples.

Below we are showing a comparison of scalene profiler with other python profilers which is present on the scalene GitHub page.

Scalene - CPU and Memory Profiler for Python Code

In the above figure, green represents fast, yellow represents slow, and red represents very slow.

Example 1: Default Profiling

As a part of our first example of scalene, we'll simply explain how we can profile python script using scalene. We have below included a simple script which we'll be using for profiling. The script generates two lists of size 100000 with random numbers between 1-10. We then take elements from both lists one by one and add them to generate the third list. We then take a sum of the third list and print it. We'll be then profiling this script in jupyter notebook using the ! magic command which lets us run shell/command prompt commands from jupyter notebook. Please make a note that you can also run the script from the command line. We have saved the script into the profiling_examples folder by the name of scalene_ex1.py

CODE

def main_func():
    import random
    arr1 = [random.randint(1,10) for i in range(100000)]
    arr2 = [random.randint(1,10) for i in range(100000)]
    arr3 = [arr1[i]+arr2[i] for i in range(100000)]
    tot = sum(arr3)
    print(tot)

if __name__ == "__main__":
    main_func()

Below is a list of columns present in profiling output.

  • Time % Python - It specifies % of the time spent in python.
  • Time % native - It specifies % of time spent in native code (python library which comes along with python package like random, DateTime, etc and C code).
  • Sys % - It specifies % time spent in other tasks of OS, not from python or native code. If we put the thread to sleep and CPU is utilized for other tasks, this refers to sys.
  • Mem % Python - It specifies the amount of memory used by python.
  • Net (MB) - It specifies net memory allocation in MB during that line of code. If it’s negative then it means that memory was recollected during that instruction.
  • Memory usage over time /% - It shows memory usage over time as a bar chart.
  • Copy(MB/s) - It specifies the amount of memory copied per second.

Apart from this, scalene also shows memory usage as a bar chart for the whole script at the beginning with maximum memory usage in MB throughout that script execution. It also specifies how much of the total script execution time profiling was performed.

Below we have executed a script using scalene to profile its performance.

In [8]:
!scalene profiling_examples/scalene_ex1.py
1098085
                                      Memory usage: ▂▂▆▆█▇▆▅▆█▇▆▆▅▆▅▆▆▆▆▆▆ (max:  11.00MB)
                             profiling_examples/scalene_ex1.py: % of time = 100.00% out of   0.52s.
       ╷       ╷       ╷    ╷       ╷      ╷              ╷       ╷
  Line Time % Time % Sys Mem %  Net   Memory usage  Copy                                                                Python native %   Python (MB)  over time / % (MB/s) profiling_examples/scalene_ex1.py                            
╺━━━━━━┿━━━━━━━┿━━━━━━━┿━━━━┿━━━━━━━┿━━━━━━┿━━━━━━━━━━━━━━┿━━━━━━━┿━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╸
     1 │       │       │    │       │      │              │       │def main_func():                                           
     2 │       │       │    │       │      │              │       │    import random                                          
     3 │   47% │       │    │  100% │    7 │▁▂▂▃▃         │    31 │    arr1 = [random.randint(1,10) for i in range(100000)]   
     4 │   37% │       │    │  100% │    0 │▂▂▂▂          │    13 │    arr2 = [random.randint(1,10) for i in range(100000)]   
     5 │   16% │       │    │  100% │    1 │▂▂▂▂▂▂▂       │    12 │    arr3 = [arr1[i]+arr2[i] for i in range(100000)]        
     6 │       │       │    │       │      │              │       │    tot = sum(arr3)                                        
     7 │       │       │    │       │      │              │       │    print(tot)                                             
     8 │       │       │    │       │      │              │       │                                                           
     9 │       │       │    │       │      │              │       │if __name__ == "__main__":                                 
    10 │       │       │    │       │      │              │       │    main_func()                                            
       ╵       ╵       ╵    ╵       ╵      ╵              ╵       ╵

Example 2: Saving Output To A File

As a part of our second example, we'll explain how we can save the output of profiling to a file. The scalene has an option named --output which lets us save profiling output to a file. We need to pass the output file name followed by this option and it'll save profiling output to that file.

Below we have explained how we can store profiling output to a file.

In [11]:
!scalene --outfile profiling_output.txt profiling_examples/scalene_ex1.py
1101208
In [12]:
!cat profiling_output.txt
                                           Memory usage: ▂▄▆▆▆▇███▇███ (max:  17.00MB)
                             profiling_examples/scalene_ex1.py: % of time = 100.00% out of   0.65s.
       ╷       ╷       ╷    ╷       ╷      ╷              ╷       ╷
  Line │Time % │Time % │Sys │Mem %  │Net   │Memory usage  │Copy   │
       │Python │native │%   │Python │(MB)  │over time / % │(MB/s) │profiling_examples/scalene_ex1.py
╺━━━━━━┿━━━━━━━┿━━━━━━━┿━━━━┿━━━━━━━┿━━━━━━┿━━━━━━━━━━━━━━┿━━━━━━━┿━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╸
     1 │       │       │    │       │      │              │       │def main_func():
     2 │       │       │    │       │      │              │       │    import random
     3 │   46% │       │    │  100% │    3 │▁▁▁▁▁         │    25 │    arr1 = [random.randint(1,10) for i in range(100000)]
     4 │   43% │       │    │  100% │    9 │▂▂▃▃▃         │    10 │    arr2 = [random.randint(1,10) for i in range(100000)]
     5 │   11% │       │    │  100% │    3 │▂▃▃▃▃▂▃▃▃     │    10 │    arr3 = [arr1[i]+arr2[i] for i in range(100000)]
     6 │       │       │    │       │      │              │       │    tot = sum(arr3)
     7 │       │       │    │       │      │              │       │    print(tot)
     8 │       │       │    │       │      │              │       │
     9 │       │       │    │       │      │              │       │if __name__ == "__main__":
    10 │       │       │    │       │      │              │       │    main_func()
       ╵       ╵       ╵    ╵       ╵      ╵              ╵       ╵

Example 3: Saving Output In A HTML Format

As a part of our third example, we'll explain how we can store the output of profiling as HTML. The scalene has options named --html which converts the output of profiling into the string which has profiled output as HTML.

Below we have explained how we can store HTML output to a file and then reopen it and display in a jupyter notebook.

In [14]:
!scalene --html --outfile profiling_output.html profiling_examples/scalene_ex1.py
1100247
In [16]:
from IPython.display import HTML

HTML(open("profiling_output.html").read())
Out[16]:
                                      Memory usage: ▃▃▂▆██▇▇▆▅▄▅▆▅▆▅▆▆▇▆▇█ (max:  11.00MB)
                             profiling_examples/scalene_ex1.py: % of time = 100.00% out of   0.74s.
       ╷       ╷       ╷    ╷       ╷      ╷              ╷       ╷
  Line Time % Time % Sys Mem %  Net   Memory usage  Copy                                                                Python native %   Python (MB)  over time / % (MB/s) profiling_examples/scalene_ex1.py                            
╺━━━━━━┿━━━━━━━┿━━━━━━━┿━━━━┿━━━━━━━┿━━━━━━┿━━━━━━━━━━━━━━┿━━━━━━━┿━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╸
     1 │       │       │    │       │      │              │       │def main_func():                                           
     2 │       │       │    │       │      │              │       │    import random                                          
     3 │   40% │       │    │  100% │    5 │▂▁▁▁▂▁▂▂▂     │    22 │    arr1 = [random.randint(1,10) for i in range(100000)]   
     4 │   44% │       │    │  100% │    5 │▂▃▃▂▂▂▂▂▃     │     9 │    arr2 = [random.randint(1,10) for i in range(100000)]   
     5 │   16% │       │    │  100% │    0 │▂▂▂▂          │     9 │    arr3 = [arr1[i]+arr2[i] for i in range(100000)]        
     6 │       │       │    │       │      │              │       │    tot = sum(arr3)                                        
     7 │       │       │    │       │      │              │       │    print(tot)                                             
     8 │       │       │    │       │      │              │       │                                                           
     9 │       │       │    │       │      │              │       │if __name__ == "__main__":                                 
    10 │       │       │    │       │      │              │       │    main_func()                                            
       ╵       ╵       ╵    ╵       ╵      ╵              ╵       ╵
generated by the scalene profiler

Example 3: Displaying Only Important Lines In Profiling

The scalene also provides us with functionality to display only a line where sampling was performed and profiling results were recorded. We can instruct scalene to only show us the line for which profiling data is present by using the --reduced-profile option.

Below we have explained the usage of the --reduced-profile option on our script and we can see that it only displays 3 lines where memory and CPU usage is maximum and are profiled by scalene.

In [18]:
!scalene --reduced-profile profiling_examples/scalene_ex1.py
1098035
                                       Memory usage: ▂▂▄▅▆█▇▇▇▆▆▅▄▃▄▃▄▅▄▅▆ (max:  11.00MB)
                             profiling_examples/scalene_ex1.py: % of time = 100.00% out of   0.61s.
       ╷       ╷       ╷    ╷       ╷      ╷              ╷       ╷
  Line Time % Time % Sys Mem %  Net   Memory usage  Copy                                                                Python native %   Python (MB)  over time / % (MB/s) profiling_examples/scalene_ex1.py                            
╺━━━━━━┿━━━━━━━┿━━━━━━━┿━━━━┿━━━━━━━┿━━━━━━┿━━━━━━━━━━━━━━┿━━━━━━━┿━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╸
   ... │       │       │    │       │      │              │       │
     3 │   40% │       │    │  100% │    4 │▁▁▁▁▁▁▁▂      │    27 │    arr1 = [random.randint(1,10) for i in range(100000)]   
     4 │   43% │       │    │  100% │    6 │▂▂▃▃▃▃        │    10 │    arr2 = [random.randint(1,10) for i in range(100000)]   
     5 │   17% │       │    │  100% │   -3 │▂▁▁▂▁▂▂       │    10 │    arr3 = [arr1[i]+arr2[i] for i in range(100000)]        
   ... │       │       │    │       │      │              │       │
       ╵       ╵       ╵    ╵       ╵      ╵              ╵       ╵

Example 4: CPU Only Profiling

The fourth example explains how we can eliminate memory usage information from profiling if we don't need it. We can use the --cpu-only option if we want to check only CPU consumption by our script.

In [29]:
!scalene --cpu-only profiling_examples/scalene_ex1.py
1099308
                            profiling_examples/scalene_ex1.py: % of time = 100.00% out of   0.15s.
       ╷       ╷       ╷    ╷
  Line Time % Time % Sys                                                                                                  Python native %   profiling_examples/scalene_ex1.py                                                                
╺━━━━━━┿━━━━━━━┿━━━━━━━┿━━━━┿━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╸
     1 │       │       │    │def main_func():                                                                               
     2 │       │       │    │    import random                                                                              
     3 │   45% │       │    │    arr1 = [random.randint(1,10) for i in range(100000)]                                       
     4 │   46% │       │    │    arr2 = [random.randint(1,10) for i in range(100000)]                                       
     5 │    9% │       │    │    arr3 = [arr1[i]+arr2[i] for i in range(100000)]                                            
     6 │       │       │    │    tot = sum(arr3)                                                                            
     7 │       │       │    │    print(tot)                                                                                 
     8 │       │       │    │                                                                                               
     9 │       │       │    │if __name__ == "__main__":                                                                     
    10 │       │       │    │    main_func()                                                                                
       ╵       ╵       ╵    ╵

Example 5: Measure CPU Time Excluding Time Spent in I/O Blocking

The scalene provides us with the option --use-virtual-time which lets profiling result only report time spent in actually utilizing CPU excluding time which was spent in I/O or blocking (waiting for input from the network, other thread, etc).

In [30]:
!scalene --use-virtual-time profiling_examples/scalene_ex1.py
1099895
                                      Memory usage: ▂▃▄▆▆▅▅▆▇██████▇▇▇▇▆▇▇██ (max:  17.00MB)
                              profiling_examples/scalene_ex1.py: % of time = 100.00% out of   0.61s.
       ╷       ╷        ╷    ╷       ╷      ╷              ╷       ╷
  Line Time % Time %  Sys Mem %  Net   Memory usage  Copy                                                                Python native  %   Python (MB)  over time / % (MB/s) profiling_examples/scalene_ex1.py                            
╺━━━━━━┿━━━━━━━┿━━━━━━━━┿━━━━┿━━━━━━━┿━━━━━━┿━━━━━━━━━━━━━━┿━━━━━━━┿━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╸
     1 │       │        │    │       │      │              │       │def main_func():                                           
     2 │       │        │    │       │      │              │       │    import random                                          
     3 │   29%    19% │    │  100% │    4 │▁▁▁▁▁▁▁▁      │    27 │    arr1 = [random.randint(1,10) for i in range(100000)]   
     4 │   29%    16% │    │  100% │    6 │▁▁▁▂▂▂        │    10 │    arr2 = [random.randint(1,10) for i in range(100000)]   
     5 │    4% │     3% │    │  100% │    6 │▃▃▃▄          │    10 │    arr3 = [arr1[i]+arr2[i] for i in range(100000)]        
     6 │       │        │    │       │      │              │       │    tot = sum(arr3)                                        
     7 │       │        │    │       │      │              │       │    print(tot)                                             
     8 │       │        │    │       │      │              │       │                                                           
     9 │       │        │    │       │      │              │       │if __name__ == "__main__":                                 
    10 │       │        │    │       │      │              │       │    main_func()                                            
       ╵       ╵        ╵    ╵       ╵      ╵              ╵       ╵

Example 6: Report Only Profiles Where CPU % is Greater Than Some Limit

The scalene also lets us reporting only lines where profiling has used CPU above some % threshold. It lets us do that using the --cpu-percent-threshold option. We need to provide an integer followed by this option to set it as the threshold.

We have written another script for this example. The script simply loops 10000 times, generates two random numbers, sum them and add it to the list. We have then executed a script with a different threshold %.

CODE

import random

def add(a, b):
    return a+b


def get_sum_of_list():
    final_list = []
    for i in range(10000):
        rand1 = random.randint(1,100)
        rand2 = random.randint(1,100)
        out = add(rand1, rand2)
        final_list.append(out)
    return final_list


if __name__ == "__main__":
    l = get_sum_of_list()
#    print(l)
In [33]:
!scalene --cpu-percent-threshold 10 profiling_examples/example_2.py
                                                 Memory usage:  (max:   1.00MB)
                              profiling_examples/example_2.py: % of time = 100.00% out of   0.07s.
       ╷       ╷       ╷    ╷       ╷      ╷              ╷       ╷
  Line Time % Time % Sys Mem %  Net   Memory usage  Copy                                                                Python native %   Python (MB)  over time / % (MB/s) profiling_examples/example_2.py                              
╺━━━━━━┿━━━━━━━┿━━━━━━━┿━━━━┿━━━━━━━┿━━━━━━┿━━━━━━━━━━━━━━┿━━━━━━━┿━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╸
     1 │       │       │    │       │      │              │       │import random                                              
     2 │       │       │    │       │      │              │       │                                                           
     3 │       │       │    │       │      │              │       │def add(a, b):                                             
     4 │       │       │    │       │      │              │       │    return a+b                                             
     5 │       │       │    │       │      │              │       │                                                           
     6 │       │       │    │       │      │              │       │                                                           
     7 │       │       │    │       │      │              │       │def get_sum_of_list():                                     
     8 │       │       │    │       │      │              │       │    final_list = []                                        
     9 │       │       │    │       │      │              │       │    for i in range(10000):                                 
    10 │   35% │       │    │       │      │              │       │        rand1 = random.randint(1,100)                      
    11 │   16% │       │    │       │      │              │       │        rand2 = random.randint(1,100)                      
    12 │       │       │    │       │      │              │       │        out = add(rand1, rand2)                            
    13 │   48% │       │    │  100% │    1 │█100%         │   172 │        final_list.append(out)                             
    14 │       │       │    │       │      │              │       │    return final_list                                      
    15 │       │       │    │       │      │              │       │                                                           
    16 │       │       │    │       │      │              │       │                                                           
    17 │       │       │    │       │      │              │       │if __name__ == "__main__":                                 
    18 │       │       │    │       │      │              │       │    l = get_sum_of_list()                                  
    19 │       │       │    │       │      │              │       │#    print(l)                                              
    20 │       │       │    │       │      │              │       │                                                           
       ╵       ╵       ╵    ╵       ╵      ╵              ╵       ╵
In [34]:
!scalene --cpu-percent-threshold 50 profiling_examples/example_2.py
                                                Memory usage: ▅█ (max:   2.00MB)
                              profiling_examples/example_2.py: % of time = 100.00% out of   0.07s.
       ╷       ╷       ╷    ╷       ╷      ╷              ╷       ╷
  Line Time % Time % Sys Mem %  Net   Memory usage  Copy                                                                Python native %   Python (MB)  over time / % (MB/s) profiling_examples/example_2.py                              
╺━━━━━━┿━━━━━━━┿━━━━━━━┿━━━━┿━━━━━━━┿━━━━━━┿━━━━━━━━━━━━━━┿━━━━━━━┿━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╸
     1 │       │       │    │       │      │              │       │import random                                              
     2 │       │       │    │       │      │              │       │                                                           
     3 │       │       │    │       │      │              │       │def add(a, b):                                             
     4 │       │       │    │       │      │              │       │    return a+b                                             
     5 │       │       │    │       │      │              │       │                                                           
     6 │       │       │    │       │      │              │       │                                                           
     7 │       │       │    │       │      │              │       │def get_sum_of_list():                                     
     8 │       │       │    │       │      │              │       │    final_list = []                                        
     9 │       │       │    │       │      │              │       │    for i in range(10000):                                 
    10 │       │       │    │  100% │    1 │▅ 50%         │       │        rand1 = random.randint(1,100)                      
    11 │   41% │       │    │       │      │              │       │        rand2 = random.randint(1,100)                      
    12 │       │       │    │       │      │              │       │        out = add(rand1, rand2)                            
    13 │   59% │       │    │  100% │    1 │▃ 50%         │   178 │        final_list.append(out)                             
    14 │       │       │    │       │      │              │       │    return final_list                                      
    15 │       │       │    │       │      │              │       │                                                           
    16 │       │       │    │       │      │              │       │                                                           
    17 │       │       │    │       │      │              │       │if __name__ == "__main__":                                 
    18 │       │       │    │       │      │              │       │    l = get_sum_of_list()                                  
    19 │       │       │    │       │      │              │       │#    print(l)                                              
    20 │       │       │    │       │      │              │       │                                                           
       ╵       ╵       ╵    ╵       ╵      ╵              ╵       ╵
In [35]:
!scalene --cpu-percent-threshold 75 profiling_examples/example_2.py
                                                Memory usage: ▃▆█ (max:   3.00MB)
                              profiling_examples/example_2.py: % of time = 100.00% out of   0.07s.
       ╷       ╷       ╷    ╷       ╷      ╷              ╷       ╷
  Line Time % Time % Sys Mem %  Net   Memory usage  Copy                                                                Python native %   Python (MB)  over time / % (MB/s) profiling_examples/example_2.py                              
╺━━━━━━┿━━━━━━━┿━━━━━━━┿━━━━┿━━━━━━━┿━━━━━━┿━━━━━━━━━━━━━━┿━━━━━━━┿━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╸
     1 │       │       │    │       │      │              │       │import random                                              
     2 │       │       │    │       │      │              │       │                                                           
     3 │       │       │    │       │      │              │       │def add(a, b):                                             
     4 │       │       │    │       │      │              │       │    return a+b                                             
     5 │       │       │    │       │      │              │       │                                                           
     6 │       │       │    │       │      │              │       │                                                           
     7 │       │       │    │       │      │              │       │def get_sum_of_list():                                     
     8 │       │       │    │       │      │              │       │    final_list = []                                        
     9 │       │       │    │       │      │              │       │    for i in range(10000):                                 
    10 │   46% │       │    │       │      │              │       │        rand1 = random.randint(1,100)                      
    11 │       │       │    │       │      │              │       │        rand2 = random.randint(1,100)                      
    12 │       │       │    │       │      │              │       │        out = add(rand1, rand2)                            
    13 │   54% │       │    │  100% │    3 │▃▆█100%       │   174 │        final_list.append(out)                             
    14 │       │       │    │       │      │              │       │    return final_list                                      
    15 │       │       │    │       │      │              │       │                                                           
    16 │       │       │    │       │      │              │       │                                                           
    17 │       │       │    │       │      │              │       │if __name__ == "__main__":                                 
    18 │       │       │    │       │      │              │       │    l = get_sum_of_list()                                  
    19 │       │       │    │       │      │              │       │#    print(l)                                              
    20 │       │       │    │       │      │              │       │                                                           
       ╵       ╵       ╵    ╵       ╵      ╵              ╵       ╵

Example 7: Change Default CPU Sampling Rate (Default Every 0.01 seconds)

As a part of our seventh example, we'll explain how we can inform to sample for CPU and memory usage at different intervals than the default of 0.01 second. The scalene provides us with an option named --cpu-sampling-rate to do this. We can provide integer/float after this option to instruct scalene to sample for usage at that many second’s intervals.

We have written another script for explaining this option. Our script has three functions that generate 10000 random numbers and finds their average. All three methods take a different amount of time which we have introduced artificially to make them look taking the different times. We are then calling all three methods in our main function to generate an average of 10000 random numbers.

CODE

import time
import random

def very_slow_random_generator():
    time.sleep(5)
    arr = [random.randint(1,100) for i in range(100000)]
    return sum(arr) / len(arr)

def slow_random_generator():
    time.sleep(2)
    arr = [random.randint(1,100) for i in range(100000)]
    return sum(arr) / len(arr)

def fast_random_generator():
    time.sleep(1)
    arr = [random.randint(1,100) for i in range(100000)]
    return sum(arr) / len(arr)

def main_func():
    result = fast_random_generator()
    print(result)

    result = slow_random_generator()
    print(result)

    result = very_slow_random_generator()
    print(result)

main_func()
In [7]:
!scalene --cpu-sampling-rate 0.001 profiling_examples/average.py
50.38552
50.47872
50.60303
                                           Memory usage: ▁▃▂▅▅▇█▇▇▇▇▇▆ (max:  13.00MB)
                               profiling_examples/addition.py: % of time = 100.00% out of   8.93s.
       ╷       ╷       ╷     ╷       ╷      ╷              ╷       ╷
  Line Time % Time % Sys  Mem %  Net   Memory usage  Copy                                                                Python native %    Python (MB)  over time / % (MB/s) profiling_examples/addition.py                               
╺━━━━━━┿━━━━━━━┿━━━━━━━┿━━━━━┿━━━━━━━┿━━━━━━┿━━━━━━━━━━━━━━┿━━━━━━━┿━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╸
     1 │       │       │     │       │      │              │       │import time                                                
     2 │       │       │     │       │      │              │       │import random                                              
     3 │       │       │     │       │      │              │       │                                                           
     4 │       │       │     │       │      │              │       │def very_slow_random_generator():                          
     5 │   57% │       │ 52% │  100% │    1 │             │       │    time.sleep(5)                                          
     6 │    3% │       │  0% │  100% │    0 │▂▂▂▂▂▂▂▂▂     │     1 │    arr = [random.randint(1,100) for i in range(100000)]   
     7 │       │       │     │       │      │              │       │    return sum(arr) / len(arr)                             
     8 │       │       │     │       │      │              │       │                                                           
     9 │       │       │     │       │      │              │       │def slow_random_generator():                               
    10 │   24% │       │ 22% │       │      │              │       │    time.sleep(2)                                          
    11 │    2% │       │  0% │  100% │    5 │▁▂▂▂▃         │     1 │    arr = [random.randint(1,100) for i in range(100000)]   
    12 │       │       │     │       │      │              │       │    return sum(arr) / len(arr)                             
    13 │       │       │     │       │      │              │       │                                                           
    14 │       │       │     │       │      │              │       │def fast_random_generator():                               
    15 │   12% │       │ 11% │       │      │              │       │    time.sleep(1)                                          
    16 │    2% │       │  0% │  100% │    4 │▁▁▂▁          │     2 │    arr = [random.randint(1,100) for i in range(100000)]   
    17 │       │       │     │       │      │              │       │    return sum(arr) / len(arr)                             
    18 │       │       │     │       │      │              │       │                                                           
    19 │       │       │     │       │      │              │       │def main_func():                                           
    20 │       │       │     │       │      │              │       │    result = fast_random_generator()                       
    21 │       │       │     │       │      │              │       │    print(result)                                          
    22 │       │       │     │       │      │              │       │                                                           
    23 │       │       │     │       │      │              │       │    result = slow_random_generator()                       
    24 │       │       │     │       │      │              │       │    print(result)                                          
    25 │       │       │     │       │      │              │       │                                                           
    26 │       │       │     │       │      │              │       │    result = very_slow_random_generator()                  
    27 │       │       │     │       │      │              │       │    print(result)                                          
    28 │       │       │     │       │      │              │       │                                                           
    29 │       │       │     │       │      │              │       │main_func()                                                
       ╵       ╵       ╵     ╵       ╵      ╵              ╵       ╵

Example 8: Report Profiles Above Minimum Allocations

The scalene also provides us with an option named --malloc-threshold which lets us report profiles where at least minimum number of memory allocations have happened. We need to pass integer followed by --malloc-threshold option and scalene will profile the only line where minimum that many numbers of memory allocations have happened.

In [6]:
!scalene --malloc-threshold 5 profiling_examples/average.py
50.56064
50.68286
50.54243
                                        Memory usage: ▁▂▂▂▄▅▆▇██▇▇▇▇▇███▇▇ (max:  13.00MB)
                               profiling_examples/addition.py: % of time = 100.00% out of   8.76s.
       ╷       ╷       ╷     ╷       ╷      ╷              ╷       ╷
  Line Time % Time % Sys  Mem %  Net   Memory usage  Copy                                                                Python native %    Python (MB)  over time / % (MB/s) profiling_examples/addition.py                               
╺━━━━━━┿━━━━━━━┿━━━━━━━┿━━━━━┿━━━━━━━┿━━━━━━┿━━━━━━━━━━━━━━┿━━━━━━━┿━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╸
     1 │       │       │     │       │      │              │       │import time                                                
     2 │       │       │     │       │      │              │       │import random                                              
     3 │       │       │     │       │      │              │       │                                                           
     4 │       │       │     │       │      │              │       │def very_slow_random_generator():                          
     5 │   59% │       │ 59% │       │      │              │       │    time.sleep(5)                                          
     6 │    2% │       │     │  100% │    1 │▂▂▃▂▂         │     1 │    arr = [random.randint(1,100) for i in range(100000)]   
     7 │       │       │     │       │      │              │       │    return sum(arr) / len(arr)                             
     8 │       │       │     │       │      │              │       │                                                           
     9 │       │       │     │       │      │              │       │def slow_random_generator():                               
    10 │   24% │       │ 23% │       │      │              │       │    time.sleep(2)                                          
    11 │    2% │       │     │  100% │    8 │▁▂▂▂▃▃        │     1 │    arr = [random.randint(1,100) for i in range(100000)]   
    12 │       │       │     │       │      │              │       │    return sum(arr) / len(arr)                             
    13 │       │       │     │       │      │              │       │                                                           
    14 │       │       │     │       │      │              │       │def fast_random_generator():                               
    15 │   12% │       │ 12% │       │      │              │       │    time.sleep(1)                                          
    16 │    2% │       │     │  100% │    2 │▁▁▁▁▁▁▁▁      │     2 │    arr = [random.randint(1,100) for i in range(100000)]   
    17 │       │       │     │       │      │              │       │    return sum(arr) / len(arr)                             
    18 │       │       │     │       │      │              │       │                                                           
    19 │       │       │     │       │      │              │       │def main_func():                                           
    20 │       │       │     │       │      │              │       │    result = fast_random_generator()                       
    21 │       │       │     │       │      │              │       │    print(result)                                          
    22 │       │       │     │       │      │              │       │                                                           
    23 │       │       │     │       │      │              │       │    result = slow_random_generator()                       
    24 │       │       │     │       │      │              │       │    print(result)                                          
    25 │       │       │     │       │      │              │       │                                                           
    26 │       │       │     │       │      │              │       │    result = very_slow_random_generator()                  
    27 │       │       │     │       │      │              │       │    print(result)                                          
    28 │       │       │     │       │      │              │       │                                                           
    29 │       │       │     │       │      │              │       │main_func()                                                
       ╵       ╵       ╵     ╵       ╵      ╵              ╵       ╵

Example 9: Profiling At Constant Interval

The --profile-interval option instructs scalene to output profiling info at an interval in seconds specified by an integer followed by this option.

In [4]:
!scalene --profile-interval 1 profiling_examples/average.py
50.46675
50.46608
50.55942
                                        Memory usage: ▂▅▃▄▅▄▂▃▅▆▇▆▇▇███▇█ (max:  12.00MB)
                               profiling_examples/addition.py: % of time = 100.00% out of   9.14s.
       ╷       ╷       ╷     ╷       ╷      ╷              ╷       ╷
  Line Time % Time % Sys  Mem %  Net   Memory usage  Copy                                                                Python native %    Python (MB)  over time / % (MB/s) profiling_examples/addition.py                               
╺━━━━━━┿━━━━━━━┿━━━━━━━┿━━━━━┿━━━━━━━┿━━━━━━┿━━━━━━━━━━━━━━┿━━━━━━━┿━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╸
     1 │       │       │     │       │      │              │       │import time                                                
     2 │       │       │     │       │      │              │       │import random                                              
     3 │       │       │     │       │      │              │       │                                                           
     4 │       │       │     │       │      │              │       │def very_slow_random_generator():                          
     5 │   59% │       │ 58% │       │      │              │       │    time.sleep(5)                                          
     6 │    2% │       │     │  100% │    5 │▂▃▄▃▄         │     1 │    arr = [random.randint(1,100) for i in range(100000)]   
     7 │       │       │     │       │      │              │       │    return sum(arr) / len(arr)                             
     8 │       │       │     │       │      │              │       │                                                           
     9 │       │       │     │       │      │              │       │def slow_random_generator():                               
    10 │   23% │       │ 23% │       │      │              │       │    time.sleep(2)                                          
    11 │    2% │       │     │  100% │    0 │▁▁▁▂▁         │     1 │    arr = [random.randint(1,100) for i in range(100000)]   
    12 │       │       │     │       │      │              │       │    return sum(arr) / len(arr)                             
    13 │       │       │     │       │      │              │       │                                                           
    14 │       │       │     │       │      │              │       │def fast_random_generator():                               
    15 │   12% │       │ 11% │       │      │              │       │    time.sleep(1)                                          
    16 │    2% │       │     │  100% │    7 │▁▁▁▁▂▂▂▂▂     │     2 │    arr = [random.randint(1,100) for i in range(100000)]   
    17 │       │       │     │       │      │              │       │    return sum(arr) / len(arr)                             
    18 │       │       │     │       │      │              │       │                                                           
    19 │       │       │     │       │      │              │       │def main_func():                                           
    20 │       │       │     │       │      │              │       │    result = fast_random_generator()                       
    21 │       │       │     │       │      │              │       │    print(result)                                          
    22 │       │       │     │       │      │              │       │                                                           
    23 │       │       │     │       │      │              │       │    result = slow_random_generator()                       
    24 │       │       │     │       │      │              │       │    print(result)                                          
    25 │       │       │     │       │      │              │       │                                                           
    26 │       │       │     │       │      │              │       │    result = very_slow_random_generator()                  
    27 │       │       │     │       │      │              │       │    print(result)                                          
    28 │       │       │     │       │      │              │       │                                                           
    29 │       │       │     │       │      │              │       │main_func()                                                
       ╵       ╵       ╵     ╵       ╵      ╵              ╵       ╵

Example 10: Profile All Python Code Which Got Executed Including All Imports

The default settings of scalene remove all other imported files which were used in the script which was profiled. Sometimes we need to check to the profile of all scripts involved in running a particular script and we can do that using the --profile-all option of scalene.

In [17]:
!scalene --profile-all profiling_examples/scalene_ex1.py
1098963
                                         Memory usage: ▂▃▄▃▄▅▇██▇▇▇▇▆▅▆ (max:  12.00MB)
                       /home/sunny/anaconda3/lib/python3.7/random.py: % of time =  60.11% out of   0.70s.
       ╷       ╷       ╷    ╷       ╷      ╷              ╷       ╷
  Line Time % Time % Sys Mem %  Net   Memory usage  Copy                                                                Python native %   Python (MB)  over time / % (MB/s) /home/sunny/anaconda3/lib/python3.7/random.py                
╺━━━━━━┿━━━━━━━┿━━━━━━━┿━━━━┿━━━━━━━┿━━━━━━┿━━━━━━━━━━━━━━┿━━━━━━━┿━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╸
     1 │       │       │    │       │      │              │       │"""Random variable generators.                             
     2 │       │       │    │       │      │              │       │                                                           
     3 │       │       │    │       │      │              │       │    integers                                               
     4 │       │       │    │       │      │              │       │    --------                                               
     5 │       │       │    │       │      │              │       │           uniform within range                            
     6 │       │       │    │       │      │              │       │                                                           
     7 │       │       │    │       │      │              │       │    sequences                                              
     8 │       │       │    │       │      │              │       │    ---------                                              
     9 │       │       │    │       │      │              │       │           pick random element                             
    10 │       │       │    │       │      │              │       │           pick random sample                              
    11 │       │       │    │       │      │              │       │           pick weighted random sample                     
    12 │       │       │    │       │      │              │       │           generate random permutation                     
    13 │       │       │    │       │      │              │       │                                                           
    14 │       │       │    │       │      │              │       │    distributions on the real line:                        
    15 │       │       │    │       │      │              │       │    ------------------------------                         
    16 │       │       │    │       │      │              │       │           uniform                                         
    17 │       │       │    │       │      │              │       │           triangular                                      
    18 │       │       │    │       │      │              │       │           normal (Gaussian)                               
    19 │       │       │    │       │      │              │       │           lognormal                                       
    20 │       │       │    │       │      │              │       │           negative exponential                            
    21 │       │       │    │       │      │              │       │           gamma                                           
    22 │       │       │    │       │      │              │       │           beta                                            
    23 │       │       │    │       │      │              │       │           pareto                                          
    24 │       │       │    │       │      │              │       │           Weibull                                         
    25 │       │       │    │       │      │              │       │                                                           
    26 │       │       │    │       │      │              │       │    distributions on the circle (angles 0 to 2pi)          
    27 │       │       │    │       │      │              │       │    ---------------------------------------------          
    28 │       │       │    │       │      │              │       │           circular uniform                                
    29 │       │       │    │       │      │              │       │           von Mises                                       
    30 │       │       │    │       │      │              │       │                                                           
    31 │       │       │    │       │      │              │       │General notes on the underlying Mersenne Twister core gene…
    32 │       │       │    │       │      │              │       │                                                           
    33 │       │       │    │       │      │              │       │* The period is 2**19937-1.                                
    34 │       │       │    │       │      │              │       │* It is one of the most extensively tested generators in e…
    35 │       │       │    │       │      │              │       │* The random() method is implemented in C, executes in a s…
    36 │       │       │    │       │      │              │       │  and is, therefore, threadsafe.                           
    37 │       │       │    │       │      │              │       │                                                           
    38 │       │       │    │       │      │              │       │"""                                                        
    39 │       │       │    │       │      │              │       │                                                           
    40 │       │       │    │       │      │              │       │from warnings import warn as _warn                         
    41 │       │       │    │       │      │              │       │from types import MethodType as _MethodType, BuiltinMethod…
    42 │       │       │    │       │      │              │       │from math import log as _log, exp as _exp, pi as _pi, e as
    43 │       │       │    │       │      │              │       │from math import sqrt as _sqrt, acos as _acos, cos as _cos
    44 │       │       │    │       │      │              │       │from os import urandom as _urandom                         
    45 │       │       │    │       │      │              │       │from _collections_abc import Set as _Set, Sequence as _Seq…
    46 │       │       │    │       │      │              │       │from hashlib import sha512 as _sha512                      
    47 │       │       │    │       │      │              │       │import itertools as _itertools                             
    48 │       │       │    │       │      │              │       │import bisect as _bisect                                   
    49 │       │       │    │       │      │              │       │import os as _os                                           
    50 │       │       │    │       │      │              │       │                                                           
    51 │       │       │    │       │      │              │       │__all__ = ["Random","seed","random","uniform","randint","c…
    52 │       │       │    │       │      │              │       │           "randrange","shuffle","normalvariate","lognormv…
    53 │       │       │    │       │      │              │       │           "expovariate","vonmisesvariate","gammavariate",
    54 │       │       │    │       │      │              │       │           "gauss","betavariate","paretovariate","weibullv…
    55 │       │       │    │       │      │              │       │           "getstate","setstate", "getrandbits", "choices",
    56 │       │       │    │       │      │              │       │           "SystemRandom"]                                 
    57 │       │       │    │       │      │              │       │                                                           
    58 │       │       │    │       │      │              │       │NV_MAGICCONST = 4 * _exp(-0.5)/_sqrt(2.0)                  
    59 │       │       │    │       │      │              │       │TWOPI = 2.0*_pi                                            
    60 │       │       │    │       │      │              │       │LOG4 = _log(4.0)                                           
    61 │       │       │    │       │      │              │       │SG_MAGICCONST = 1.0 + _log(4.5)                            
    62 │       │       │    │       │      │              │       │BPF = 53        # Number of bits in a float                
    63 │       │       │    │       │      │              │       │RECIP_BPF = 2**-BPF                                        
    64 │       │       │    │       │      │              │       │                                                           
    65 │       │       │    │       │      │              │       │                                                           
    66 │       │       │    │       │      │              │       │# Translated by Guido van Rossum from C source provided by 
    67 │       │       │    │       │      │              │       │# Adrian Baddeley.  Adapted by Raymond Hettinger for use w…
    68 │       │       │    │       │      │              │       │# the Mersenne Twister  and os.urandom() core generators.  
    69 │       │       │    │       │      │              │       │                                                           
    70 │       │       │    │       │      │              │       │import _random                                             
    71 │       │       │    │       │      │              │       │                                                           
    72 │       │       │    │       │      │              │       │class Random(_random.Random):                              
    73 │       │       │    │       │      │              │       │    """Random number generator base class used by bound mo…
    74 │       │       │    │       │      │              │       │                                                           
    75 │       │       │    │       │      │              │       │    Used to instantiate instances of Random to get generat…
    76 │       │       │    │       │      │              │       │    share state.                                           
    77 │       │       │    │       │      │              │       │                                                           
    78 │       │       │    │       │      │              │       │    Class Random can also be subclassed if you want to use
    79 │       │       │    │       │      │              │       │    generator of your own devising: in that case, override
    80 │       │       │    │       │      │              │       │    methods:  random(), seed(), getstate(), and setstate().
    81 │       │       │    │       │      │              │       │    Optionally, implement a getrandbits() method so that r…
    82 │       │       │    │       │      │              │       │    can cover arbitrarily large ranges.                    
    83 │       │       │    │       │      │              │       │                                                           
    84 │       │       │    │       │      │              │       │    """                                                    
    85 │       │       │    │       │      │              │       │                                                           
    86 │       │       │    │       │      │              │       │    VERSION = 3     # used by getstate/setstate            
    87 │       │       │    │       │      │              │       │                                                           
    88 │       │       │    │       │      │              │       │    def __init__(self, x=None):                            
    89 │       │       │    │       │      │              │       │        """Initialize an instance.                         
    90 │       │       │    │       │      │              │       │                                                           
    91 │       │       │    │       │      │              │       │        Optional argument x controls seeding, as for Rando…
    92 │       │       │    │       │      │              │       │        """                                                
    93 │       │       │    │       │      │              │       │                                                           
    94 │       │       │    │       │      │              │       │        self.seed(x)                                       
    95 │       │       │    │       │      │              │       │        self.gauss_next = None                             
    96 │       │       │    │       │      │              │       │                                                           
    97 │       │       │    │       │      │              │       │    def seed(self, a=None, version=2):                     
    98 │       │       │    │       │      │              │       │        """Initialize internal state from hashable object. 
    99 │       │       │    │       │      │              │       │                                                           
   100 │       │       │    │       │      │              │       │        None or no argument seeds from current time or fro…
   101 │       │       │    │       │      │              │       │        system specific randomness source if available.    
   102 │       │       │    │       │      │              │       │                                                           
   103 │       │       │    │       │      │              │       │        If *a* is an int, all bits are used.               
   104 │       │       │    │       │      │              │       │                                                           
   105 │       │       │    │       │      │              │       │        For version 2 (the default), all of the bits are u…
   106 │       │       │    │       │      │              │       │        bytes, or bytearray.  For version 1 (provided for 
   107 │       │       │    │       │      │              │       │        sequences from older versions of Python), the algo…
   108 │       │       │    │       │      │              │       │        bytes generates a narrower range of seeds.         
   109 │       │       │    │       │      │              │       │                                                           
   110 │       │       │    │       │      │              │       │        """                                                
   111 │       │       │    │       │      │              │       │                                                           
   112 │       │       │    │       │      │              │       │        if version == 1 and isinstance(a, (str, bytes)):   
   113 │       │       │    │       │      │              │       │            a = a.decode('latin-1') if isinstance(a, bytes
   114 │       │       │    │       │      │              │       │            x = ord(a[0]) << 7 if a else 0                 
   115 │       │       │    │       │      │              │       │            for c in map(ord, a):                          
   116 │       │       │    │       │      │              │       │                x = ((1000003 * x) ^ c) & 0xFFFFFFFFFFFFFF…
   117 │       │       │    │       │      │              │       │            x ^= len(a)                                    
   118 │       │       │    │       │      │              │       │            a = -2 if x == -1 else x                       
   119 │       │       │    │       │      │              │       │                                                           
   120 │       │       │    │       │      │              │       │        if version == 2 and isinstance(a, (str, bytes, byt…
   121 │       │       │    │       │      │              │       │            if isinstance(a, str):                         
   122 │       │       │    │       │      │              │       │                a = a.encode()                             
   123 │       │       │    │       │      │              │       │            a += _sha512(a).digest()                       
   124 │       │       │    │       │      │              │       │            a = int.from_bytes(a, 'big')                   
   125 │       │       │    │       │      │              │       │                                                           
   126 │       │       │    │       │      │              │       │        super().seed(a)                                    
   127 │       │       │    │       │      │              │       │        self.gauss_next = None                             
   128 │       │       │    │       │      │              │       │                                                           
   129 │       │       │    │       │      │              │       │    def getstate(self):                                    
   130 │       │       │    │       │      │              │       │        """Return internal state; can be passed to setstat…
   131 │       │       │    │       │      │              │       │        return self.VERSION, super().getstate(), self.gaus…
   132 │       │       │    │       │      │              │       │                                                           
   133 │       │       │    │       │      │              │       │    def setstate(self, state):                             
   134 │       │       │    │       │      │              │       │        """Restore internal state from object returned by …
   135 │       │       │    │       │      │              │       │        version = state[0]                                 
   136 │       │       │    │       │      │              │       │        if version == 3:                                   
   137 │       │       │    │       │      │              │       │            version, internalstate, self.gauss_next = state
   138 │       │       │    │       │      │              │       │            super().setstate(internalstate)                
   139 │       │       │    │       │      │              │       │        elif version == 2:                                 
   140 │       │       │    │       │      │              │       │            version, internalstate, self.gauss_next = state
   141 │       │       │    │       │      │              │       │            # In version 2, the state was saved as signed …
   142 │       │       │    │       │      │              │       │            #   inconsistencies between 32/64-bit systems.…
   143 │       │       │    │       │      │              │       │            #   really unsigned 32-bit ints, so we convert…
   144 │       │       │    │       │      │              │       │            #   version 2 to positive longs for version 3. 
   145 │       │       │    │       │      │              │       │            try:                                           
   146 │       │       │    │       │      │              │       │                internalstate = tuple(x % (2**32) for x in
   147 │       │       │    │       │      │              │       │            except ValueError as e:                        
   148 │       │       │    │       │      │              │       │                raise TypeError from e                     
   149 │       │       │    │       │      │              │       │            super().setstate(internalstate)                
   150 │       │       │    │       │      │              │       │        else:                                              
   151 │       │       │    │       │      │              │       │            raise ValueError("state with version %s passed…
   152 │       │       │    │       │      │              │       │                             "Random.setstate() of version…
   153 │       │       │    │       │      │              │       │                             (version, self.VERSION))      
   154 │       │       │    │       │      │              │       │                                                           
   155 │       │       │    │       │      │              │       │## ---- Methods below this point do not need to be overrid…
   156 │       │       │    │       │      │              │       │## ---- subclassing for the purpose of using a different c…
   157 │       │       │    │       │      │              │       │                                                           
   158 │       │       │    │       │      │              │       │## -------------------- pickle support  -------------------
   159 │       │       │    │       │      │              │       │                                                           
   160 │       │       │    │       │      │              │       │    # Issue 17489: Since __reduce__ was defined to fix #75…
   161 │       │       │    │       │      │              │       │    # longer called; we leave it here because it has been …
   162 │       │       │    │       │      │              │       │    # rewritten back in 2001 and why risk breaking somethi…
   163 │       │       │    │       │      │              │       │    def __getstate__(self): # for pickle                   
   164 │       │       │    │       │      │              │       │        return self.getstate()                             
   165 │       │       │    │       │      │              │       │                                                           
   166 │       │       │    │       │      │              │       │    def __setstate__(self, state):  # for pickle           
   167 │       │       │    │       │      │              │       │        self.setstate(state)                               
   168 │       │       │    │       │      │              │       │                                                           
   169 │       │       │    │       │      │              │       │    def __reduce__(self):                                  
   170 │       │       │    │       │      │              │       │        return self.__class__, (), self.getstate()         
   171 │       │       │    │       │      │              │       │                                                           
   172 │       │       │    │       │      │              │       │## -------------------- integer methods  -----------------…
   173 │       │       │    │       │      │              │       │                                                           
   174 │       │       │    │       │      │              │       │    def randrange(self, start, stop=None, step=1, _int=int
   175 │       │       │    │       │      │              │       │        """Choose a random item from range(start, stop[, s…
   176 │       │       │    │       │      │              │       │                                                           
   177 │       │       │    │       │      │              │       │        This fixes the problem with randint() which includ…
   178 │       │       │    │       │      │              │       │        endpoint; in Python this is usually not what you w…
   179 │       │       │    │       │      │              │       │                                                           
   180 │       │       │    │       │      │              │       │        """                                                
   181 │       │       │    │       │      │              │       │                                                           
   182 │       │       │    │       │      │              │       │        # This code is a bit messy to make it fast for the 
   183 │       │       │    │       │      │              │       │        # common case while still doing adequate error che…
   184 │    4% │       │    │       │      │              │       │        istart = _int(start)                               
   185 │    2% │       │    │       │      │              │       │        if istart != start:                                
   186 │       │       │    │       │      │              │       │            raise ValueError("non-integer arg 1 for randra…
   187 │    5% │       │    │       │      │              │       │        if stop is None:                                   
   188 │       │       │    │       │      │              │       │            if istart > 0:                                 
   189 │       │       │    │       │      │              │       │                return self._randbelow(istart)             
   190 │       │       │    │       │      │              │       │            raise ValueError("empty range for randrange()")
   191 │       │       │    │       │      │              │       │                                                           
   192 │       │       │    │       │      │              │       │        # stop argument supplied.                          
   193 │    6% │       │    │  100% │    1 │▁             │       │        istop = _int(stop)                                 
   194 │    4% │       │    │       │      │              │       │        if istop != stop:                                  
   195 │       │       │    │       │      │              │       │            raise ValueError("non-integer stop for randran…
   196 │       │       │    │       │      │              │       │        width = istop - istart                             
   197 │    5% │       │    │       │      │              │       │        if step == 1 and width > 0:                        
   198 │   13% │       │    │       │      │              │       │            return istart + self._randbelow(width)         
   199 │       │       │    │       │      │              │       │        if step == 1:                                      
   200 │       │       │    │       │      │              │       │            raise ValueError("empty range for randrange() …
   201 │       │       │    │       │      │              │       │                                                           
   202 │       │       │    │       │      │              │       │        # Non-unit step argument supplied.                 
   203 │       │       │    │       │      │              │       │        istep = _int(step)                                 
   204 │       │       │    │       │      │              │       │        if istep != step:                                  
   205 │       │       │    │       │      │              │       │            raise ValueError("non-integer step for randran…
   206 │       │       │    │       │      │              │       │        if istep > 0:                                      
   207 │       │       │    │       │      │              │       │            n = (width + istep - 1) // istep               
   208 │       │       │    │       │      │              │       │        elif istep < 0:                                    
   209 │       │       │    │       │      │              │       │            n = (width + istep + 1) // istep               
   210 │       │       │    │       │      │              │       │        else:                                              
   211 │       │       │    │       │      │              │       │            raise ValueError("zero step for randrange()")  
   212 │       │       │    │       │      │              │       │                                                           
   213 │       │       │    │       │      │              │       │        if n <= 0:                                         
   214 │       │       │    │       │      │              │       │            raise ValueError("empty range for randrange()")
   215 │       │       │    │       │      │              │       │                                                           
   216 │       │       │    │       │      │              │       │        return istart + istep*self._randbelow(n)           
   217 │       │       │    │       │      │              │       │                                                           
   218 │    5% │       │    │       │      │              │       │    def randint(self, a, b):                               
   219 │       │       │    │       │      │              │       │        """Return random integer in range [a, b], includin…
   220 │       │       │    │       │      │              │       │        """                                                
   221 │       │       │    │       │      │              │       │                                                           
   222 │       │       │    │       │      │              │       │        return self.randrange(a, b+1)                      
   223 │       │       │    │       │      │              │       │                                                           
   224 │       │       │    │       │      │              │       │    def _randbelow(self, n, int=int, maxsize=1<<BPF, type=
   225 │       │       │    │       │      │              │       │                   Method=_MethodType, BuiltinMethod=_Buil…
   226 │       │       │    │       │      │              │       │        "Return a random int in the range [0,n).  Raises V…
   227 │       │       │    │       │      │              │       │                                                           
   228 │       │       │    │       │      │              │       │        random = self.random                               
   229 │    1% │       │    │       │      │              │       │        getrandbits = self.getrandbits                     
   230 │       │       │    │       │      │              │       │        # Only call self.getrandbits if the original rando…
   231 │       │       │    │       │      │              │       │        # has not been overridden or if a new getrandbits(…
   232 │       │       │    │       │      │              │       │        if type(random) is BuiltinMethod or type(getrandbi…
   233 │    4% │       │    │       │      │              │       │            k = n.bit_length()  # don't use (n-1) here bec…
   234 │    3% │       │    │       │      │              │       │            r = getrandbits(k)          # 0 <= r < 2**k    
   235 │    8% │       │    │       │      │              │       │            while r >= n:                                  
   236 │       │       │    │       │      │              │       │                r = getrandbits(k)                         
   237 │       │       │    │       │      │              │       │            return r                                       
   238 │       │       │    │       │      │              │       │        # There's an overridden random() method but no new…
   239 │       │       │    │       │      │              │       │        # so we can only use random() from here.           
   240 │       │       │    │       │      │              │       │        if n >= maxsize:                                   
   241 │       │       │    │       │      │              │       │            _warn("Underlying random() generator does not …
   242 │       │       │    │       │      │              │       │                "enough bits to choose from a population r…
   243 │       │       │    │       │      │              │       │                "To remove the range limitation, add a get…
   244 │       │       │    │       │      │              │       │            return int(random() * n)                       
   245 │       │       │    │       │      │              │       │        if n == 0:                                         
   246 │       │       │    │       │      │              │       │            raise ValueError("Boundary cannot be zero")    
   247 │       │       │    │       │      │              │       │        rem = maxsize % n                                  
   248 │       │       │    │       │      │              │       │        limit = (maxsize - rem) / maxsize   # int(limit * …
   249 │       │       │    │       │      │              │       │        r = random()                                       
   250 │       │       │    │       │      │              │       │        while r >= limit:                                  
   251 │       │       │    │       │      │              │       │            r = random()                                   
   252 │       │       │    │       │      │              │       │        return int(r*maxsize) % n                          
   253 │       │       │    │       │      │              │       │                                                           
   254 │       │       │    │       │      │              │       │## -------------------- sequence methods  ----------------…
   255 │       │       │    │       │      │              │       │                                                           
   256 │       │       │    │       │      │              │       │    def choice(self, seq):                                 
   257 │       │       │    │       │      │              │       │        """Choose a random element from a non-empty sequen…
   258 │       │       │    │       │      │              │       │        try:                                               
   259 │       │       │    │       │      │              │       │            i = self._randbelow(len(seq))                  
   260 │       │       │    │       │      │              │       │        except ValueError:                                 
   261 │       │       │    │       │      │              │       │            raise IndexError('Cannot choose from an empty …
   262 │       │       │    │       │      │              │       │        return seq[i]                                      
   263 │       │       │    │       │      │              │       │                                                           
   264 │       │       │    │       │      │              │       │    def shuffle(self, x, random=None):                     
   265 │       │       │    │       │      │              │       │        """Shuffle list x in place, and return None.       
   266 │       │       │    │       │      │              │       │                                                           
   267 │       │       │    │       │      │              │       │        Optional argument random is a 0-argument function 
   268 │       │       │    │       │      │              │       │        random float in [0.0, 1.0); if it is the default N…
   269 │       │       │    │       │      │              │       │        standard random.random will be used.               
   270 │       │       │    │       │      │              │       │                                                           
   271 │       │       │    │       │      │              │       │        """                                                
   272 │       │       │    │       │      │              │       │                                                           
   273 │       │       │    │       │      │              │       │        if random is None:                                 
   274 │       │       │    │       │      │              │       │            randbelow = self._randbelow                    
   275 │       │       │    │       │      │              │       │            for i in reversed(range(1, len(x))):           
   276 │       │       │    │       │      │              │       │                # pick an element in x[:i+1] with which to…
   277 │       │       │    │       │      │              │       │                j = randbelow(i+1)                         
   278 │       │       │    │       │      │              │       │                x[i], x[j] = x[j], x[i]                    
   279 │       │       │    │       │      │              │       │        else:                                              
   280 │       │       │    │       │      │              │       │            _int = int                                     
   281 │       │       │    │       │      │              │       │            for i in reversed(range(1, len(x))):           
   282 │       │       │    │       │      │              │       │                # pick an element in x[:i+1] with which to…
   283 │       │       │    │       │      │              │       │                j = _int(random() * (i+1))                 
   284 │       │       │    │       │      │              │       │                x[i], x[j] = x[j], x[i]                    
   285 │       │       │    │       │      │              │       │                                                           
   286 │       │       │    │       │      │              │       │    def sample(self, population, k):                       
   287 │       │       │    │       │      │              │       │        """Chooses k unique random elements from a populat…
   288 │       │       │    │       │      │              │       │                                                           
   289 │       │       │    │       │      │              │       │        Returns a new list containing elements from the po…
   290 │       │       │    │       │      │              │       │        leaving the original population unchanged.  The re…
   291 │       │       │    │       │      │              │       │        in selection order so that all sub-slices will als…
   292 │       │       │    │       │      │              │       │        samples.  This allows raffle winners (the sample) 
   293 │       │       │    │       │      │              │       │        into grand prize and second place winners (the sub…
   294 │       │       │    │       │      │              │       │                                                           
   295 │       │       │    │       │      │              │       │        Members of the population need not be hashable or 
   296 │       │       │    │       │      │              │       │        population contains repeats, then each occurrence 
   297 │       │       │    │       │      │              │       │        selection in the sample.                           
   298 │       │       │    │       │      │              │       │                                                           
   299 │       │       │    │       │      │              │       │        To choose a sample in a range of integers, use ran…
   300 │       │       │    │       │      │              │       │        This is especially fast and space efficient for sa…
   301 │       │       │    │       │      │              │       │        large population:   sample(range(10000000), 60)    
   302 │       │       │    │       │      │              │       │        """                                                
   303 │       │       │    │       │      │              │       │                                                           
   304 │       │       │    │       │      │              │       │        # Sampling without replacement entails tracking ei…
   305 │       │       │    │       │      │              │       │        # selections (the pool) in a list or previous sele…
   306 │       │       │    │       │      │              │       │                                                           
   307 │       │       │    │       │      │              │       │        # When the number of selections is small compared …
   308 │       │       │    │       │      │              │       │        # population, then tracking selections is efficien…
   309 │       │       │    │       │      │              │       │        # only a small set and an occasional reselection. …
   310 │       │       │    │       │      │              │       │        # a larger number of selections, the pool tracking…
   311 │       │       │    │       │      │              │       │        # preferred since the list takes less space than t…
   312 │       │       │    │       │      │              │       │        # set and it doesn't suffer from frequent reselect…
   313 │       │       │    │       │      │              │       │                                                           
   314 │       │       │    │       │      │              │       │        if isinstance(population, _Set):                   
   315 │       │       │    │       │      │              │       │            population = tuple(population)                 
   316 │       │       │    │       │      │              │       │        if not isinstance(population, _Sequence):          
   317 │       │       │    │       │      │              │       │            raise TypeError("Population must be a sequence…
   318 │       │       │    │       │      │              │       │        randbelow = self._randbelow                        
   319 │       │       │    │       │      │              │       │        n = len(population)                                
   320 │       │       │    │       │      │              │       │        if not 0 <= k <= n:                                
   321 │       │       │    │       │      │              │       │            raise ValueError("Sample larger than populatio…
   322 │       │       │    │       │      │              │       │        result = [None] * k                                
   323 │       │       │    │       │      │              │       │        setsize = 21        # size of a small set minus si…
   324 │       │       │    │       │      │              │       │        if k > 5:                                          
   325 │       │       │    │       │      │              │       │            setsize += 4 ** _ceil(_log(k * 3, 4)) # table …
   326 │       │       │    │       │      │              │       │        if n <= setsize:                                   
   327 │       │       │    │       │      │              │       │            # An n-length list is smaller than a k-length …
   328 │       │       │    │       │      │              │       │            pool = list(population)                        
   329 │       │       │    │       │      │              │       │            for i in range(k):         # invariant:  non-s…
   330 │       │       │    │       │      │              │       │                j = randbelow(n-i)                         
   331 │       │       │    │       │      │              │       │                result[i] = pool[j]                        
   332 │       │       │    │       │      │              │       │                pool[j] = pool[n-i-1]   # move non-selecte…
   333 │       │       │    │       │      │              │       │        else:                                              
   334 │       │       │    │       │      │              │       │            selected = set()                               
   335 │       │       │    │       │      │              │       │            selected_add = selected.add                    
   336 │       │       │    │       │      │              │       │            for i in range(k):                             
   337 │       │       │    │       │      │              │       │                j = randbelow(n)                           
   338 │       │       │    │       │      │              │       │                while j in selected:                       
   339 │       │       │    │       │      │              │       │                    j = randbelow(n)                       
   340 │       │       │    │       │      │              │       │                selected_add(j)                            
   341 │       │       │    │       │      │              │       │                result[i] = population[j]                  
   342 │       │       │    │       │      │              │       │        return result                                      
   343 │       │       │    │       │      │              │       │                                                           
   344 │       │       │    │       │      │              │       │    def choices(self, population, weights=None, *, cum_wei…
   345 │       │       │    │       │      │              │       │        """Return a k sized list of population elements ch…
   346 │       │       │    │       │      │              │       │                                                           
   347 │       │       │    │       │      │              │       │        If the relative weights or cumulative weights are 
   348 │       │       │    │       │      │              │       │        the selections are made with equal probability.    
   349 │       │       │    │       │      │              │       │                                                           
   350 │       │       │    │       │      │              │       │        """                                                
   351 │       │       │    │       │      │              │       │        random = self.random                               
   352 │       │       │    │       │      │              │       │        if cum_weights is None:                            
   353 │       │       │    │       │      │              │       │            if weights is None:                            
   354 │       │       │    │       │      │              │       │                _int = int                                 
   355 │       │       │    │       │      │              │       │                total = len(population)                    
   356 │       │       │    │       │      │              │       │                return [population[_int(random() * total)]
   357 │       │       │    │       │      │              │       │            cum_weights = list(_itertools.accumulate(weigh…
   358 │       │       │    │       │      │              │       │        elif weights is not None:                          
   359 │       │       │    │       │      │              │       │            raise TypeError('Cannot specify both weights a…
   360 │       │       │    │       │      │              │       │        if len(cum_weights) != len(population):            
   361 │       │       │    │       │      │              │       │            raise ValueError('The number of weights does n…
   362 │       │       │    │       │      │              │       │        bisect = _bisect.bisect                            
   363 │       │       │    │       │      │              │       │        total = cum_weights[-1]                            
   364 │       │       │    │       │      │              │       │        hi = len(cum_weights) - 1                          
   365 │       │       │    │       │      │              │       │        return [population[bisect(cum_weights, random() * 
   366 │       │       │    │       │      │              │       │                for i in range(k)]                         
   367 │       │       │    │       │      │              │       │                                                           
   368 │       │       │    │       │      │              │       │## -------------------- real-valued distributions  -------…
   369 │       │       │    │       │      │              │       │                                                           
   370 │       │       │    │       │      │              │       │## -------------------- uniform distribution -------------…
   371 │       │       │    │       │      │              │       │                                                           
   372 │       │       │    │       │      │              │       │    def uniform(self, a, b):                               
   373 │       │       │    │       │      │              │       │        "Get a random number in the range [a, b) or [a, b]…
   374 │       │       │    │       │      │              │       │        return a + (b-a) * self.random()                   
   375 │       │       │    │       │      │              │       │                                                           
   376 │       │       │    │       │      │              │       │## -------------------- triangular --------------------    
   377 │       │       │    │       │      │              │       │                                                           
   378 │       │       │    │       │      │              │       │    def triangular(self, low=0.0, high=1.0, mode=None):    
   379 │       │       │    │       │      │              │       │        """Triangular distribution.                        
   380 │       │       │    │       │      │              │       │                                                           
   381 │       │       │    │       │      │              │       │        Continuous distribution bounded by given lower and
   382 │       │       │    │       │      │              │       │        and having a given mode value in-between.          
   383 │       │       │    │       │      │              │       │                                                           
   384 │       │       │    │       │      │              │       │        http://en.wikipedia.org/wiki/Triangular_distributi…
   385 │       │       │    │       │      │              │       │                                                           
   386 │       │       │    │       │      │              │       │        """                                                
   387 │       │       │    │       │      │              │       │        u = self.random()                                  
   388 │       │       │    │       │      │              │       │        try:                                               
   389 │       │       │    │       │      │              │       │            c = 0.5 if mode is None else (mode - low) / (h…
   390 │       │       │    │       │      │              │       │        except ZeroDivisionError:                          
   391 │       │       │    │       │      │              │       │            return low                                     
   392 │       │       │    │       │      │              │       │        if u > c:                                          
   393 │       │       │    │       │      │              │       │            u = 1.0 - u                                    
   394 │       │       │    │       │      │              │       │            c = 1.0 - c                                    
   395 │       │       │    │       │      │              │       │            low, high = high, low                          
   396 │       │       │    │       │      │              │       │        return low + (high - low) * _sqrt(u * c)           
   397 │       │       │    │       │      │              │       │                                                           
   398 │       │       │    │       │      │              │       │## -------------------- normal distribution --------------…
   399 │       │       │    │       │      │              │       │                                                           
   400 │       │       │    │       │      │              │       │    def normalvariate(self, mu, sigma):                    
   401 │       │       │    │       │      │              │       │        """Normal distribution.                            
   402 │       │       │    │       │      │              │       │                                                           
   403 │       │       │    │       │      │              │       │        mu is the mean, and sigma is the standard deviatio…
   404 │       │       │    │       │      │              │       │                                                           
   405 │       │       │    │       │      │              │       │        """                                                
   406 │       │       │    │       │      │              │       │        # mu = mean, sigma = standard deviation            
   407 │       │       │    │       │      │              │       │                                                           
   408 │       │       │    │       │      │              │       │        # Uses Kinderman and Monahan method. Reference: Ki…
   409 │       │       │    │       │      │              │       │        # A.J. and Monahan, J.F., "Computer generation of …
   410 │       │       │    │       │      │              │       │        # variables using the ratio of uniform deviates", …
   411 │       │       │    │       │      │              │       │        # Math Software, 3, (1977), pp257-260.             
   412 │       │       │    │       │      │              │       │                                                           
   413 │       │       │    │       │      │              │       │        random = self.random                               
   414 │       │       │    │       │      │              │       │        while 1:                                           
   415 │       │       │    │       │      │              │       │            u1 = random()                                  
   416 │       │       │    │       │      │              │       │            u2 = 1.0 - random()                            
   417 │       │       │    │       │      │              │       │            z = NV_MAGICCONST*(u1-0.5)/u2                  
   418 │       │       │    │       │      │              │       │            zz = z*z/4.0                                   
   419 │       │       │    │       │      │              │       │            if zz <= -_log(u2):                            
   420 │       │       │    │       │      │              │       │                break                                      
   421 │       │       │    │       │      │              │       │        return mu + z*sigma                                
   422 │       │       │    │       │      │              │       │                                                           
   423 │       │       │    │       │      │              │       │## -------------------- lognormal distribution -----------…
   424 │       │       │    │       │      │              │       │                                                           
   425 │       │       │    │       │      │              │       │    def lognormvariate(self, mu, sigma):                   
   426 │       │       │    │       │      │              │       │        """Log normal distribution.                        
   427 │       │       │    │       │      │              │       │                                                           
   428 │       │       │    │       │      │              │       │        If you take the natural logarithm of this distribu…
   429 │       │       │    │       │      │              │       │        normal distribution with mean mu and standard devi…
   430 │       │       │    │       │      │              │       │        mu can have any value, and sigma must be greater t…
   431 │       │       │    │       │      │              │       │                                                           
   432 │       │       │    │       │      │              │       │        """                                                
   433 │       │       │    │       │      │              │       │        return _exp(self.normalvariate(mu, sigma))         
   434 │       │       │    │       │      │              │       │                                                           
   435 │       │       │    │       │      │              │       │## -------------------- exponential distribution ---------…
   436 │       │       │    │       │      │              │       │                                                           
   437 │       │       │    │       │      │              │       │    def expovariate(self, lambd):                          
   438 │       │       │    │       │      │              │       │        """Exponential distribution.                       
   439 │       │       │    │       │      │              │       │                                                           
   440 │       │       │    │       │      │              │       │        lambd is 1.0 divided by the desired mean.  It shou…
   441 │       │       │    │       │      │              │       │        nonzero.  (The parameter would be called "lambda",
   442 │       │       │    │       │      │              │       │        a reserved word in Python.)  Returned values range
   443 │       │       │    │       │      │              │       │        positive infinity if lambd is positive, and from n…
   444 │       │       │    │       │      │              │       │        infinity to 0 if lambd is negative.                
   445 │       │       │    │       │      │              │       │                                                           
   446 │       │       │    │       │      │              │       │        """                                                
   447 │       │       │    │       │      │              │       │        # lambd: rate lambd = 1/mean                       
   448 │       │       │    │       │      │              │       │        # ('lambda' is a Python reserved word)             
   449 │       │       │    │       │      │              │       │                                                           
   450 │       │       │    │       │      │              │       │        # we use 1-random() instead of random() to preclud…
   451 │       │       │    │       │      │              │       │        # possibility of taking the log of zero.           
   452 │       │       │    │       │      │              │       │        return -_log(1.0 - self.random())/lambd            
   453 │       │       │    │       │      │              │       │                                                           
   454 │       │       │    │       │      │              │       │## -------------------- von Mises distribution -----------…
   455 │       │       │    │       │      │              │       │                                                           
   456 │       │       │    │       │      │              │       │    def vonmisesvariate(self, mu, kappa):                  
   457 │       │       │    │       │      │              │       │        """Circular data distribution.                     
   458 │       │       │    │       │      │              │       │                                                           
   459 │       │       │    │       │      │              │       │        mu is the mean angle, expressed in radians between
   460 │       │       │    │       │      │              │       │        kappa is the concentration parameter, which must b…
   461 │       │       │    │       │      │              │       │        equal to zero.  If kappa is equal to zero, this di…
   462 │       │       │    │       │      │              │       │        to a uniform random angle over the range 0 to 2*pi.
   463 │       │       │    │       │      │              │       │                                                           
   464 │       │       │    │       │      │              │       │        """                                                
   465 │       │       │    │       │      │              │       │        # mu:    mean angle (in radians between 0 and 2*pi)
   466 │       │       │    │       │      │              │       │        # kappa: concentration parameter kappa (>= 0)      
   467 │       │       │    │       │      │              │       │        # if kappa = 0 generate uniform random angle       
   468 │       │       │    │       │      │              │       │                                                           
   469 │       │       │    │       │      │              │       │        # Based upon an algorithm published in: Fisher, N.…
   470 │       │       │    │       │      │              │       │        # "Statistical Analysis of Circular Data", Cambrid…
   471 │       │       │    │       │      │              │       │        # University Press, 1993.                          
   472 │       │       │    │       │      │              │       │                                                           
   473 │       │       │    │       │      │              │       │        # Thanks to Magnus Kessler for a correction to the 
   474 │       │       │    │       │      │              │       │        # implementation of step 4.                        
   475 │       │       │    │       │      │              │       │                                                           
   476 │       │       │    │       │      │              │       │        random = self.random                               
   477 │       │       │    │       │      │              │       │        if kappa <= 1e-6:                                  
   478 │       │       │    │       │      │              │       │            return TWOPI * random()                        
   479 │       │       │    │       │      │              │       │                                                           
   480 │       │       │    │       │      │              │       │        s = 0.5 / kappa                                    
   481 │       │       │    │       │      │              │       │        r = s + _sqrt(1.0 + s * s)                         
   482 │       │       │    │       │      │              │       │                                                           
   483 │       │       │    │       │      │              │       │        while 1:                                           
   484 │       │       │    │       │      │              │       │            u1 = random()                                  
   485 │       │       │    │       │      │              │       │            z = _cos(_pi * u1)                             
   486 │       │       │    │       │      │              │       │                                                           
   487 │       │       │    │       │      │              │       │            d = z / (r + z)                                
   488 │       │       │    │       │      │              │       │            u2 = random()                                  
   489 │       │       │    │       │      │              │       │            if u2 < 1.0 - d * d or u2 <= (1.0 - d) * _exp(
   490 │       │       │    │       │      │              │       │                break                                      
   491 │       │       │    │       │      │              │       │                                                           
   492 │       │       │    │       │      │              │       │        q = 1.0 / r                                        
   493 │       │       │    │       │      │              │       │        f = (q + z) / (1.0 + q * z)                        
   494 │       │       │    │       │      │              │       │        u3 = random()                                      
   495 │       │       │    │       │      │              │       │        if u3 > 0.5:                                       
   496 │       │       │    │       │      │              │       │            theta = (mu + _acos(f)) % TWOPI                
   497 │       │       │    │       │      │              │       │        else:                                              
   498 │       │       │    │       │      │              │       │            theta = (mu - _acos(f)) % TWOPI                
   499 │       │       │    │       │      │              │       │                                                           
   500 │       │       │    │       │      │              │       │        return theta                                       
   501 │       │       │    │       │      │              │       │                                                           
   502 │       │       │    │       │      │              │       │## -------------------- gamma distribution ---------------…
   503 │       │       │    │       │      │              │       │                                                           
   504 │       │       │    │       │      │              │       │    def gammavariate(self, alpha, beta):                   
   505 │       │       │    │       │      │              │       │        """Gamma distribution.  Not the gamma function!    
   506 │       │       │    │       │      │              │       │                                                           
   507 │       │       │    │       │      │              │       │        Conditions on the parameters are alpha > 0 and bet…
   508 │       │       │    │       │      │              │       │                                                           
   509 │       │       │    │       │      │              │       │        The probability distribution function is:          
   510 │       │       │    │       │      │              │       │                                                           
   511 │       │       │    │       │      │              │       │                    x ** (alpha - 1) * math.exp(-x / beta) 
   512 │       │       │    │       │      │              │       │          pdf(x) =  -------------------------------------- 
   513 │       │       │    │       │      │              │       │                      math.gamma(alpha) * beta ** alpha    
   514 │       │       │    │       │      │              │       │                                                           
   515 │       │       │    │       │      │              │       │        """                                                
   516 │       │       │    │       │      │              │       │                                                           
   517 │       │       │    │       │      │              │       │        # alpha > 0, beta > 0, mean is alpha*beta, varianc…
   518 │       │       │    │       │      │              │       │                                                           
   519 │       │       │    │       │      │              │       │        # Warning: a few older sources define the gamma di…
   520 │       │       │    │       │      │              │       │        # of alpha > -1.0                                  
   521 │       │       │    │       │      │              │       │        if alpha <= 0.0 or beta <= 0.0:                    
   522 │       │       │    │       │      │              │       │            raise ValueError('gammavariate: alpha and beta…
   523 │       │       │    │       │      │              │       │                                                           
   524 │       │       │    │       │      │              │       │        random = self.random                               
   525 │       │       │    │       │      │              │       │        if alpha > 1.0:                                    
   526 │       │       │    │       │      │              │       │                                                           
   527 │       │       │    │       │      │              │       │            # Uses R.C.H. Cheng, "The generation of Gamma  
   528 │       │       │    │       │      │              │       │            # variables with non-integral shape parameters…
   529 │       │       │    │       │      │              │       │            # Applied Statistics, (1977), 26, No. 1, p71-74
   530 │       │       │    │       │      │              │       │                                                           
   531 │       │       │    │       │      │              │       │            ainv = _sqrt(2.0 * alpha - 1.0)                
   532 │       │       │    │       │      │              │       │            bbb = alpha - LOG4                             
   533 │       │       │    │       │      │              │       │            ccc = alpha + ainv                             
   534 │       │       │    │       │      │              │       │                                                           
   535 │       │       │    │       │      │              │       │            while 1:                                       
   536 │       │       │    │       │      │              │       │                u1 = random()                              
   537 │       │       │    │       │      │              │       │                if not 1e-7 < u1 < .9999999:               
   538 │       │       │    │       │      │              │       │                    continue                               
   539 │       │       │    │       │      │              │       │                u2 = 1.0 - random()                        
   540 │       │       │    │       │      │              │       │                v = _log(u1/(1.0-u1))/ainv                 
   541 │       │       │    │       │      │              │       │                x = alpha*_exp(v)                          
   542 │       │       │    │       │      │              │       │                z = u1*u1*u2                               
   543 │       │       │    │       │      │              │       │                r = bbb+ccc*v-x                            
   544 │       │       │    │       │      │              │       │                if r + SG_MAGICCONST - 4.5*z >= 0.0 or r >
   545 │       │       │    │       │      │              │       │                    return x * beta                        
   546 │       │       │    │       │      │              │       │                                                           
   547 │       │       │    │       │      │              │       │        elif alpha == 1.0:                                 
   548 │       │       │    │       │      │              │       │            # expovariate(1/beta)                          
   549 │       │       │    │       │      │              │       │            u = random()                                   
   550 │       │       │    │       │      │              │       │            while u <= 1e-7:                               
   551 │       │       │    │       │      │              │       │                u = random()                               
   552 │       │       │    │       │      │              │       │            return -_log(u) * beta                         
   553 │       │       │    │       │      │              │       │                                                           
   554 │       │       │    │       │      │              │       │        else:   # alpha is between 0 and 1 (exclusive)     
   555 │       │       │    │       │      │              │       │                                                           
   556 │       │       │    │       │      │              │       │            # Uses ALGORITHM GS of Statistical Computing -…
   557 │       │       │    │       │      │              │       │                                                           
   558 │       │       │    │       │      │              │       │            while 1:                                       
   559 │       │       │    │       │      │              │       │                u = random()                               
   560 │       │       │    │       │      │              │       │                b = (_e + alpha)/_e                        
   561 │       │       │    │       │      │              │       │                p = b*u                                    
   562 │       │       │    │       │      │              │       │                if p <= 1.0:                               
   563 │       │       │    │       │      │              │       │                    x = p ** (1.0/alpha)                   
   564 │       │       │    │       │      │              │       │                else:                                      
   565 │       │       │    │       │      │              │       │                    x = -_log((b-p)/alpha)                 
   566 │       │       │    │       │      │              │       │                u1 = random()                              
   567 │       │       │    │       │      │              │       │                if p > 1.0:                                
   568 │       │       │    │       │      │              │       │                    if u1 <= x ** (alpha - 1.0):           
   569 │       │       │    │       │      │              │       │                        break                              
   570 │       │       │    │       │      │              │       │                elif u1 <= _exp(-x):                       
   571 │       │       │    │       │      │              │       │                    break                                  
   572 │       │       │    │       │      │              │       │            return x * beta                                
   573 │       │       │    │       │      │              │       │                                                           
   574 │       │       │    │       │      │              │       │## -------------------- Gauss (faster alternative) -------…
   575 │       │       │    │       │      │              │       │                                                           
   576 │       │       │    │       │      │              │       │    def gauss(self, mu, sigma):                            
   577 │       │       │    │       │      │              │       │        """Gaussian distribution.                          
   578 │       │       │    │       │      │              │       │                                                           
   579 │       │       │    │       │      │              │       │        mu is the mean, and sigma is the standard deviatio…
   580 │       │       │    │       │      │              │       │        slightly faster than the normalvariate() function. 
   581 │       │       │    │       │      │              │       │                                                           
   582 │       │       │    │       │      │              │       │        Not thread-safe without a lock around calls.       
   583 │       │       │    │       │      │              │       │                                                           
   584 │       │       │    │       │      │              │       │        """                                                
   585 │       │       │    │       │      │              │       │                                                           
   586 │       │       │    │       │      │              │       │        # When x and y are two variables from [0, 1), unif…
   587 │       │       │    │       │      │              │       │        # distributed, then                                
   588 │       │       │    │       │      │              │       │        #                                                  
   589 │       │       │    │       │      │              │       │        #    cos(2*pi*x)*sqrt(-2*log(1-y))                 
   590 │       │       │    │       │      │              │       │        #    sin(2*pi*x)*sqrt(-2*log(1-y))                 
   591 │       │       │    │       │      │              │       │        #                                                  
   592 │       │       │    │       │      │              │       │        # are two *independent* variables with normal dist…
   593 │       │       │    │       │      │              │       │        # (mu = 0, sigma = 1).                             
   594 │       │       │    │       │      │              │       │        # (Lambert Meertens)                               
   595 │       │       │    │       │      │              │       │        # (corrected version; bug discovered by Mike Mille…
   596 │       │       │    │       │      │              │       │                                                           
   597 │       │       │    │       │      │              │       │        # Multithreading note: When two threads call this …
   598 │       │       │    │       │      │              │       │        # simultaneously, it is possible that they will re…
   599 │       │       │    │       │      │              │       │        # same return value.  The window is very small tho…
   600 │       │       │    │       │      │              │       │        # avoid this, you have to use a lock around all ca…
   601 │       │       │    │       │      │              │       │        # didn't want to slow this down in the serial case…
   602 │       │       │    │       │      │              │       │        # lock here.)                                      
   603 │       │       │    │       │      │              │       │                                                           
   604 │       │       │    │       │      │              │       │        random = self.random                               
   605 │       │       │    │       │      │              │       │        z = self.gauss_next                                
   606 │       │       │    │       │      │              │       │        self.gauss_next = None                             
   607 │       │       │    │       │      │              │       │        if z is None:                                      
   608 │       │       │    │       │      │              │       │            x2pi = random() * TWOPI                        
   609 │       │       │    │       │      │              │       │            g2rad = _sqrt(-2.0 * _log(1.0 - random()))     
   610 │       │       │    │       │      │              │       │            z = _cos(x2pi) * g2rad                         
   611 │       │       │    │       │      │              │       │            self.gauss_next = _sin(x2pi) * g2rad           
   612 │       │       │    │       │      │              │       │                                                           
   613 │       │       │    │       │      │              │       │        return mu + z*sigma                                
   614 │       │       │    │       │      │              │       │                                                           
   615 │       │       │    │       │      │              │       │## -------------------- beta --------------------          
   616 │       │       │    │       │      │              │       │## See                                                     
   617 │       │       │    │       │      │              │       │## http://mail.python.org/pipermail/python-bugs-list/2001-…
   618 │       │       │    │       │      │              │       │## for Ivan Frohne's insightful analysis of why the origin…
   619 │       │       │    │       │      │              │       │##                                                         
   620 │       │       │    │       │      │              │       │##    def betavariate(self, alpha, beta):                  
   621 │       │       │    │       │      │              │       │##        # Discrete Event Simulation in C, pp 87-88.      
   622 │       │       │    │       │      │              │       │##                                                         
   623 │       │       │    │       │      │              │       │##        y = self.expovariate(alpha)                      
   624 │       │       │    │       │      │              │       │##        z = self.expovariate(1.0/beta)                   
   625 │       │       │    │       │      │              │       │##        return z/(y+z)                                   
   626 │       │       │    │       │      │              │       │##                                                         
   627 │       │       │    │       │      │              │       │## was dead wrong, and how it probably got that way.       
   628 │       │       │    │       │      │              │       │                                                           
   629 │       │       │    │       │      │              │       │    def betavariate(self, alpha, beta):                    
   630 │       │       │    │       │      │              │       │        """Beta distribution.                              
   631 │       │       │    │       │      │              │       │                                                           
   632 │       │       │    │       │      │              │       │        Conditions on the parameters are alpha > 0 and bet…
   633 │       │       │    │       │      │              │       │        Returned values range between 0 and 1.             
   634 │       │       │    │       │      │              │       │                                                           
   635 │       │       │    │       │      │              │       │        """                                                
   636 │       │       │    │       │      │              │       │                                                           
   637 │       │       │    │       │      │              │       │        # This version due to Janne Sinkkonen, and matches…
   638 │       │       │    │       │      │              │       │        # texts (e.g., Knuth Vol 2 Ed 3 pg 134 "the beta d…
   639 │       │       │    │       │      │              │       │        y = self.gammavariate(alpha, 1.0)                  
   640 │       │       │    │       │      │              │       │        if y == 0:                                         
   641 │       │       │    │       │      │              │       │            return 0.0                                     
   642 │       │       │    │       │      │              │       │        else:                                              
   643 │       │       │    │       │      │              │       │            return y / (y + self.gammavariate(beta, 1.0))  
   644 │       │       │    │       │      │              │       │                                                           
   645 │       │       │    │       │      │              │       │## -------------------- Pareto --------------------        
   646 │       │       │    │       │      │              │       │                                                           
   647 │       │       │    │       │      │              │       │    def paretovariate(self, alpha):                        
   648 │       │       │    │       │      │              │       │        """Pareto distribution.  alpha is the shape parame…
   649 │       │       │    │       │      │              │       │        # Jain, pg. 495                                    
   650 │       │       │    │       │      │              │       │                                                           
   651 │       │       │    │       │      │              │       │        u = 1.0 - self.random()                            
   652 │       │       │    │       │      │              │       │        return 1.0 / u ** (1.0/alpha)                      
   653 │       │       │    │       │      │              │       │                                                           
   654 │       │       │    │       │      │              │       │## -------------------- Weibull --------------------       
   655 │       │       │    │       │      │              │       │                                                           
   656 │       │       │    │       │      │              │       │    def weibullvariate(self, alpha, beta):                 
   657 │       │       │    │       │      │              │       │        """Weibull distribution.                           
   658 │       │       │    │       │      │              │       │                                                           
   659 │       │       │    │       │      │              │       │        alpha is the scale parameter and beta is the shape
   660 │       │       │    │       │      │              │       │                                                           
   661 │       │       │    │       │      │              │       │        """                                                
   662 │       │       │    │       │      │              │       │        # Jain, pg. 499; bug fix courtesy Bill Arms        
   663 │       │       │    │       │      │              │       │                                                           
   664 │       │       │    │       │      │              │       │        u = 1.0 - self.random()                            
   665 │       │       │    │       │      │              │       │        return alpha * (-_log(u)) ** (1.0/beta)            
   666 │       │       │    │       │      │              │       │                                                           
   667 │       │       │    │       │      │              │       │## --------------- Operating System Random Source  -------…
   668 │       │       │    │       │      │              │       │                                                           
   669 │       │       │    │       │      │              │       │class SystemRandom(Random):                                
   670 │       │       │    │       │      │              │       │    """Alternate random number generator using sources pro…
   671 │       │       │    │       │      │              │       │    by the operating system (such as /dev/urandom on Unix 
   672 │       │       │    │       │      │              │       │    CryptGenRandom on Windows).                            
   673 │       │       │    │       │      │              │       │                                                           
   674 │       │       │    │       │      │              │       │     Not available on all systems (see os.urandom() for de…
   675 │       │       │    │       │      │              │       │    """                                                    
   676 │       │       │    │       │      │              │       │                                                           
   677 │       │       │    │       │      │              │       │    def random(self):                                      
   678 │       │       │    │       │      │              │       │        """Get the next random number in the range [0.0, 1…
   679 │       │       │    │       │      │              │       │        return (int.from_bytes(_urandom(7), 'big') >> 3) *
   680 │       │       │    │       │      │              │       │                                                           
   681 │       │       │    │       │      │              │       │    def getrandbits(self, k):                              
   682 │       │       │    │       │      │              │       │        """getrandbits(k) -> x.  Generates an int with k r…
   683 │       │       │    │       │      │              │       │        if k <= 0:                                         
   684 │       │       │    │       │      │              │       │            raise ValueError('number of bits must be great…
   685 │       │       │    │       │      │              │       │        if k != int(k):                                    
   686 │       │       │    │       │      │              │       │            raise TypeError('number of bits should be an i…
   687 │       │       │    │       │      │              │       │        numbytes = (k + 7) // 8                       # bi…
   688 │       │       │    │       │      │              │       │        x = int.from_bytes(_urandom(numbytes), 'big')      
   689 │       │       │    │       │      │              │       │        return x >> (numbytes * 8 - k)                # tr…
   690 │       │       │    │       │      │              │       │                                                           
   691 │       │       │    │       │      │              │       │    def seed(self, *args, **kwds):                         
   692 │       │       │    │       │      │              │       │        "Stub method.  Not used for a system random number…
   693 │       │       │    │       │      │              │       │        return None                                        
   694 │       │       │    │       │      │              │       │                                                           
   695 │       │       │    │       │      │              │       │    def _notimplemented(self, *args, **kwds):              
   696 │       │       │    │       │      │              │       │        "Method should not be called for a system random n…
   697 │       │       │    │       │      │              │       │        raise NotImplementedError('System entropy source d…
   698 │       │       │    │       │      │              │       │    getstate = setstate = _notimplemented                  
   699 │       │       │    │       │      │              │       │                                                           
   700 │       │       │    │       │      │              │       │## -------------------- test program --------------------  
   701 │       │       │    │       │      │              │       │                                                           
   702 │       │       │    │       │      │              │       │def _test_generator(n, func, args):                        
   703 │       │       │    │       │      │              │       │    import time                                            
   704 │       │       │    │       │      │              │       │    print(n, 'times', func.__name__)                       
   705 │       │       │    │       │      │              │       │    total = 0.0                                            
   706 │       │       │    │       │      │              │       │    sqsum = 0.0                                            
   707 │       │       │    │       │      │              │       │    smallest = 1e10                                        
   708 │       │       │    │       │      │              │       │    largest = -1e10                                        
   709 │       │       │    │       │      │              │       │    t0 = time.perf_counter()                               
   710 │       │       │    │       │      │              │       │    for i in range(n):                                     
   711 │       │       │    │       │      │              │       │        x = func(*args)                                    
   712 │       │       │    │       │      │              │       │        total += x                                         
   713 │       │       │    │       │      │              │       │        sqsum = sqsum + x*x                                
   714 │       │       │    │       │      │              │       │        smallest = min(x, smallest)                        
   715 │       │       │    │       │      │              │       │        largest = max(x, largest)                          
   716 │       │       │    │       │      │              │       │    t1 = time.perf_counter()                               
   717 │       │       │    │       │      │              │       │    print(round(t1-t0, 3), 'sec,', end=' ')                
   718 │       │       │    │       │      │              │       │    avg = total/n                                          
   719 │       │       │    │       │      │              │       │    stddev = _sqrt(sqsum/n - avg*avg)                      
   720 │       │       │    │       │      │              │       │    print('avg %g, stddev %g, min %g, max %g\n' % \        
   721 │       │       │    │       │      │              │       │              (avg, stddev, smallest, largest))            
   722 │       │       │    │       │      │              │       │                                                           
   723 │       │       │    │       │      │              │       │                                                           
   724 │       │       │    │       │      │              │       │def _test(N=2000):                                         
   725 │       │       │    │       │      │              │       │    _test_generator(N, random, ())                         
   726 │       │       │    │       │      │              │       │    _test_generator(N, normalvariate, (0.0, 1.0))          
   727 │       │       │    │       │      │              │       │    _test_generator(N, lognormvariate, (0.0, 1.0))         
   728 │       │       │    │       │      │              │       │    _test_generator(N, vonmisesvariate, (0.0, 1.0))        
   729 │       │       │    │       │      │              │       │    _test_generator(N, gammavariate, (0.01, 1.0))          
   730 │       │       │    │       │      │              │       │    _test_generator(N, gammavariate, (0.1, 1.0))           
   731 │       │       │    │       │      │              │       │    _test_generator(N, gammavariate, (0.1, 2.0))           
   732 │       │       │    │       │      │              │       │    _test_generator(N, gammavariate, (0.5, 1.0))           
   733 │       │       │    │       │      │              │       │    _test_generator(N, gammavariate, (0.9, 1.0))           
   734 │       │       │    │       │      │              │       │    _test_generator(N, gammavariate, (1.0, 1.0))           
   735 │       │       │    │       │      │              │       │    _test_generator(N, gammavariate, (2.0, 1.0))           
   736 │       │       │    │       │      │              │       │    _test_generator(N, gammavariate, (20.0, 1.0))          
   737 │       │       │    │       │      │              │       │    _test_generator(N, gammavariate, (200.0, 1.0))         
   738 │       │       │    │       │      │              │       │    _test_generator(N, gauss, (0.0, 1.0))                  
   739 │       │       │    │       │      │              │       │    _test_generator(N, betavariate, (3.0, 3.0))            
   740 │       │       │    │       │      │              │       │    _test_generator(N, triangular, (0.0, 1.0, 1.0/3.0))    
   741 │       │       │    │       │      │              │       │                                                           
   742 │       │       │    │       │      │              │       │# Create one instance, seeded from current time, and expor…
   743 │       │       │    │       │      │              │       │# as module-level functions.  The functions share state ac…
   744 │       │       │    │       │      │              │       │#(both in the user's code and in the Python libraries), bu…
   745 │       │       │    │       │      │              │       │# for most programs and is easier for the casual user than…
   746 │       │       │    │       │      │              │       │# instantiate their own Random() instance.                 
   747 │       │       │    │       │      │              │       │                                                           
   748 │       │       │    │       │      │              │       │_inst = Random()                                           
   749 │       │       │    │       │      │              │       │seed = _inst.seed                                          
   750 │       │       │    │       │      │              │       │random = _inst.random                                      
   751 │       │       │    │       │      │              │       │uniform = _inst.uniform                                    
   752 │       │       │    │       │      │              │       │triangular = _inst.triangular                              
   753 │       │       │    │       │      │              │       │randint = _inst.randint                                    
   754 │       │       │    │       │      │              │       │choice = _inst.choice                                      
   755 │       │       │    │       │      │              │       │randrange = _inst.randrange                                
   756 │       │       │    │       │      │              │       │sample = _inst.sample                                      
   757 │       │       │    │       │      │              │       │shuffle = _inst.shuffle                                    
   758 │       │       │    │       │      │              │       │choices = _inst.choices                                    
   759 │       │       │    │       │      │              │       │normalvariate = _inst.normalvariate                        
   760 │       │       │    │       │      │              │       │lognormvariate = _inst.lognormvariate                      
   761 │       │       │    │       │      │              │       │expovariate = _inst.expovariate                            
   762 │       │       │    │       │      │              │       │vonmisesvariate = _inst.vonmisesvariate                    
   763 │       │       │    │       │      │              │       │gammavariate = _inst.gammavariate                          
   764 │       │       │    │       │      │              │       │gauss = _inst.gauss                                        
   765 │       │       │    │       │      │              │       │betavariate = _inst.betavariate                            
   766 │       │       │    │       │      │              │       │paretovariate = _inst.paretovariate                        
   767 │       │       │    │       │      │              │       │weibullvariate = _inst.weibullvariate                      
   768 │       │       │    │       │      │              │       │getstate = _inst.getstate                                  
   769 │       │       │    │       │      │              │       │setstate = _inst.setstate                                  
   770 │       │       │    │       │      │              │       │getrandbits = _inst.getrandbits                            
   771 │       │       │    │       │      │              │       │                                                           
   772 │       │       │    │       │      │              │       │if hasattr(_os, "fork"):                                   
   773 │       │       │    │       │      │              │       │    _os.register_at_fork(after_in_child=_inst.seed)        
   774 │       │       │    │       │      │              │       │                                                           
   775 │       │       │    │       │      │              │       │                                                           
   776 │       │       │    │       │      │              │       │if __name__ == '__main__':                                 
   777 │       │       │    │       │      │              │       │    _test()                                                
       ╵       ╵       ╵    ╵       ╵      ╵              ╵       ╵
                             profiling_examples/scalene_ex1.py: % of time =  39.89% out of   0.70s.
       ╷       ╷       ╷    ╷       ╷      ╷              ╷       ╷
  Line Time % Time % Sys Mem %  Net   Memory usage  Copy                                                                Python native %   Python (MB)  over time / % (MB/s) profiling_examples/scalene_ex1.py                            
╺━━━━━━┿━━━━━━━┿━━━━━━━┿━━━━┿━━━━━━━┿━━━━━━┿━━━━━━━━━━━━━━┿━━━━━━━┿━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╸
     1 │       │       │    │       │      │              │       │def main_func():                                           
     2 │       │       │    │       │      │              │       │    import random                                          
     3 │   20% │       │    │  100% │    7 │▁▁▂▂▃▂▃       │    23 │    arr1 = [random.randint(1,10) for i in range(100000)]   
     4 │   11% │       │    │  100% │    4 │▂▂▂▃▃▃▃▄      │     9 │    arr2 = [random.randint(1,10) for i in range(100000)]   
     5 │    8% │       │    │  100% │   -4 │▁▁▁▁▁▁        │     9 │    arr3 = [arr1[i]+arr2[i] for i in range(100000)]        
     6 │       │       │    │       │      │              │       │    tot = sum(arr3)                                        
     7 │       │       │    │       │      │              │       │    print(tot)                                             
     8 │       │       │    │       │      │              │       │                                                           
     9 │       │       │    │       │      │              │       │if __name__ == "__main__":                                 
    10 │       │       │    │       │      │              │       │    main_func()                                            
       ╵       ╵       ╵    ╵       ╵      ╵              ╵       ╵


Sunny Solanki  Sunny Solanki