Nano Hash - криптовалюты, майнинг, программирование

Неожиданно высокий уровень использования памяти в Google App Engine

У меня есть приложение Python GAE, которое хранит данные в каждом экземпляре, и использование памяти намного выше, чем я ожидал. В качестве иллюстрации рассмотрим этот тестовый код, который я добавил в свое приложение:

from google.appengine.ext import webapp

bucket = []

class Memory(webapp.RequestHandler):
    def get(self):
        global bucket
        n = int(self.request.get('n'))
        size = 0
        for i in range(n):
            text = '%10d' % i
            bucket.append(text)
            size += len(text)
        self.response.out.write('Total number of characters = %d' % size)

Вызов этого обработчика со значением переменной запроса n приведет к тому, что экземпляр добавит в свой список n строк, каждая из которых имеет длину 10 символов.

Если я вызову это с n=1 (чтобы все было загружено), а затем проверю использование памяти экземпляра на рабочем сервере, я увижу цифру 29,4 МБ. Если я затем вызову его с n=100000 и снова проверю, использование памяти подскочило до 38,9 МБ. То есть объем моей памяти увеличился на 9,5 МБ, чтобы хранить всего один миллион символов, что почти в десять раз больше, чем я ожидал. Я считаю, что символы потребляют только один байт каждый, но даже если это неправильно, нам еще предстоит пройти долгий путь. Накладные расходы на структуру списка, конечно, не могут объяснить этого. Я попытался добавить явный вызов сборки мусора, но цифры не изменились. Что мне не хватает, и есть ли способ уменьшить занимаемую площадь?

(Между прочим, я попытался использовать набор вместо списка и обнаружил, что после вызова с n=100000 использование памяти увеличилось на 13 МБ. Это говорит о том, что накладные расходы набора для 100000 строк на 3,5 МБ больше, чем для списков, что также намного больше. больше, чем ожидалось.)


Ответы:


1

Я знаю, что действительно опаздываю на здешнюю вечеринку, но это совсем не удивительно...

Рассмотрим строку длины 1:

s = '1'

Это довольно мало, верно? Может где-то порядка 1 байта? Неа.

>>> import sys
>>> sys.getsizeof('1')
38

Таким образом, каждая создаваемая вами строка связана примерно с 37 байтами служебных данных (все эти строковые методы должны храниться где-то).

Кроме того, для вашего процессора обычно наиболее эффективно хранить элементы на основе «размера слова», а не размера байта. Во многих системах «слово» составляет 4 байта...). Я не знаю наверняка, но я не удивлюсь, если распределитель памяти python тоже использует трюки, чтобы он работал довольно быстро.

Кроме того, не забывайте, что списки представлены в виде перераспределенных массивов (чтобы предотвратить огромные проблемы с производительностью каждый раз, когда вы .append). Вполне возможно, что когда вы составляете список из 100 000 элементов, python на самом деле выделяет указатели для 110 000 или более.

Наконец, что касается set -- это, вероятно, довольно легко объясняется тем фактом, что set еще более перераспределены, чем list (в конце концов, им нужно избегать всех этих хеш-коллизий). В итоге они имеют большие скачки в использовании памяти по мере роста размера набора, чтобы иметь достаточно свободных слотов в массиве, чтобы избежать коллизий хэшей:

>>> sys.getsizeof(set([1]))
232
>>> sys.getsizeof(set([1, 2]))
232
>>> sys.getsizeof(set([1, 2, 3]))
232
>>> sys.getsizeof(set([1, 2, 3, 4]))
232
>>> sys.getsizeof(set([1, 2, 3, 4, 5]))
232
>>> sys.getsizeof(set([1, 2, 3, 4, 5, 6]))  # resize!
744
20.07.2016

2

Накладные расходы структуры списка не объясняют то, что вы видите напрямую, но фрагментацию памяти< /а> делает. И строки имеют ненулевые накладные расходы с точки зрения базовой памяти, поэтому подсчет длины строк будет значительно занижен.

