aws/autoscaling.ipynb

702 lines
154 KiB
Plaintext
Raw Permalink Normal View History

2024-12-21 12:30:26 +01:00
{
"cells": [
{
"metadata": {},
"cell_type": "markdown",
"source": "### 2 do 5 maszyn - ze skalowaniem (60s)",
"id": "8da38f621da326fa"
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-12-21T11:18:15.646664Z",
"start_time": "2024-12-21T11:00:55.641352Z"
}
},
"cell_type": "code",
"source": [
"import requests\n",
"import random\n",
"import math\n",
"import time\n",
"import threading\n",
"import logging\n",
"logging.getLogger().setLevel(logging.INFO)\n",
"\n",
"API_URL=\"http://s464863-lb-2086103880.us-east-1.elb.amazonaws.com:8080/\"\n",
"\n",
"UNIT = 60.0 # secs\n",
"\n",
"# Pre generated primes\n",
"first_primes_list = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29,\n",
" 31, 37, 41, 43, 47, 53, 59, 61, 67,\n",
" 71, 73, 79, 83, 89, 97, 101, 103,\n",
" 107, 109, 113, 127, 131, 137, 139,\n",
" 149, 151, 157, 163, 167, 173, 179,\n",
" 181, 191, 193, 197, 199, 211, 223,\n",
" 227, 229, 233, 239, 241, 251, 257,\n",
" 263, 269, 271, 277, 281, 283, 293,\n",
" 307, 311, 313, 317, 331, 337, 347, 349]\n",
"\n",
"\n",
"def nBitRandom(n):\n",
" return random.randrange(2**(n-1)+1, 2**n - 1)\n",
" \n",
"def getLowLevelPrime(n):\n",
" '''Generate a prime candidate divisible\n",
" by first primes'''\n",
" while True:\n",
" # Obtain a random number\n",
" pc = nBitRandom(n)\n",
" \n",
" # Test divisibility by pre-generated\n",
" # primes\n",
" for divisor in first_primes_list:\n",
" if pc % divisor == 0 and divisor**2 <= pc:\n",
" break\n",
" else: return pc\n",
" \n",
"def isMillerRabinPassed(mrc):\n",
" '''Run 20 iterations of Rabin Miller Primality test'''\n",
" maxDivisionsByTwo = 0\n",
" ec = mrc-1\n",
" while ec % 2 == 0:\n",
" ec >>= 1\n",
" maxDivisionsByTwo += 1\n",
" assert(2**maxDivisionsByTwo * ec == mrc-1)\n",
" \n",
" def trialComposite(round_tester):\n",
" if pow(round_tester, ec, mrc) == 1:\n",
" return False\n",
" for i in range(maxDivisionsByTwo):\n",
" if pow(round_tester, 2**i * ec, mrc) == mrc-1:\n",
" return False\n",
" return True\n",
" \n",
" # Set number of trials here\n",
" numberOfRabinTrials = 20\n",
" for i in range(numberOfRabinTrials):\n",
" round_tester = random.randrange(2, mrc)\n",
" if trialComposite(round_tester):\n",
" return False\n",
" return True\n",
" \n",
"def random_large_prime(bits):\n",
" while True:\n",
" prime_candidate = getLowLevelPrime(bits)\n",
" if not isMillerRabinPassed(prime_candidate):\n",
" continue\n",
" else:\n",
" return prime_candidate\n",
"\n",
"def thread_function(i, fast, timeout):\n",
" start = time.time()\n",
"\n",
" c = 5 # bits: 20: 200ms; 21: 350ms; 22: 700ms 23: 1.5s; 25: 6s; 26: 10s; 27: 24s\n",
" bits = 19 if fast else 23\n",
" last_report = time.time()\n",
" processing_time = 0.0\n",
" reqs = 0\n",
" while True:\n",
" iter_start = time.time()\n",
" if iter_start - start > timeout:\n",
" logging.info(\"Thread: %d\\treqs: %d\\tmean time: %.3fs\\t%s\"%(i, reqs, processing_time/reqs if reqs>0 else 0.0, \"fast\\t\" if fast else \"\"))\n",
" results[i][iter_start] = processing_time/reqs if reqs>0 else 0.0\n",
" return\n",
" if iter_start - last_report > UNIT/2:\n",
" if len(results[i])%2 == 0:\n",
" logging.info(\"Thread: %d\\treqs: %d\\tmean time: %.3fs\\t%s\"%(i, reqs, processing_time/reqs if reqs>0 else 0.0, \"fast\\t\" if fast else \"\"))\n",
" results[i][iter_start] = processing_time/reqs if reqs>0 else 0.0\n",
" processing_time = 0.0\n",
" reqs = 0\n",
" last_report=iter_start\n",
"\n",
" factors = [random_large_prime(bits) for i in range(c)]\n",
" factors.sort()\n",
" n=math.prod(factors)\n",
"\n",
" r = requests.get(API_URL+'/factors/%d'%(n))\n",
" if r.status_code != 200:\n",
" logging.error(\"wrong status code from webservice\")\n",
" else:\n",
" result = r.json()\n",
" if result != factors:\n",
" logging.error(\"Wrong factors\")\n",
"\n",
" processing_time+=time.time() - iter_start\n",
" reqs+=1\n",
" time.sleep(0.5)\n",
"\n",
"START = time.time()\n",
"slow_threads = 4\n",
"\n",
"results = [ {} for i in range(slow_threads+1)]\n",
"\n",
"t0 = threading.Thread(target=thread_function, args=(0, True, (5 + slow_threads*3) * UNIT))\n",
"t0.start()\n",
"time.sleep(2 * UNIT)\n",
"for i in range(slow_threads):\n",
" t = threading.Thread(target=thread_function, args=(i+1, False, (slow_threads-i) * 3 * UNIT))\n",
" t.start()\n",
" time.sleep(2 * UNIT)\n",
"\n",
"t0.join()"
],
"id": "8613c6fa0c16b496",
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"INFO:root:Thread: 0\treqs: 34\tmean time: 0.394s\tfast\t\n",
"INFO:root:Thread: 0\treqs: 25\tmean time: 1.235s\tfast\t\n",
"INFO:root:Thread: 1\treqs: 5\tmean time: 5.553s\t\n",
"INFO:root:Thread: 0\treqs: 17\tmean time: 1.370s\tfast\t\n",
"INFO:root:Thread: 1\treqs: 13\tmean time: 2.978s\t\n",
"INFO:root:Thread: 0\treqs: 2\tmean time: 21.416s\tfast\t\n",
"INFO:root:Thread: 2\treqs: 2\tmean time: 22.743s\t\n",
"INFO:root:Thread: 1\treqs: 16\tmean time: 1.436s\t\n",
"INFO:root:Thread: 0\treqs: 15\tmean time: 1.586s\tfast\t\n",
"INFO:root:Thread: 2\treqs: 15\tmean time: 1.637s\t\n",
"INFO:root:Thread: 1\treqs: 14\tmean time: 1.746s\t\n",
"INFO:root:Thread: 3\treqs: 10\tmean time: 2.624s\t\n",
"INFO:root:Thread: 2\treqs: 4\tmean time: 7.588s\t\n",
"INFO:root:Thread: 0\treqs: 10\tmean time: 4.636s\tfast\t\n",
"INFO:root:Thread: 1\treqs: 2\tmean time: 23.023s\t\n",
"INFO:root:Thread: 3\treqs: 4\tmean time: 7.500s\t\n",
"INFO:root:Thread: 2\treqs: 10\tmean time: 2.699s\t\n",
"INFO:root:Thread: 0\treqs: 9\tmean time: 2.929s\tfast\t\n",
"INFO:root:Thread: 4\treqs: 8\tmean time: 3.502s\t\n",
"INFO:root:Thread: 1\treqs: 8\tmean time: 3.540s\t\n",
"INFO:root:Thread: 3\treqs: 8\tmean time: 3.638s\t\n",
"INFO:root:Thread: 2\treqs: 8\tmean time: 3.663s\t\n",
"INFO:root:Thread: 0\treqs: 8\tmean time: 3.670s\tfast\t\n",
"INFO:root:Thread: 4\treqs: 8\tmean time: 3.527s\t\n",
"INFO:root:Thread: 1\treqs: 8\tmean time: 3.522s\t\n",
"INFO:root:Thread: 3\treqs: 8\tmean time: 3.624s\t\n",
"INFO:root:Thread: 2\treqs: 8\tmean time: 3.738s\t\n",
"INFO:root:Thread: 0\treqs: 8\tmean time: 3.691s\tfast\t\n",
"INFO:root:Thread: 4\treqs: 8\tmean time: 3.527s\t\n",
"INFO:root:Thread: 1\treqs: 8\tmean time: 3.502s\t\n",
"INFO:root:Thread: 4\treqs: 5\tmean time: 3.653s\t\n",
"INFO:root:Thread: 3\treqs: 8\tmean time: 3.592s\t\n",
"INFO:root:Thread: 2\treqs: 9\tmean time: 3.011s\t\n",
"INFO:root:Thread: 0\treqs: 9\tmean time: 2.892s\tfast\t\n",
"INFO:root:Thread: 1\treqs: 11\tmean time: 2.508s\t\n",
"INFO:root:Thread: 3\treqs: 8\tmean time: 2.525s\t\n",
"INFO:root:Thread: 2\treqs: 14\tmean time: 1.679s\t\n",
"INFO:root:Thread: 0\treqs: 14\tmean time: 1.679s\tfast\t\n",
"INFO:root:Thread: 2\treqs: 6\tmean time: 5.037s\t\n",
"INFO:root:Thread: 1\treqs: 6\tmean time: 5.041s\t\n",
"INFO:root:Thread: 0\treqs: 2\tmean time: 21.444s\tfast\t\n",
"INFO:root:Thread: 1\treqs: 6\tmean time: 1.288s\t\n",
"INFO:root:Thread: 0\treqs: 34\tmean time: 1.010s\tfast\t\n",
"INFO:root:Thread: 0\treqs: 2\tmean time: 21.476s\tfast\t\n",
"INFO:root:Thread: 0\treqs: 2\tmean time: 21.461s\tfast\t\n"
]
}
],
"execution_count": 1
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-12-21T11:18:17.153724Z",
"start_time": "2024-12-21T11:18:15.665198Z"
}
},
"cell_type": "code",
"source": [
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"import scipy.stats as stats\n",
"mu = 0\n",
"std = 1\n",
"for i, result in enumerate(results):\n",
" x = [(x - START)/UNIT for x in result.keys()]\n",
" y = result.values()\n",
" plt.plot(x, y, label=\"t%d\"%(i,))\n",
"\n",
"plt.legend()\n",
"plt.show()"
],
"id": "a81368cf19a093c1",
"outputs": [
{
"data": {
"text/plain": [
"<Figure size 640x480 with 1 Axes>"
],
"image/png": "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
},
"metadata": {},
"output_type": "display_data"
}
],
"execution_count": 2
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-12-21T11:19:43.182407Z",
"start_time": "2024-12-21T11:18:17.322562Z"
}
},
"cell_type": "code",
"source": [
"import requests\n",
"import random\n",
"import math\n",
"import time\n",
"import threading\n",
"import logging\n",
"logging.getLogger().setLevel(logging.INFO)\n",
"\n",
"\n",
"API_URL=\"http://s464863-lb-2086103880.us-east-1.elb.amazonaws.com:8080/\"\n",
"\n",
"UNIT = 5 # secs\n",
"\n",
"# Pre generated primes\n",
"first_primes_list = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29,\n",
" 31, 37, 41, 43, 47, 53, 59, 61, 67,\n",
" 71, 73, 79, 83, 89, 97, 101, 103,\n",
" 107, 109, 113, 127, 131, 137, 139,\n",
" 149, 151, 157, 163, 167, 173, 179,\n",
" 181, 191, 193, 197, 199, 211, 223,\n",
" 227, 229, 233, 239, 241, 251, 257,\n",
" 263, 269, 271, 277, 281, 283, 293,\n",
" 307, 311, 313, 317, 331, 337, 347, 349]\n",
"\n",
"\n",
"def nBitRandom(n):\n",
" return random.randrange(2**(n-1)+1, 2**n - 1)\n",
" \n",
"def getLowLevelPrime(n):\n",
" '''Generate a prime candidate divisible\n",
" by first primes'''\n",
" while True:\n",
" # Obtain a random number\n",
" pc = nBitRandom(n)\n",
" \n",
" # Test divisibility by pre-generated\n",
" # primes\n",
" for divisor in first_primes_list:\n",
" if pc % divisor == 0 and divisor**2 <= pc:\n",
" break\n",
" else: return pc\n",
" \n",
"def isMillerRabinPassed(mrc):\n",
" '''Run 20 iterations of Rabin Miller Primality test'''\n",
" maxDivisionsByTwo = 0\n",
" ec = mrc-1\n",
" while ec % 2 == 0:\n",
" ec >>= 1\n",
" maxDivisionsByTwo += 1\n",
" assert(2**maxDivisionsByTwo * ec == mrc-1)\n",
" \n",
" def trialComposite(round_tester):\n",
" if pow(round_tester, ec, mrc) == 1:\n",
" return False\n",
" for i in range(maxDivisionsByTwo):\n",
" if pow(round_tester, 2**i * ec, mrc) == mrc-1:\n",
" return False\n",
" return True\n",
" \n",
" # Set number of trials here\n",
" numberOfRabinTrials = 20\n",
" for i in range(numberOfRabinTrials):\n",
" round_tester = random.randrange(2, mrc)\n",
" if trialComposite(round_tester):\n",
" return False\n",
" return True\n",
" \n",
"def random_large_prime(bits):\n",
" while True:\n",
" prime_candidate = getLowLevelPrime(bits)\n",
" if not isMillerRabinPassed(prime_candidate):\n",
" continue\n",
" else:\n",
" return prime_candidate\n",
"\n",
"def thread_function(i, fast, timeout):\n",
" start = time.time()\n",
"\n",
" c = 5 # bits: 20: 200ms; 21: 350ms; 22: 700ms 23: 1.5s; 25: 6s; 26: 10s; 27: 24s\n",
" bits = 19 if fast else 23\n",
" last_report = time.time()\n",
" processing_time = 0.0\n",
" reqs = 0\n",
" while True:\n",
" iter_start = time.time()\n",
" if iter_start - start > timeout:\n",
" logging.info(\"Thread: %d\\treqs: %d\\tmean time: %.3fs\\t%s\"%(i, reqs, processing_time/reqs if reqs>0 else 0.0, \"fast\\t\" if fast else \"\"))\n",
" results[i][iter_start] = processing_time/reqs if reqs>0 else 0.0\n",
" return\n",
" if iter_start - last_report > UNIT/2:\n",
" if len(results[i])%2 == 0:\n",
" logging.info(\"Thread: %d\\treqs: %d\\tmean time: %.3fs\\t%s\"%(i, reqs, processing_time/reqs if reqs>0 else 0.0, \"fast\\t\" if fast else \"\"))\n",
" results[i][iter_start] = processing_time/reqs if reqs>0 else 0.0\n",
" processing_time = 0.0\n",
" reqs = 0\n",
" last_report=iter_start\n",
"\n",
" factors = [random_large_prime(bits) for i in range(c)]\n",
" factors.sort()\n",
" n=math.prod(factors)\n",
"\n",
" r = requests.get(API_URL+'/factors/%d'%(n))\n",
" if r.status_code != 200:\n",
" logging.error(\"wrong status code from webservice\")\n",
" else:\n",
" result = r.json()\n",
" if result != factors:\n",
" logging.error(\"Wrong factors\")\n",
"\n",
" processing_time+=time.time() - iter_start\n",
" reqs+=1\n",
" time.sleep(0.5)\n",
"\n",
"START = time.time()\n",
"slow_threads = 4\n",
"\n",
"results = [ {} for i in range(slow_threads+1)]\n",
"\n",
"t0 = threading.Thread(target=thread_function, args=(0, True, (5 + slow_threads*3) * UNIT))\n",
"t0.start()\n",
"time.sleep(2 * UNIT)\n",
"for i in range(slow_threads):\n",
" t = threading.Thread(target=thread_function, args=(i+1, False, (slow_threads-i) * 3 * UNIT))\n",
" t.start()\n",
" time.sleep(2 * UNIT)\n",
"\n",
"t0.join()"
],
"id": "712561570af0603d",
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"INFO:root:Thread: 0\treqs: 1\tmean time: 21.442s\tfast\t\n",
"INFO:root:Thread: 1\treqs: 1\tmean time: 22.393s\t\n",
"INFO:root:Thread: 2\treqs: 1\tmean time: 22.035s\t\n",
"INFO:root:Thread: 4\treqs: 2\tmean time: 1.301s\t\n",
"INFO:root:Thread: 0\treqs: 2\tmean time: 1.606s\tfast\t\n",
"INFO:root:Thread: 2\treqs: 2\tmean time: 1.551s\t\n",
"INFO:root:Thread: 4\treqs: 2\tmean time: 1.677s\t\n",
"INFO:root:Thread: 3\treqs: 1\tmean time: 23.503s\t\n",
"INFO:root:Thread: 4\treqs: 1\tmean time: 2.620s\t\n",
"INFO:root:Thread: 0\treqs: 1\tmean time: 2.646s\tfast\t\n",
"INFO:root:Thread: 2\treqs: 1\tmean time: 2.535s\t\n",
"INFO:root:Thread: 1\treqs: 1\tmean time: 2.514s\t\n",
"INFO:root:Thread: 3\treqs: 1\tmean time: 2.443s\t\n",
"INFO:root:Thread: 0\treqs: 1\tmean time: 2.544s\tfast\t\n",
"INFO:root:Thread: 2\treqs: 2\tmean time: 1.671s\t\n",
"INFO:root:Thread: 1\treqs: 2\tmean time: 1.527s\t\n",
"INFO:root:Thread: 1\treqs: 2\tmean time: 1.147s\t\n",
"INFO:root:Thread: 0\treqs: 3\tmean time: 0.891s\tfast\t\n",
"INFO:root:Thread: 0\treqs: 3\tmean time: 0.379s\tfast\t\n",
"INFO:root:Thread: 0\treqs: 3\tmean time: 0.394s\tfast\t\n",
"INFO:root:Thread: 0\treqs: 2\tmean time: 0.380s\tfast\t\n"
]
}
],
"execution_count": 3
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-12-21T11:19:43.300061Z",
"start_time": "2024-12-21T11:19:43.205792Z"
}
},
"cell_type": "code",
"source": [
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"import scipy.stats as stats\n",
"mu = 0\n",
"std = 1\n",
"for i, result in enumerate(results):\n",
" x = [(x - START)/UNIT for x in result.keys()]\n",
" y = result.values()\n",
" plt.plot(x, y, label=\"t%d\"%(i,))\n",
"\n",
"plt.legend()\n",
"plt.show()"
],
"id": "983c6fffba1a5dd0",
"outputs": [
{
"data": {
"text/plain": [
"<Figure size 640x480 with 1 Axes>"
],
"image/png": "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
},
"metadata": {},
"output_type": "display_data"
}
],
"execution_count": 4
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-12-21T11:28:25.963320Z",
"start_time": "2024-12-21T11:19:43.307797Z"
}
},
"cell_type": "code",
"source": [
"import requests\n",
"import random\n",
"import math\n",
"import time\n",
"import threading\n",
"import logging\n",
"logging.getLogger().setLevel(logging.INFO)\n",
"\n",
"\n",
"API_URL=\"http://s464863-lb-2086103880.us-east-1.elb.amazonaws.com:8080/\"\n",
"\n",
"UNIT = 30 # secs\n",
"\n",
"# Pre generated primes\n",
"first_primes_list = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29,\n",
" 31, 37, 41, 43, 47, 53, 59, 61, 67,\n",
" 71, 73, 79, 83, 89, 97, 101, 103,\n",
" 107, 109, 113, 127, 131, 137, 139,\n",
" 149, 151, 157, 163, 167, 173, 179,\n",
" 181, 191, 193, 197, 199, 211, 223,\n",
" 227, 229, 233, 239, 241, 251, 257,\n",
" 263, 269, 271, 277, 281, 283, 293,\n",
" 307, 311, 313, 317, 331, 337, 347, 349]\n",
"\n",
"\n",
"def nBitRandom(n):\n",
" return random.randrange(2**(n-1)+1, 2**n - 1)\n",
" \n",
"def getLowLevelPrime(n):\n",
" '''Generate a prime candidate divisible\n",
" by first primes'''\n",
" while True:\n",
" # Obtain a random number\n",
" pc = nBitRandom(n)\n",
" \n",
" # Test divisibility by pre-generated\n",
" # primes\n",
" for divisor in first_primes_list:\n",
" if pc % divisor == 0 and divisor**2 <= pc:\n",
" break\n",
" else: return pc\n",
" \n",
"def isMillerRabinPassed(mrc):\n",
" '''Run 20 iterations of Rabin Miller Primality test'''\n",
" maxDivisionsByTwo = 0\n",
" ec = mrc-1\n",
" while ec % 2 == 0:\n",
" ec >>= 1\n",
" maxDivisionsByTwo += 1\n",
" assert(2**maxDivisionsByTwo * ec == mrc-1)\n",
" \n",
" def trialComposite(round_tester):\n",
" if pow(round_tester, ec, mrc) == 1:\n",
" return False\n",
" for i in range(maxDivisionsByTwo):\n",
" if pow(round_tester, 2**i * ec, mrc) == mrc-1:\n",
" return False\n",
" return True\n",
" \n",
" # Set number of trials here\n",
" numberOfRabinTrials = 20\n",
" for i in range(numberOfRabinTrials):\n",
" round_tester = random.randrange(2, mrc)\n",
" if trialComposite(round_tester):\n",
" return False\n",
" return True\n",
" \n",
"def random_large_prime(bits):\n",
" while True:\n",
" prime_candidate = getLowLevelPrime(bits)\n",
" if not isMillerRabinPassed(prime_candidate):\n",
" continue\n",
" else:\n",
" return prime_candidate\n",
"\n",
"def thread_function(i, fast, timeout):\n",
" start = time.time()\n",
"\n",
" c = 5 # bits: 20: 200ms; 21: 350ms; 22: 700ms 23: 1.5s; 25: 6s; 26: 10s; 27: 24s\n",
" bits = 19 if fast else 23\n",
" last_report = time.time()\n",
" processing_time = 0.0\n",
" reqs = 0\n",
" while True:\n",
" iter_start = time.time()\n",
" if iter_start - start > timeout:\n",
" logging.info(\"Thread: %d\\treqs: %d\\tmean time: %.3fs\\t%s\"%(i, reqs, processing_time/reqs if reqs>0 else 0.0, \"fast\\t\" if fast else \"\"))\n",
" results[i][iter_start] = processing_time/reqs if reqs>0 else 0.0\n",
" return\n",
" if iter_start - last_report > UNIT/2:\n",
" if len(results[i])%2 == 0:\n",
" logging.info(\"Thread: %d\\treqs: %d\\tmean time: %.3fs\\t%s\"%(i, reqs, processing_time/reqs if reqs>0 else 0.0, \"fast\\t\" if fast else \"\"))\n",
" results[i][iter_start] = processing_time/reqs if reqs>0 else 0.0\n",
" processing_time = 0.0\n",
" reqs = 0\n",
" last_report=iter_start\n",
"\n",
" factors = [random_large_prime(bits) for i in range(c)]\n",
" factors.sort()\n",
" n=math.prod(factors)\n",
"\n",
" r = requests.get(API_URL+'/factors/%d'%(n))\n",
" if r.status_code != 200:\n",
" logging.error(\"wrong status code from webservice\")\n",
" else:\n",
" result = r.json()\n",
" if result != factors:\n",
" logging.error(\"Wrong factors\")\n",
"\n",
" processing_time+=time.time() - iter_start\n",
" reqs+=1\n",
" time.sleep(0.5)\n",
"\n",
"START = time.time()\n",
"slow_threads = 4\n",
"\n",
"results = [ {} for i in range(slow_threads+1)]\n",
"\n",
"t0 = threading.Thread(target=thread_function, args=(0, True, (5 + slow_threads*3) * UNIT))\n",
"t0.start()\n",
"time.sleep(2 * UNIT)\n",
"for i in range(slow_threads):\n",
" t = threading.Thread(target=thread_function, args=(i+1, False, (slow_threads-i) * 3 * UNIT))\n",
" t.start()\n",
" time.sleep(2 * UNIT)\n",
"\n",
"t0.join()"
],
"id": "be094f896ed40b1b",
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"INFO:root:Thread: 0\treqs: 16\tmean time: 1.717s\tfast\t\n",
"INFO:root:Thread: 0\treqs: 1\tmean time: 21.419s\tfast\t\n",
"INFO:root:Thread: 1\treqs: 1\tmean time: 22.195s\t\n",
"INFO:root:Thread: 0\treqs: 9\tmean time: 1.339s\tfast\t\n",
"INFO:root:Thread: 1\treqs: 9\tmean time: 1.361s\t\n",
"INFO:root:Thread: 2\treqs: 8\tmean time: 1.505s\t\n",
"INFO:root:Thread: 0\treqs: 8\tmean time: 1.586s\tfast\t\n",
"INFO:root:Thread: 1\treqs: 8\tmean time: 1.575s\t\n",
"INFO:root:Thread: 2\treqs: 8\tmean time: 1.622s\t\n",
"INFO:root:Thread: 0\treqs: 8\tmean time: 1.587s\tfast\t\n",
"INFO:root:Thread: 1\treqs: 8\tmean time: 1.554s\t\n",
"INFO:root:Thread: 3\treqs: 6\tmean time: 2.339s\t\n",
"INFO:root:Thread: 2\treqs: 5\tmean time: 2.570s\t\n",
"INFO:root:Thread: 0\treqs: 5\tmean time: 2.604s\tfast\t\n",
"INFO:root:Thread: 1\treqs: 5\tmean time: 2.694s\t\n",
"INFO:root:Thread: 3\treqs: 5\tmean time: 2.558s\t\n",
"INFO:root:Thread: 2\treqs: 5\tmean time: 2.583s\t\n",
"INFO:root:Thread: 0\treqs: 5\tmean time: 2.570s\tfast\t\n",
"INFO:root:Thread: 1\treqs: 5\tmean time: 2.792s\t\n",
"INFO:root:Thread: 4\treqs: 4\tmean time: 3.601s\t\n",
"INFO:root:Thread: 3\treqs: 4\tmean time: 3.686s\t\n",
"INFO:root:Thread: 1\treqs: 1\tmean time: 22.297s\t\n",
"INFO:root:Thread: 0\treqs: 3\tmean time: 9.913s\tfast\t\n",
"INFO:root:Thread: 2\treqs: 4\tmean time: 8.330s\t\n",
"INFO:root:Thread: 4\treqs: 1\tmean time: 22.307s\t\n",
"INFO:root:Thread: 3\treqs: 1\tmean time: 22.306s\t\n",
"INFO:root:Thread: 4\treqs: 1\tmean time: 1.490s\t\n",
"INFO:root:Thread: 1\treqs: 1\tmean time: 23.343s\t\n",
"INFO:root:Thread: 0\treqs: 1\tmean time: 23.323s\tfast\t\n",
"INFO:root:Thread: 2\treqs: 1\tmean time: 24.088s\t\n",
"INFO:root:Thread: 3\treqs: 5\tmean time: 2.595s\t\n",
"INFO:root:Thread: 3\treqs: 5\tmean time: 2.638s\t\n",
"INFO:root:Thread: 1\treqs: 5\tmean time: 2.624s\t\n",
"INFO:root:Thread: 0\treqs: 5\tmean time: 2.625s\tfast\t\n",
"INFO:root:Thread: 2\treqs: 6\tmean time: 2.302s\t\n",
"INFO:root:Thread: 1\treqs: 6\tmean time: 5.040s\t\n",
"INFO:root:Thread: 0\treqs: 6\tmean time: 5.038s\tfast\t\n",
"INFO:root:Thread: 2\treqs: 4\tmean time: 6.798s\t\n",
"INFO:root:Thread: 1\treqs: 1\tmean time: 22.445s\t\n",
"INFO:root:Thread: 0\treqs: 1\tmean time: 21.447s\tfast\t\n",
"INFO:root:Thread: 0\treqs: 1\tmean time: 21.433s\tfast\t\n",
"INFO:root:Thread: 0\treqs: 1\tmean time: 21.465s\tfast\t\n"
]
}
],
"execution_count": 5
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2024-12-21T11:28:26.106329Z",
"start_time": "2024-12-21T11:28:25.992058Z"
}
},
"cell_type": "code",
"source": [
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"import scipy.stats as stats\n",
"mu = 0\n",
"std = 1\n",
"for i, result in enumerate(results):\n",
" x = [(x - START)/UNIT for x in result.keys()]\n",
" y = result.values()\n",
" plt.plot(x, y, label=\"t%d\"%(i,))\n",
"\n",
"plt.legend()\n",
"plt.show()"
],
"id": "b19df5c85a45eec7",
"outputs": [
{
"data": {
"text/plain": [
"<Figure size 640x480 with 1 Axes>"
],
"image/png": "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
},
"metadata": {},
"output_type": "display_data"
}
],
"execution_count": 6
},
{
"metadata": {},
"cell_type": "code",
"outputs": [],
"execution_count": null,
"source": "",
"id": "b8073aff580c827f"
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.6"
}
},
"nbformat": 4,
"nbformat_minor": 5
}