C не хватает памяти в python, осталось много места

#python #c #python-3.x #numpy #out-of-memory

#python #c #python-3.x #numpy #нехватка памяти

Вопрос:

Я работаю над проектом, в котором мне нужно найти ближайших соседей встраиваемого вектора. Недавно я пытаюсь использовать новый инструмент Google ANN для сканирования github. Я смог создать объект searcher и сериализовать его для небольшого набора данных (~ 200 тыс. строк с 512 значениями) с помощью следующего кода

 import numpy as np
import scann
data = np.random.random((200k,512))
data = data / np.linalg.norm(data, axis=1)[:, np.newaxis]
searcher = scann.scann_ops_pybind.builder(data, 10, "dot_product").tree(
    num_leaves=2000, num_leaves_to_search=100, training_sample_size=250000).score_ah(
    2, anisotropic_quantization_threshold=0.2).reorder(100).build()
searcher.serialize('./scann')
 

Но когда я попробовал с реальным набором данных (~ 48 млн строк с 512 значениями), я получил:

 In [11]: searcher.serialize('scann/')
---------------------------------------------------------------------------
MemoryError                               Traceback (most recent call last)
<ipython-input-11-71a5ef71c81f> in <module>
----> 1 searcher.serialize('scann/')

~/.local/lib/python3.6/site-packages/scann/scann_ops/py/scann_ops_pybind.py in serialize(self, artifacts_dir)
     70
     71   def serialize(self, artifacts_dir):
---> 72     self.searcher.serialize(artifacts_dir)
     73
     74

MemoryError: std::bad_alloc
 

Размер .npy файла для набора данных составляет ~ 90 ГБ, и на моем компьютере осталось не менее 500 ГБ свободной оперативной памяти и 1 ТБ свободного диска:

введите описание изображения здесь

Я использую Ubuntu 18.04.5 LTS и Python 3.6.9. Модуль Scann был установлен с помощью Pip.

Есть идеи о том, что может происходить?

Спасибо за помощь

[править / править код] После комментария @MSalters я провел некоторое тестирование и выяснил, что если подлежащий сериализации набор данных содержит более 16777220 байт (2 ^ 24 4), он завершается ошибкой с bad_alloc сообщением. Я до сих пор не знаю, почему это происходит…

[edit2] Я создаю SCANN из исходного кода и добавляю в него несколько отладочных отпечатков. Ошибка, похоже, в этой строке:

 vector<uint8_t> storage(hash_dim * expected_size);
 

и если я напечатаю это так:

 std::cout << hash_dim <<  " " << expected_size <<"n" << std::flush;
std::cout << hash_dim * expected_size <<"n" << std::flush;
vector<uint8_t> v2;
std::cout << v2.max_size() << "n" << std::flush;
vector<uint8_t> storage(hash_dim * expected_size);
std::cout << "after storage creationn" << std::flush;
 

Тогда я получаю;

 256 8388608
-2147483648
9223372036854775807
 

Комментарии:

1. Один очевидный вопрос. Используете ли вы 32-битную или 64-битную версию Python? 32-разрядная версия может использовать только 2 ГБ оперативной памяти.

2. Вывод platform.architecture() есть ('64bit', 'ELF') . Я думал, что это может быть проблемой, но мне только что удалось сериализовать набор данных с 5 млн строк и 11 ГБ, но не удалось с 10 млн строк и 21 ГБ

3. Получить a bad_alloc в Linux довольно сложно. Linux обычно делает вид, что памяти достаточно, даже если ее на самом деле нет. Это называется чрезмерной ответственностью, контролируемой proc/sys/vm/overcommit_memory . Результатом использования недоступной памяти является то, что Linux сначала замедляется (замена), а затем убивает случайный процесс. A bad_alloc больше намекает на ошибку программирования, такую как попытка выделить -1 байт.

4. @MSalters Я провел некоторое тестирование, и кажется, что если размер моего набора данных меньше 16777126 (2 ^ 24), он работает просто отлично. Итак, я думаю, вы правы, и где-то в коде СКАНИРОВАНИЯ размер выделяемой памяти переполняет переменную, а затем пытается выделить отрицательный объем памяти. Теперь мне нужно просто выяснить, где

5. Похоже на ошибку в SCANN или способ ее использования (не эксперт в этом, поэтому не могу сказать наверняка). Тип обоих hash_dim и expected_size есть int , поэтому hash_dim * expected_size переполняется. size_t или int64_t работал бы лучше.

Ответ №1:

Похоже, в ScaNN есть отчет о существующей проблеме # 427 с аналогичной ошибкой.

Основываясь на выводе -2147483648 for std::cout << hash_dim * expected_size , мы можем сделать вывод, что hash_dim * expected_size переполняется.

Глядя на источник, мы видим тип обоих hash_dim и expected_size есть int .

Так что, вероятно, тип хотя бы одного из них должен был быть int64_t , long long или, что еще лучше, size_t .

Глядя на источник ScaNN, кажется, что может быть больше мест, которые могли бы извлечь выгоду из типа данных, специально предназначенного для хранения size ( size_t ) вместо an int .