05.03.2012
  • Я согласен, что количество символов может значительно занижать использование памяти, но в десять раз?? Вы хотите сказать, что память в моем экземпляре на 10% состоит из реальных данных и на 90% из-за фрагментации? Если это правда, проблема может заключаться не столько в фрагментации, сколько в чрезвычайно неэффективных алгоритмах распределения памяти в Google App Engine. malloc() еще никогда не был таким плохим. 07.03.2012
  • Мой опыт работы с malloc отличается. Посмотрите еще раз, что делает этот пример кода. Это вызывает периодический рост базовой области памяти, которая поддерживает список, имея как минимум возможность поддерживать старую и новую копию в рабочем состоянии, пока копируются данные. И это создает новые строки между ними. Это приведет к фрагментации этой кучи. 10.03.2012

  • 3

    Я не эксперт, но это интересный вопрос. Похоже, это скорее проблема управления памятью Python, чем проблема GAE. Вы пытались запустить его локально и сравнить использование памяти на вашем локальном сервере dev_appserver и на GAE? Это должно указывать, является ли это платформой GAE или просто python.

    Во-вторых, код Python, который вы использовали, прост, но не очень эффективен, понимание списка вместо цикла for должно быть более эффективным. Это должно немного уменьшить использование памяти:

    ''.join([`%10d` % i for i in range(n)])
    

    Под одеялом ваша растущая строка должна постоянно перераспределяться. Каждый раз, проходя цикл for, остается отброшенная строка. Я ожидал, что запуск сборщика мусора после цикла for должен был очистить лишние строки.

    Попробуйте запустить сборщик мусора перед проверкой использования памяти.

    import gc
    gc.collect()
    return len(gc.get_objects())
    

    Это должно дать вам представление о том, не очистил ли сборщик мусора некоторые лишние строки.

    07.03.2012

    4

    Во многом это ответ на dragonx.

    Пример кода существует только для иллюстрации проблемы, поэтому я не беспокоился о небольшой эффективности. Вместо этого меня беспокоит, почему приложение потребляет примерно в десять раз больше памяти, чем фактических данных. Я могу понять, что есть некоторые накладные расходы памяти, но настолько?

    Тем не менее, я попытался использовать понимание списка (без объединения, чтобы соответствовать моему оригиналу), и использование памяти немного увеличилось, с 9,5 МБ до 9,6 МБ. Возможно, это в пределах погрешности. Или, возможно, большое выражение range() поглощает его; он выпущен, без сомнения, но я думаю, что лучше использовать xrange(). При соединении переменная экземпляра устанавливается в одну очень длинную строку, и объем памяти, что неудивительно, снижается до разумных 1,1 МБ, но это совсем не тот случай. Вы получите те же 1,1 МБ, просто установив переменную экземпляра в один миллион символов без использования понимания списка.

    Я не уверен, что согласен с тем, что с моей петлей «осталась отброшенная строка». Я считаю, что строка добавляется в список (по ссылке, если можно так выразиться) и никакие строки не отбрасываются.

    Я уже пробовал явную сборку мусора, как говорится в моем первоначальном вопросе. Никакой помощи там.

    Вот показательный результат. Изменение длины строк с 10 на какое-то другое число вызывает пропорциональное изменение использования памяти, но там также есть константа. Мои эксперименты показывают, что для каждой строки, добавленной в список, приходится 85 байт служебных данных, независимо от длины строки. Это стоимость строк или помещения строк в список? Я склоняюсь к последнему. Создание списка из 100 000 None занимает 4,5 МБ или около 45 байт на None. Это не так плохо, как для строк, но все же довольно плохо. И, как я уже говорил, для наборов это хуже, чем для списков.

    Хотел бы я понять, почему накладные расходы (или фрагментация) были такими плохими, но неизбежный вывод, похоже, заключается в том, что большие коллекции небольших объектов чрезвычайно дороги. Вы, вероятно, правы, что это больше проблема Python, чем проблема GAE.

    08.03.2012
  • Привет, я извиняюсь, я полностью неправильно прочитал ваш пример кода и не обратил внимания на тот факт, что ведро было списком и было далеко от базы. 11.03.2012
  • Если вы используете среду выполнения 2.7, вы можете использовать sys.getsizeof(), чтобы узнать размер ваших объектов. Использование памяти выше, чем ожидалось, связано с python и не относится к GAE. Строка Python — это строковый объект, а не просто строка символов. Попробуйте выполнить dir(a) в оболочке python, чтобы дать вам представление о том, что прикреплено к строковому объекту, тогда вы поймете, что размер соответствует. 11.03.2012
  • Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..