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.
In the above figure, green represents fast, yellow represents slow, and red represents very slow.
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.
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.
!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() ╵ ╵ ╵ ╵ ╵ ╵ ╵ ╵
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.
!scalene --outfile profiling_output.txt profiling_examples/scalene_ex1.py
1101208
!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() ╵ ╵ ╵ ╵ ╵ ╵ ╵ ╵
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.
!scalene --html --outfile profiling_output.html profiling_examples/scalene_ex1.py
1100247
from IPython.display import HTML
HTML(open("profiling_output.html").read())
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
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.
!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)] ... │ │ │ │ │ │ │ │ ╵ ╵ ╵ ╵ ╵ ╵ ╵ ╵
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.
!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() ╵ ╵ ╵ ╵
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).
!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() ╵ ╵ ╵ ╵ ╵ ╵ ╵ ╵
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)
!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 │ │ │ │ │ │ │ │ ╵ ╵ ╵ ╵ ╵ ╵ ╵ ╵
!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 │ │ │ │ │ │ │ │ ╵ ╵ ╵ ╵ ╵ ╵ ╵ ╵
!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 │ │ │ │ │ │ │ │ ╵ ╵ ╵ ╵ ╵ ╵ ╵ ╵
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()
!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() ╵ ╵ ╵ ╵ ╵ ╵ ╵ ╵
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.
!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() ╵ ╵ ╵ ╵ ╵ ╵ ╵ ╵
The --profile-interval
option instructs scalene to output profiling info at an interval in seconds specified by an integer followed by this option.
!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() ╵ ╵ ╵ ╵ ╵ ╵ ╵ ╵
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.
!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() ╵ ╵ ╵ ╵ ╵ ╵ ╵ ╵
This ends our small tutorial explaining the usage of scalene. Please feel free to let us know your views in the comments section.
Intro: Software Developer | Youtuber | Bonsai Enthusiast
About: Sunny Solanki holds a bachelor's degree in Information Technology (2006-2010) from L.D. College of Engineering. Post completion of his graduation, he has 8.5+ years of experience (2011-2019) in the IT Industry (TCS). His IT experience involves working on Python & Java Projects with US/Canada banking clients. Since 2020, he’s primarily concentrating on growing CoderzColumn.
His main areas of interest are AI, Machine Learning, Data Visualization, and Concurrent Programming. He has good hands-on with Python and its ecosystem libraries.
Apart from his tech life, he prefers reading biographies and autobiographies. And yes, he spends his leisure time taking care of his plants and a few pre-Bonsai trees.
Contact: sunny.2309@yahoo.in
If you are more comfortable learning through video tutorials then we would recommend that you subscribe to our YouTube channel.
When going through coding examples, it's quite common to have doubts and errors.
If you have doubts about some code examples or are stuck somewhere when trying our code, send us an email at coderzcolumn07@gmail.com. We'll help you or point you in the direction where you can find a solution to your problem.
You can even send us a mail if you are trying something new and need guidance regarding coding. We'll try to respond as soon as possible.
If you want to