Почему мой процесс Python не может получить доступ ко всей памяти?

Это зависит от конкретной реализации, которую использует ваш дистрибутив (, а иногда и от конфигурации ), но один из:

  • Алфавитный, по некоторому определению алфавитный, вероятно, LC _ALL=C. Это в основном простой и распространенный способ реализации. Вот почему числа имеют префикс 0 -, чтобы убедиться, что простая старая сортировка помещает их в правильном порядке.
  • Параллельно. Вот что делал startparбез -Mеще во времена Debian Lenny.
  • Числа не имеют значения, порядок определяется заголовками LSB, объявляющими зависимости :, что делает insserv/ startpar -M. Или что делают другие системы инициализации (, например, systemd )уровни совместимости SysV.

Вам нужно будет проверить документацию по вашей системе или код (, который в SysV init часто представлял собой сценарии оболочки, поэтому проверить их относительно легко; вы можете узнать, где он находится, прочитав/etc/inittab).

2
22.01.2021, 22:41
1 ответ

Чтение текстового файла с текстовыми данными 20G (давайте предположим, что он закодирован как utf -8 )все равно сначала потребуется прочитать содержимое файла как двоичные фрагменты, прежде чем данные можно будет декодировать как строку Python.

Предположим, у нас есть текстовый файл с именем utf-8_text.txt, содержимое которого состоит из следующих 4 символов utf -8 символов без новой строки:

aä猫

Следующий сценарий должен немного прояснить ситуацию.

import codecs
import os
import sys
import unicodedata

# some unicode characters from different codepoint ranges
unicode_characters = dict(
    character="a",
    umlaut="ä",
    cat="猫",
    monster=""
)

# add "utf-16", "utf-32" if you want to test with other encodings
encodings = ["utf-8"]

for key, character in unicode_characters.items():
    character_mul = character * 1000
    print(
        f"{key}: {repr(character)} "
        f"(len={len(character)} {'U+%04X' % ord(character)} "
        f"{unicodedata.category(character)} "
        f"{unicodedata.name(character)}) "
        f"/ {repr(character)}*1000 (len={len(character_mul)})"
    )
    for encoding in encodings:
        encoded_character = character.encode(encoding=encoding)
        print(
            f"\t{encoding} of {repr(character)}: "
            f"{encoded_character} "
            f"(len={len(encoded_character)} "
            f"sizeof={sys.getsizeof(encoded_character)})"
        )
        encoded_character_mul = character_mul.encode(encoding=encoding)
        print(
            f"\t{encoding} of {repr(character)}*1000: "
            f"{encoded_character_mul[:12]}... "
            f"(len={len(encoded_character_mul)} "
            f"sizeof={sys.getsizeof(encoded_character_mul)})"
        )
        print()

# python text needs to be decoded after it was read,
# this causes memory exhaustion
for encoding in encodings:
    with open(f"{encoding}_test.txt", mode='rb') as reader:
        stat_result = os.fstat(reader.fileno())
        binary = reader.read()
        binary_mul = binary * 1000
        text = codecs.decode(binary, encoding=encoding)
        text_mul = text * 1000
        print(
            f"{encoding}_test.txt: {binary} = {repr(text)}\n"
            f"\tfile_size: {stat_result.st_size}\n"
            f"\t{repr(text)} len: {len(binary)} as bytes / {len(text)} as str\n"
            f"\t{repr(text)} sizeof: {sys.getsizeof(binary)} as bytes / "
            f"{sys.getsizeof(text)} as str\n"
        )
        print(
            f"\t1000*{repr(text)} len: {len(binary_mul)} as bytes / "
            f"{len(text_mul)} as str\n"
            f"\t1000*{repr(text)} sizeof: {sys.getsizeof(binary_mul)} as bytes / "
            f"{sys.getsizeof(text_mul)} as str"
        )

Когда мы запускаем этот скрипт, он сначала выводит некоторую информацию об использовании памяти для каждого отдельного символа Юникода, включая копию этого символа, повторяющуюся 1000 раз. Затем мы видим использование памяти при чтении utf-8_text.txtв двоичном режиме.

character: 'a' (len=1 U+0061 Ll LATIN SMALL LETTER A) / 'a'*1000 (len=1000)
        utf-8 of 'a': b'a' (len=1 sizeof=34)
        utf-8 of 'a'*1000: b'aaaaaaaaaaaa'... (len=1000 sizeof=1033)

umlaut: 'ä' (len=1 U+00E4 Ll LATIN SMALL LETTER A WITH DIAERESIS) / 'ä'*1000 (len=1000)
        utf-8 of 'ä': b'\xc3\xa4' (len=2 sizeof=35)
        utf-8 of 'ä'*1000: b'\xc3\xa4\xc3\xa4\xc3\xa4\xc3\xa4\xc3\xa4\xc3\xa4'... (len=2000 sizeof=2033)

cat: '猫' (len=1 U+732B Lo CJK UNIFIED IDEOGRAPH-732B) / '猫'*1000 (len=1000)
        utf-8 of '猫': b'\xe7\x8c\xab' (len=3 sizeof=36)
        utf-8 of '猫'*1000: b'\xe7\x8c\xab\xe7\x8c\xab\xe7\x8c\xab\xe7\x8c\xab'... (len=3000 sizeof=3033)

monster: '' (len=1 U+1F47E So ALIEN MONSTER) / ''*1000 (len=1000)
        utf-8 of '': b'\xf0\x9f\x91\xbe' (len=4 sizeof=37)
        utf-8 of ''*1000: b'\xf0\x9f\x91\xbe\xf0\x9f\x91\xbe\xf0\x9f\x91\xbe'... (len=4000 sizeof=4033)

utf-8_test.txt: b'a\xc3\xa4\xe7\x8c\xab\xf0\x9f\x91\xbe' = 'aä猫'
        file_size: 10
        'aä猫' len: 10 as bytes / 4 as str
        'aä猫' sizeof: 43 as bytes / 92 as str

        1000*'aä猫' len: 10000 as bytes / 4000 as str
        1000*'aä猫' sizeof: 10033 as bytes / 16076 as str

Функцияsys.getsizeofсообщает размер объекта Python в памяти, и, как мы видим, простой файл в кодировке utf -8 с 4 символами Юникода может занимать всего 10 байт на диске, но в конечном итоге всего 92 байта в памяти как str, сильно завышено.

Это, конечно, не является реальным увеличением для приведенного выше варианта использования и, вероятно, вызвано некоторым бухгалтерским учетом Python или интернированием строк. Более реалистично, у нас есть больший вход (, такой как вышеупомянутый файл 20G ), и для решения этого мы сообщаем об использовании памяти версией файла из 4000 символов Юникода (1000 повторенийaä猫).

Теперь 1000 раз aä猫переводится в 10 033 байта в памяти как необработанныеbytes(т. е. 10 000 байт на диске как aä猫в utf -8 имеет 10 байт + книга -с сохранением )и 16 076 байт в памяти как str.

Обобщая, 20 ГБ необработанного utf -8 байт на диске увеличивают память примерно на 60% в этом примере до 32 ГБ, как str, что не заполнило бы память, но оба они вместе должны храниться в памяти плюс некоторая книга декодера -сохраняется при декодировании необработанных байтов в utf -8 str, что, вероятно, является причиной использования всей памяти для машинных спецификаций, приведенных выше.

Проверка реализации io.TextIOWrapper.read(), кажется, подтверждает мою догадку.

3
18.03.2021, 22:35

Теги

Похожие вопросы