-
Notifications
You must be signed in to change notification settings - Fork 173
homework_06 #1271
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
erinar
wants to merge
1
commit into
DmitryChitalov:master
Choose a base branch
from
erinar:lesson_6
base: master
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
homework_06 #1271
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 |
|---|---|---|
| @@ -1,15 +1,167 @@ | ||
| """ | ||
| Задание 1. | ||
| Выполните профилирование памяти в скриптах | ||
| Проанализировать результат и определить программы с | ||
| наиболее эффективным использованием памяти. | ||
|
|
||
| Примечание: Для анализа возьмите любые 1-3 ваших программы или несколько | ||
| вариантов кода для одной и той же задачи. Можно взять задачи с курса Основ или с текущего курса Алгоритмов | ||
|
|
||
| Результаты анализа вставьте в виде комментариев к коду. | ||
| Также укажите в комментариях версию Python и разрядность вашей ОС. | ||
|
|
||
| ВНИМАНИЕ: ЗАДАНИЯ, В КОТОРЫХ БУДУТ ГОЛЫЕ ЦИФРЫ ЗАМЕРОВ (БЕЗ АНАЛИТИКИ) | ||
| БУДУТ ПРИНИМАТЬСЯ С ОЦЕНКОЙ УДОВЛЕТВОРИТЕЛЬНО | ||
| """ | ||
| """ | ||
| Задание 1. | ||
|
|
||
| Выполните профилирование памяти в скриптах | ||
| Проанализировать результат и определить программы с | ||
| наиболее эффективным использованием памяти. | ||
|
|
||
| Примечание: Для анализа возьмите любые 3-5 ваших РАЗНЫХ скриптов! | ||
| (хотя бы 3 разных для получения оценки отл). | ||
| На каждый скрипт вы должны сделать как минимум по две реализации. | ||
|
|
||
| Можно взять задачи с курса Основ | ||
| или с текущего курса Алгоритмов | ||
|
|
||
| Результаты профилирования добавьте в виде комментариев к коду. | ||
| Обязательно сделайте аналитику (что с памятью в ваших скриптах, в чем ваша оптимизация и т.д.) | ||
|
|
||
| ВНИМАНИЕ: ЗАДАНИЯ, В КОТОРЫХ БУДУТ ГОЛЫЕ ЦИФРЫ ЗАМЕРОВ (БЕЗ АНАЛИТИКИ) | ||
| БУДУТ ПРИНИМАТЬСЯ С ОЦЕНКОЙ УДОВЛЕТВОРИТЕЛЬНО | ||
|
|
||
| Попытайтесь дополнительно свой декоратор используя ф-цию memory_usage из memory_profiler | ||
| С одновременным замером времени (timeit.default_timer())! | ||
| """ | ||
| from random import randint, random | ||
| from timememit import timememit | ||
| from pympler.asizeof import asizeof | ||
| from recordclass import recordclass | ||
| from datetime import datetime, timedelta | ||
| import numpy as np | ||
| import json | ||
| import msgpack | ||
| import sys | ||
|
|
||
|
|
||
| args = iter(sys.argv) | ||
| next(args) | ||
| ARG1 = next(args, None) | ||
| NOW = datetime.now() | ||
| LETRANGE = (ord('A'), ord('Z')) | ||
|
|
||
| # Будем применять способы оптимизации памяти, рассмотренные на уроке | ||
|
|
||
| # 1. Ленивые вычисления | ||
| # пример в файле task_3a.py | ||
|
|
||
| # 2. Слоты в ООП | ||
| # 3. numpy (также пример в файле task_2.py) | ||
| # 4. recordclass | ||
|
|
||
|
|
||
| # Пусть у нас есть список чего-либо, допустим, биржевой торговли | ||
| def gen_transaction(): | ||
| return ( | ||
| # ticker | ||
| ''.join(chr(randint(*LETRANGE)) for _ in range(4)), | ||
| # number | ||
| randint(1, 1000), | ||
| # seller_id | ||
| randint(1, 10000000), | ||
| # buyer_id | ||
| randint(1, 10000000), | ||
| # price | ||
| randint(1, 10000000), | ||
| # timestamp | ||
| NOW + timedelta(seconds=randint(0, 100000)) | ||
| ) | ||
|
|
||
|
|
||
| class Transaction: | ||
| def __init__(self, ticker, number, seller, buyer, price, timestamp): | ||
| self.ticker = ticker | ||
| self.number = number | ||
| self.seller = seller | ||
| self.byer = buyer | ||
| self.price = price | ||
| self.timestamp = timestamp | ||
|
|
||
|
|
||
| class TransSlots: | ||
| __slots__ = ['ticker', 'number', 'seller', 'buyer', 'price', 'timestamp'] | ||
|
|
||
| def __init__(self, ticker, number, seller, buyer, price, timestamp): | ||
| self.ticker = ticker | ||
| self.number = number | ||
| self.seller = seller | ||
| self.buyer = buyer | ||
| self.price = price | ||
| self.timestamp = timestamp | ||
|
|
||
|
|
||
| TransRecord = recordclass( | ||
| 'TransRecord', | ||
| 'ticker number seller buyer price timestamp') | ||
|
|
||
| trans_dtype = [ | ||
| ('ticks', '<U4'), | ||
| ('number', '<i4'), | ||
| ('seller', '<i4'), | ||
| ('buyer', '<i4'), | ||
| ('price', '<i4'), | ||
| ('timestamp', 'datetime64[m]')] | ||
|
|
||
| if ARG1 is None: | ||
| n = 1000 | ||
| classes = [Transaction(*gen_transaction()) for i in range(n)] | ||
| slots = [TransSlots(*gen_transaction()) for i in range(n)] | ||
| records = [TransRecord(*gen_transaction()) for i in range(n)] | ||
| nprecords = np.array( | ||
| [gen_transaction() for i in range(n)], | ||
| dtype=trans_dtype) | ||
|
|
||
| print(f'Размер массива из {n} элементов') | ||
| print(f' class: {asizeof(classes)}') | ||
| print(f' __slots__: {asizeof(slots)}') | ||
| print(f'recordclass: {asizeof(records)}') | ||
| print(f' numpy: {asizeof(nprecords)}') | ||
| # --- | ||
| # Размер массива из 1000 элементов | ||
| # class: 429648 | ||
| # __slots__: 318232 | ||
| # recordclass: 82176 | ||
| # numpy: 40120 | ||
| # --- | ||
| # Практически невозможно "победить" по потреблению памяти numpy, | ||
| # но у этого есть своя цена, массивы numpy фиксированного размера, | ||
| # поэтому их применение подходит не для всех алгоритмов. | ||
| # recordclass по результатам измерений на втором месте, но это | ||
| # дополнительный модуль, компилируемый в бинарный код, что | ||
| # накладывает некоторые ограничения на развертывание приложения. | ||
| # __slots__ является частью языка и этих ограничений не имеет, | ||
| # однако в данном примере не видно большого уменьшения памяти. | ||
|
|
||
| # 5. map | ||
| # map можно отнести к ленивым вычислениям, | ||
| # в python3 map(f, seq) без потерь по производительности | ||
| # и памяти можно заменять на (f(x) for x in seq) | ||
|
|
||
| # 6. Сериализация | ||
| # Продположим у нас есть сервер, к которому подключаются | ||
| # пользоатели, для каждого из которых нужно хранить | ||
| # много данных. | ||
| # Можно разделить пользователей по частоте обращения, | ||
| # и данные "заснувших" сессий сериализовать. | ||
|
|
||
|
|
||
| # Для примера построим дерево cо строчками на крайних ветках | ||
| def gen_tree(deep=5): | ||
| if deep == 0: | ||
| return str(randint(1, 99)) | ||
| newdeep = deep - 1 | ||
| return [gen_tree(newdeep), gen_tree(newdeep)] | ||
|
|
||
|
|
||
| if ARG1 == "6": | ||
| tree = gen_tree(20) | ||
| print( | ||
| f' Размер объекта в памяти: {asizeof(tree)}') | ||
| print( | ||
| f' Размер сериализованных данных (json): {len(json.dumps(tree))}') | ||
| print( | ||
| f'Размер сериализованных данных (msgpack): ' | ||
| + f'{len(msgpack.packb(tree, use_bin_type=True))}') | ||
| # --- | ||
| # Размер объекта в памяти: 134217656 | ||
| # Размер сериализованных данных (json): 8293148 | ||
| # Размер сериализованных данных (msgpack): 4098847 | ||
| # --- | ||
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 |
|---|---|---|
| @@ -1,7 +1,209 @@ | ||
| """ | ||
| Задание 2. | ||
| Предложите фундаментальные варианты оптимизации памяти | ||
| и доказать (наглядно, кодом, если получится) их эффективность | ||
|
|
||
| Например, один из вариантов, использование генераторов | ||
| """ | ||
| """ | ||
| Задание 2.* | ||
| Предложить еще какие-либо варианты (механизмы, подходы, библиотеки, идеи) | ||
| для оптимизации памяти и | ||
| доказать!!! (наглядно, кодом) их эффективность (на примере профилировщика) | ||
| """ | ||
| from timememit import timememit | ||
| from numpy import arange, log, sin, cos, sqrt | ||
| from array import array | ||
| import math | ||
| import sys | ||
|
|
||
| args = iter(sys.argv) | ||
| next(args) | ||
| ARG1 = next(args, None) | ||
|
|
||
|
|
||
| # Сравним требования по памяти для трех реализаций | ||
| # "решета Эратосфена", через array, numpy и на | ||
| # "чистом" python | ||
| def upper_prime(n): | ||
| ln = log(n) | ||
| return int(n*(ln + log(ln))) | ||
|
|
||
|
|
||
| # через numpy | ||
| @timememit | ||
| def eratosthenes_numpy(n): | ||
| # разница только в инициализации массива | ||
| sieve = arange(upper_prime(n)) | ||
| k = 2 | ||
| for _ in range(n - 1): | ||
| # и заполнении решета | ||
| sieve[::k] = 0 | ||
| while sieve[k] == 0: | ||
| k += 1 | ||
| return k | ||
|
|
||
|
|
||
| # через array | ||
| @timememit | ||
| def eratosthenes_array(n): | ||
| size = upper_prime(n) | ||
| sieve = array('i', range(size)) | ||
| k = 2 | ||
| for _ in range(n - 1): | ||
| for i in range(k, size, k): | ||
| sieve[i] = 0 | ||
| while sieve[k] == 0: | ||
| k += 1 | ||
| return k | ||
|
|
||
|
|
||
| # на чистом python | ||
| @timememit | ||
| def eratosthenes(n): | ||
| size = upper_prime(n) | ||
| sieve = list(range(size)) | ||
| k = 2 | ||
| for _ in range(n - 1): | ||
| for i in range(k, size, k): | ||
| sieve[i] = 0 | ||
| while sieve[k] == 0: | ||
| k += 1 | ||
| return k | ||
|
|
||
|
|
||
| n = 100000 | ||
|
|
||
|
|
||
|
|
||
| if ARG1 is None: | ||
| print(f'prime({n}) = {eratosthenes(n)}') | ||
| print(f'prime({n}) = {eratosthenes(n)}') | ||
| print(f'prime({n}) = {eratosthenes(n)}') | ||
| print(f'prime({n}) = {eratosthenes(n)}') | ||
| print(f'prime({n}) = {eratosthenes_numpy(n)}') | ||
| print(f'prime({n}) = {eratosthenes_numpy(n)}') | ||
| print(f'prime({n}) = {eratosthenes_numpy(n)}') | ||
| print(f'prime({n}) = {eratosthenes_numpy(n)}') | ||
| print(f'prime({n}) = {eratosthenes_array(n)}') | ||
| print(f'prime({n}) = {eratosthenes_array(n)}') | ||
| print(f'prime({n}) = {eratosthenes_array(n)}') | ||
| print(f'prime({n}) = {eratosthenes_array(n)}') | ||
| # --- | ||
| # eratosthenes: 0.6396 s, 0.656250 MiB | ||
| # prime(100000) = 1299709 | ||
| # eratosthenes: 0.5894 s, 10.886719 MiB | ||
| # prime(100000) = 1299709 | ||
| # eratosthenes: 0.5845 s, 0.238281 MiB | ||
| # prime(100000) = 1299709 | ||
| # eratosthenes: 0.5792 s, 0.238281 MiB | ||
| # prime(100000) = 1299709 | ||
| # eratosthenes_array: 0.5666 s, 0.000000 MiB | ||
| # prime(100000) = 1299709 | ||
| # eratosthenes_array: 0.5675 s, 0.000000 MiB | ||
| # prime(100000) = 1299709 | ||
| # eratosthenes_array: 0.5665 s, 0.000000 MiB | ||
| # prime(100000) = 1299709 | ||
| # eratosthenes_array: 0.5690 s, 0.000000 MiB | ||
| # prime(100000) = 1299709 | ||
| # eratosthenes_numpy: 0.5143 s, 0.000000 MiB | ||
| # prime(100000) = 1299709 | ||
| # eratosthenes_numpy: 0.5149 s, 0.000000 MiB | ||
| # prime(100000) = 1299709 | ||
| # eratosthenes_numpy: 0.5138 s, 0.000000 MiB | ||
| # prime(100000) = 1299709 | ||
| # eratosthenes_numpy: 0.5152 s, 0.000000 MiB | ||
| # prime(100000) = 1299709 | ||
| # --- | ||
| # | ||
| # Теперь поменяем вызовы местами. | ||
| # Ключ коммандной строки показывает, что замеры | ||
| # нужно делать сразу после запуска, иначе | ||
| # будут нули | ||
| if ARG1 == 'array': | ||
| print(f'prime({n}) = {eratosthenes_array(n)}') | ||
| print(f'prime({n}) = {eratosthenes_array(n)}') | ||
| print(f'prime({n}) = {eratosthenes_array(n)}') | ||
| print(f'prime({n}) = {eratosthenes_array(n)}') | ||
| print(f'prime({n}) = {eratosthenes_numpy(n)}') | ||
| print(f'prime({n}) = {eratosthenes_numpy(n)}') | ||
| print(f'prime({n}) = {eratosthenes_numpy(n)}') | ||
| print(f'prime({n}) = {eratosthenes_numpy(n)}') | ||
| print(f'prime({n}) = {eratosthenes(n)}') | ||
| print(f'prime({n}) = {eratosthenes(n)}') | ||
| print(f'prime({n}) = {eratosthenes(n)}') | ||
| print(f'prime({n}) = {eratosthenes(n)}') | ||
| # --- | ||
| # eratosthenes_array: 0.5791 s, 0.781250 MiB | ||
| # prime(100000) = 1299709 | ||
| # eratosthenes_array: 0.5782 s, 4.636719 MiB | ||
| # prime(100000) = 1299709 | ||
| # eratosthenes_array: 0.5912 s, 0.000000 MiB | ||
| # prime(100000) = 1299709 | ||
| # eratosthenes_array: 0.5735 s, 0.000000 MiB | ||
| # prime(100000) = 1299709 | ||
| # eratosthenes_numpy: 0.5275 s, 0.066406 MiB | ||
| # prime(100000) = 1299709 | ||
| # eratosthenes_numpy: 0.5225 s, 5.152344 MiB | ||
| # prime(100000) = 1299709 | ||
| # eratosthenes_numpy: 0.5150 s, 0.000000 MiB | ||
| # prime(100000) = 1299709 | ||
| # eratosthenes_numpy: 0.5153 s, 0.000000 MiB | ||
| # prime(100000) = 1299709 | ||
| # eratosthenes: 0.5854 s, 0.675781 MiB | ||
| # prime(100000) = 1299709 | ||
| # eratosthenes: 0.5867 s, 0.246094 MiB | ||
| # prime(100000) = 1299709 | ||
| # eratosthenes: 0.5843 s, 0.246094 MiB | ||
| # prime(100000) = 1299709 | ||
| # eratosthenes: 0.5867 s, 0.246094 MiB | ||
| # prime(100000) = 1299709 | ||
| # --- | ||
| # Этот пример помогает нам понять результаты прошлого | ||
| # примера. Алгоритм "array" выделяет 1 блок 5M, | ||
| # "numpy" -- 1 блок 10M, "list" -- 1 блок по 10M | ||
| # и еще множество мелких блоков. | ||
| # Поэтому если расположить вызовы в порядке возрастания | ||
| # потребления памяти, мы видим ее выделение на каждой | ||
| # группе вызовов. Если же расположить в обратном порядке, | ||
| # то выделение происходит только на первой группе, | ||
| # на остальных -- нули. | ||
|
|
||
| # Что же касается array, то в этой задаче он показывает | ||
| # двукратную экономию памяти по сравнению с numpy и python, | ||
| # при этом не теряя в производительности. | ||
| # Однако заменить numpy он, конечно, не может | ||
| # Идеология numpy не сводится к экономии памяти, | ||
| # т.к. эта библиотека создавалась для ускорения | ||
| # вычисления сложных формул путем векторизации. | ||
| # Поэтому огромные массивы numpy являются платой | ||
| # за возможность выполнять быстрые С-функции. | ||
|
|
||
|
|
||
| @timememit | ||
| def formula_array(): | ||
| xx = array('d', range(n)) | ||
| yy = array('d', range(n)) | ||
| for i, x in enumerate(xx): | ||
| yy[i] = math.sqrt(math.sin(x)**2 + math.cos(x)**2) | ||
| return yy | ||
|
|
||
|
|
||
| @timememit | ||
| def formula_numpy(): | ||
| xx = arange(n) | ||
| yy = sqrt(sin(xx)**2 + cos(xx)**2) | ||
| return yy | ||
|
|
||
|
|
||
| if ARG1 == "math": | ||
| formula_array() | ||
| formula_numpy() | ||
|
|
||
| # --- | ||
| # formula_array: 0.1974 s, 1.714844 MiB | ||
| # formula_numpy: 0.1160 s, 1.769531 MiB | ||
| # --- | ||
|
|
||
| # То есть на сложных формулах array ожидаемо проигрывает numpy | ||
| # по времени из за накладных расходов на циклы и переменные python, | ||
| # при этом разница в потреблении памяти сокращается | ||
| # по мере усложнения вычислений. | ||
| # | ||
| # "Серебряной пулей" в данном вопросе мне кажутся библиотеки, | ||
| # компилирующие в бинарный код типа cython и numba, однако их | ||
| # использование накладывает существенные ограничения на этап | ||
| # развертывания приложения и не всегда допустимо. | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. отлично, Сергей |
||
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.
но в целом хорошо