-
Notifications
You must be signed in to change notification settings - Fork 3
replace project into fork #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
maksim-dzhigil
wants to merge
1
commit into
railbotan:main
Choose a base branch
from
maksim-dzhigil:main
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| from hashlib import md5 | ||
| from random import choice | ||
|
|
||
| coins = 0 | ||
| while coins != 4: | ||
| s = "".join([choice("0123456789") for i in range(50)]) | ||
| h = md5(s.encode('utf8')).hexdigest() | ||
|
|
||
| if h.endswith("00000"): | ||
| print(s, h) | ||
| coins += 1 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| from hashlib import md5 | ||
| from random import choice | ||
| import concurrent.futures | ||
|
|
||
|
|
||
| def crypt_miner(i): | ||
| while True: | ||
| s = "".join([choice("0123456789") for j in range(50)]) | ||
| h = md5(s.encode('utf8')).hexdigest() | ||
|
|
||
| if h.endswith("00000"): | ||
| return f"{s} {h}" | ||
|
|
||
|
|
||
| def main(): | ||
| with concurrent.futures.ProcessPoolExecutor(max_workers=100) as executor: | ||
| for coin in executor.map(crypt_miner, range(4)): | ||
| print(coin) | ||
|
|
||
|
|
||
| if __name__ == '__main__': | ||
| main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| from urllib.request import urlopen, Request | ||
|
|
||
| links = open('res.txt', encoding='utf8').read().split('\n') | ||
|
|
||
| for url in links: | ||
| try: | ||
| request = Request( | ||
| url, | ||
| headers={'User-Agent': 'Mozilla/5.0 (Windows NT 9.0; Win65; x64; rv:97.0) Gecko/20105107 Firefox/92.0'}, | ||
| ) | ||
| resp = urlopen(request, timeout=5) | ||
| code = resp.code | ||
| print(code) | ||
| resp.close() | ||
| except Exception as e: | ||
| print(url, e) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| import concurrent.futures | ||
| import urllib | ||
| from urllib.request import urlopen, Request | ||
|
|
||
| links = open('res.txt', encoding='utf8').read().split('\n') | ||
|
|
||
|
|
||
| def load_url(url, timeout): | ||
| with urllib.request.urlopen(url, timeout=timeout) as conn: | ||
| return conn.read() | ||
|
|
||
|
|
||
| with concurrent.futures.ThreadPoolExecutor(max_workers=100) as executor: | ||
| future_to_url = {executor.submit(load_url, url, 60): url for url in links} | ||
| for future in concurrent.futures.as_completed(future_to_url): | ||
| url = future_to_url[future] | ||
| try: | ||
| request = Request( | ||
| url, | ||
| headers={'User-Agent': 'Mozilla/5.0 (Windows NT 9.0; Win65; x64; rv:97.0) Gecko/20105107 Firefox/92.0'}, | ||
| ) | ||
| except Exception as exc: | ||
| print(url, exc) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,195 @@ | ||
| # concurrency-and-asynchrony-task | ||
|
|
||
| <h2>IO-bound</h2> | ||
| Суть задания - посетить 100 случайных страниц в Википедии и сохранить все ссылки с этих страниц. Затем проанализировать скорость их обработки, используя ThreadPoolExecutor. | ||
|
|
||
| Этим кодом запишем все ссылки в файл res.txt: | ||
|
|
||
| from urllib.request import urlopen | ||
| from urllib.parse import unquote | ||
| from bs4 import BeautifulSoup | ||
| from tqdm import tqdm | ||
|
|
||
| url = 'https://ru.wikipedia.org/wiki/%D0%A1%D0%BB%D1%83%D0%B6%D0%B5%D0%B1%D0%BD%D0%B0%D1%8F:%D0%A1%D0%BB%D1%83%D1%87%D0%B0%D0%B9%D0%BD%D0%B0%D1%8F_%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86%D0%B0' | ||
|
|
||
| res = open('res.txt', 'w', encoding='utf8') | ||
|
|
||
| for i in tqdm(range(100)): | ||
| html = urlopen(url).read().decode('utf8') | ||
| soup = BeautifulSoup(html, 'html.parser') | ||
| links = soup.find_all('a') | ||
|
|
||
| for l in links: | ||
| href = l.get('href') | ||
| if href and href.startswith('http') and 'wiki' not in href: | ||
| print(href, file=res) | ||
|
|
||
| Всего получилось 786 ссылок | ||
|
|
||
|  | ||
|
|
||
| На протяжении работы программы количество используемой памяти почти не изменялось, а нагрузка на проессор находилась в районе 60%, периодически улетая в 98-100%. В конце (график ЦП) отчетливо видно момент, когда программа завершила работу: | ||
|
|
||
|  | ||
|
|
||
| Всего на обработку одним потоком 786 ссылок ушло 1242 секунды: | ||
|
|
||
|  | ||
|
|
||
| Теперь перепишем код, используя ThreadPoolExecutor: | ||
|
|
||
| import concurrent.futures | ||
| import urllib | ||
| from urllib.request import urlopen, Request | ||
|
|
||
| links = open('res.txt', encoding='utf8').read().split('\n') | ||
|
|
||
|
|
||
| def load_url(url, timeout): | ||
| with urllib.request.urlopen(url, timeout=timeout) as conn: | ||
| return conn.read() | ||
|
|
||
|
|
||
| with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor: | ||
| future_to_url = {executor.submit(load_url, url, 60): url for url in links} | ||
| for future in concurrent.futures.as_completed(future_to_url): | ||
| url = future_to_url[future] | ||
| try: | ||
| request = Request( | ||
| url, | ||
| headers={'User-Agent': 'Mozilla/5.0 (Windows NT 9.0; Win65; x64; rv:97.0) Gecko/20105107 Firefox/92.0'}, | ||
| ) | ||
| except Exception as exc: | ||
| print(url, exc) | ||
| else: | ||
| print(url) | ||
|
|
||
| И теперь сравним время работы и нагрузку на ЦП одного потока с временем работы с 5, 10, 100 workers: | ||
|
|
||
| <h4>С max_workers=5:</h4> | ||
|
|
||
|  | ||
|  | ||
|
|
||
| <h4>C max_workers=10:</h4> | ||
|
|
||
|  | ||
|  | ||
|  | ||
| На диаграмме диспетчера (график ЦП) тест начинается примерно в середине (после проседания),его и стоит анализировать. | ||
| На 3м скриншоте видны задержки первые 5.5 секунд в MainThread | ||
|
|
||
| <h4>И с max_workers=100:</h4> | ||
|
|
||
|  | ||
|  | ||
|  | ||
| На 3м скриншоте также видны задержки в MainThread, но уже в течение 8 секунд. | ||
| Всего ушло 218 секунд (на скриншоте может быть плохо видно) | ||
|
|
||
| В итоге по графикам видно, что количество потоков в IO-bound задаче никак не влияет на количество используемой памяти. Нагрузка на процессор при max_workers=5 не сильно отличается от нагрузки при max_workers=100, т.е. использование ThreadPoolExecutor увеличивает нагрузку на процессор, но предельное значение уровня нагрузки достигается почти сразу и дальнейшее увеличение количества воркеров ведет только к изменению времени.<br> | ||
|
|
||
| Теперь о времени. Для 5, 10 и 100 воркеров время работы составило 535, 292 и 218 секунд соответственно. Значит увеличение количества потоков ведет к увеличению скорости работы, но постепенно увеличение количества потоков будет приводить ко все меньшему увеличению производительности, пока не будет достигнут предел. | ||
|
|
||
| _____ | ||
| <<<<<<< HEAD | ||
|
|
||
| <h2>CPU-bound</h2> | ||
|
|
||
| Теперь генерируем монетки. Программа будет работать, пока не найдет 4 монетки. Начинаем с генерации на одном ядре: | ||
|
|
||
| from hashlib import md5 | ||
| from random import choice | ||
|
|
||
| coins = 0 | ||
| while coins != 4: | ||
| s = "".join([choice("0123456789") for i in range(50)]) | ||
| h = md5(s.encode('utf8')).hexdigest() | ||
|
|
||
| if h.endswith("00000"): | ||
| coins += 1 | ||
| print(s, h) | ||
|
|
||
| Результат работы программы: | ||
|
|
||
|  | ||
|  | ||
|
|
||
|
|
||
| Теперь перепишем код с использованием ProcessPoolExecutor: | ||
|
|
||
| from hashlib import md5 | ||
| from random import choice | ||
| import concurrent.futures | ||
|
|
||
|
|
||
| def crypt_miner(i): | ||
| while True: | ||
| s = "".join([choice("0123456789") for j in range(50)]) | ||
| h = md5(s.encode('utf8')).hexdigest() | ||
|
|
||
| if h.endswith("00000"): | ||
| return f"{s} {h}" | ||
|
|
||
|
|
||
| def main(): | ||
| with concurrent.futures.ProcessPoolExecutor(max_workers=2) as executor: | ||
| for coin in executor.map(crypt_miner, range(4)): | ||
| print(coin) | ||
|
|
||
|
|
||
| if __name__ == '__main__': | ||
| main() | ||
|
|
||
| Для анализа тестов важно знать информацию о процессоре (2 ядра): | ||
|
|
||
|  | ||
|
|
||
| Тесты начнем с <h4>max_workers=2:</h4> | ||
|
|
||
|  | ||
| С самого начала процессор был нагружен на 100%, диспетчер отображал 2 запущенных процесса | ||
|
|
||
|  | ||
| Уровень загруженности процесора не менялся до конца работы программы | ||
|
|
||
|  | ||
| Обратил внимание на то,что в консоли монеты появлялись парами. Не знаю, влияло ли это как-нибудь на скорость вычислений, но время работы даже больше, чем без ProcessPoolExecutor | ||
|
|
||
| <h4>Теперь max_workers=4:</h4> | ||
|
|
||
|  | ||
| Нагрузка также с самого начала 100%, диспетчер отображает 4 запущенных процесса | ||
|
|
||
|  | ||
| И как в прошлый раз, нагрузка оставалсь 100% до конца работы программы | ||
|
|
||
|  | ||
| Но время изменилось, монетки майнились быстрее | ||
|
|
||
|
|
||
| На этапе <h4>max-workers=10</h4> я не могу объяснить происходящее: | ||
|
|
||
|  | ||
| Началось все так же со 100%, но диспетчер отображал уже только 4 процесса, которые занимали основные ресуры процессора(23-24% на каждый процесс) | ||
|
|
||
|  | ||
| Затем уровень нагрузки упал. Я не сделал скрины, но осталось только 2 процесса, которые делили ресурсы уже по 46-48%. При этом в консоли не было ни одной монетки. Под конец остался один процесс, а затем программа заврешила работу | ||
|
|
||
|  | ||
| Время майнинга сопоставимо с временем при 4 ворекерах. | ||
|
|
||
|
|
||
| И последний тест с <h4>max-workers=100:</h4> | ||
|
|
||
|  | ||
| Как и раньше сразу 100% нагрузка и 4 процесса | ||
|
|
||
|  | ||
| Вот осталось 3 процесса при той же нагрузке в 100%. Если я правильно понял, процесс заканчивается, когда сгенерирована монетка, но при этом в консоль монетки выводятся только парами | ||
|
|
||
|  | ||
| Последняя монетка создана, все процессы отработали | ||
|
|
||
|  | ||
| Монетки майнились на компьютере с ОС Linux, поэтому ограничение на максимальное количество воркеров 61 (такое стоит на винде) здесь не сработало, но это самое было самое долгое выполнение программы |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Зачем?