Исследование модели памяти Python
Модель памяти Python играет важную роль в определении того, как объекты хранятся в памяти и как на них делаются ссылки. Понимание этой модели является обязательным, особенно при работе с большими наборами данных, так как это может существенно повлиять на производительность и использование памяти ваших программ на Python. В этом разделе мы сосредоточимся на том, как в Python обрабатываются строковые объекты, и рассмотрим способы оптимизации использования памяти для больших наборов данных.
Повторение строк в наборах данных
Данные о автобусах CTA содержат много повторяющихся значений, таких как названия маршрутов. Повторяющиеся значения в наборе данных могут привести к неэффективному использованию памяти, если их не обрабатывать правильно. Чтобы понять масштабы этой проблемы, давайте сначала проверим, сколько уникальных строк с названиями маршрутов содержится в наборе данных.
Сначала откройте интерпретатор Python. Вы можете сделать это, запустив следующую команду в терминале:
python3
После открытия интерпретатора Python мы загрузим данные о автобусах CTA и найдем уникальные маршруты. Вот код для этого:
import reader
rows = reader.read_csv_as_dicts('ctabus.csv', [str, str, str, int])
## Find unique route names
routes = {row['route'] for row in rows}
print(f"Number of unique route names: {len(routes)}")
В этом коде мы сначала импортируем модуль reader
, который, предположительно, содержит функцию для чтения CSV-файлов в виде словарей. Затем мы используем функцию read_csv_as_dicts
для загрузки данных из файла ctabus.csv
. Второй аргумент [str, str, str, int]
указывает типы данных для каждого столбца в CSV-файле. После этого мы используем генератор множества, чтобы найти все уникальные названия маршрутов в наборе данных и выводим количество уникальных названий маршрутов.
Вывод должен быть таким:
Number of unique route names: 181
Теперь проверим, сколько различных строковых объектов создается для этих маршрутов. Несмотря на то, что есть только 181 уникальное название маршрута, Python может создать новый строковый объект для каждого вхождения названия маршрута в наборе данных. Чтобы проверить это, мы используем функцию id()
для получения уникального идентификатора каждого строкового объекта.
## Count unique string object IDs
routeids = {id(row['route']) for row in rows}
print(f"Number of unique route string objects: {len(routeids)}")
Вывод может вас удивить:
Number of unique route string objects: 542305
Это показывает, что есть только 181 уникальное название маршрута, но более 500 000 уникальных строковых объектов. Это происходит потому, что Python создает новый строковый объект для каждой строки, даже если значения одинаковы. Это может привести к значительному расходу памяти, особенно при работе с большими наборами данных.
Интернирование строк для экономии памяти
Python предоставляет способ "интернировать" (повторно использовать) строки с помощью функции sys.intern()
. Интернирование строк может сэкономить память, если в вашем наборе данных есть много дубликатов строк. Когда вы интернируете строку, Python проверяет, существует ли идентичная строка в пуле интернированных строк. Если да, он возвращает ссылку на существующий строковый объект вместо создания нового.
Давайте продемонстрируем, как работает интернирование строк на простом примере:
import sys
## Without interning
a = 'hello world'
b = 'hello world'
print(f"a is b (without interning): {a is b}")
## With interning
a = sys.intern(a)
b = sys.intern(b)
print(f"a is b (with interning): {a is b}")
В этом коде мы сначала создаем две строковые переменные a
и b
с одинаковым значением без интернирования. Оператор is
проверяет, ссылаются ли две переменные на один и тот же объект. Без интернирования a
и b
- разные объекты, поэтому a is b
возвращает False
. Затем мы интернируем обе строки с помощью sys.intern()
. После интернирования a
и b
ссылаются на один и тот же объект в пуле интернированных строк, поэтому a is b
возвращает True
.
Вывод должен быть таким:
a is b (without interning): False
a is b (with interning): True
Теперь давайте используем интернирование строк при чтении данных о автобусах CTA, чтобы уменьшить использование памяти. Мы также используем модуль tracemalloc
для отслеживания использования памяти до и после интернирования.
import sys
import reader
import tracemalloc
## Start memory tracking
tracemalloc.start()
## Read data with interning for the route column
rows = reader.read_csv_as_dicts('ctabus.csv', [sys.intern, str, str, int])
## Check unique route objects again
routeids = {id(row['route']) for row in rows}
print(f"Number of unique route string objects (with interning): {len(routeids)}")
## Check memory usage
current, peak = tracemalloc.get_traced_memory()
print(f"Current memory usage: {current / 1024 / 1024:.2f} MB")
print(f"Peak memory usage: {peak / 1024 / 1024:.2f} MB")
В этом коде мы сначала запускаем отслеживание памяти с помощью tracemalloc.start()
. Затем мы читаем данные о автобусах CTA с интернированием для столбца маршрута, передав sys.intern
в качестве типа данных для первого столбца. После этого мы снова проверяем количество уникальных строковых объектов маршрутов и выводим текущее и пиковое использование памяти.
Вывод должен быть похожим на следующий:
Number of unique route string objects (with interning): 181
Current memory usage: 189.56 MB
Peak memory usage: 209.32 MB
Давайте перезапустим интерпретатор и попробуем интернировать как строки с названиями маршрутов, так и даты, чтобы увидеть, сможем ли мы еще больше уменьшить использование памяти.
exit()
Запустите Python снова:
python3
import sys
import reader
import tracemalloc
## Start memory tracking
tracemalloc.start()
## Read data with interning for both route and date columns
rows = reader.read_csv_as_dicts('ctabus.csv', [sys.intern, sys.intern, str, int])
## Check memory usage
current, peak = tracemalloc.get_traced_memory()
print(f"Current memory usage (interning route and date): {current / 1024 / 1024:.2f} MB")
print(f"Peak memory usage (interning route and date): {peak / 1024 / 1024:.2f} MB")
Вывод должен показать дальнейшее уменьшение использования памяти:
Current memory usage (interning route and date): 170.23 MB
Peak memory usage (interning route and date): 190.05 MB
Это демонстрирует, как понимание модели памяти Python и использование таких методов, как интернирование строк, может помочь оптимизировать ваши программы, особенно при работе с большими наборами данных, содержащими повторяющиеся значения.
Наконец, выйдите из интерпретатора Python:
exit()