избегать копирования во время преобразования массива между numpy и mxnet - PullRequest
0 голосов
/ 31 декабря 2018

Я хочу уменьшить шаг копирования памяти во время конвейера обработки данных.

Я хочу сделать следующее:

  1. Создать некоторые данные из пользовательской библиотеки C

  2. Подача сгенерированных данных в модель MXNet, работающую на графическом процессоре.

На данный момент мой конвейер выполняет следующие действия:

  1. Создание непрерывного массива C с помощью np.empty(...).

  2. Получение указателя на массив Numpy с помощью np.ndarray.__array_interface__

  3. Вызовите библиотеку C из python (через ctypes), чтобы заполнить массив numpy.

  4. Преобразовать массив numpy в mxnet NDArray, это будет copy базовый буфер памяти.

  5. Упакуйте NDArray s в экземпляр mx.io.DataBatch, затем введите в модель.

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

Я заметил, что mx.io.DataBatch может принимать только список mx.ndarray.NDArray s как параметр data и label, но not NumPy массивов. Это работает до тех пор, пока вы не загрузите его в модель. С другой стороны, у меня есть библиотека C, которая может записывать данные непосредственно в массив, смежный с C.

Я бы хотелизбегайте копирования в память на шаге 3. Один из возможных способов - получить необработанный указатель на буфер NDArray, при этом полностью игнорируя numpy.Но что бы ни работало.

1 Ответ

0 голосов
/ 31 декабря 2018

Я нашел хакерский способ добиться этого.Вот небольшой пример.

from ctypes import *
import numpy as np
import mxnet as mx

m = mx.ndarray.zeros((4,4))
m.wait_to_read() # make sure the data is allocated

c_uint64_p = POINTER(c_uint64)

handle= cast(m.handle, c_uint64_p) # NDArray*
ptr_  = cast(handle[0], c_uint64_p) # shared_ptr<Chunk>
dptr = cast(ptr_[0], POINTER(c_float)) # shandle.dptr

n = np.ctypeslib.as_array(dptr, shape=(4,4)) # m and n will share buffer

Я вывел приведенный выше код, взглянув на исходный код MxNet C ++.Некоторое объяснение:

Сначала обратите внимание на атрибут NDArray.handle.Это c_void_p.Прочитайте исходный код Python, вы узнаете, что это NDArrayHandle.Теперь погрузитесь в код src/c_api/c_api_ndarray.cc, он будет интерпретирован как NDArray*.

В дереве исходного кода перейдите к include/mxnet/ndarray.h и найдите класс NDArray.Первое поле:

/*! \brief internal data of NDArray */
std::shared_ptr<Chunk> ptr_{nullptr};

Проверка Chunk, которая является структурой, определенной внутри NDArray, мы видим:

  /*! \brief the real data chunk that backs NDArray */
  // shandle is used to store the actual values in the NDArray
  // aux_handles store the aux data(such as indices) if it's needed by non-default storage.
  struct Chunk {
    /*! \brief storage handle from storage engine.
               for non-default storage, shandle stores the data(value) array.
     */
    Storage::Handle shandle;

Наконец, shandle определено в include/mxnet/storage.h:

  struct Handle {
    /*!
     * \brief Pointer to the data.
     */
    void* dptr{nullptr};

Написание небольшой программы показывает, что sizeof(shared_ptr<some_type>) равно 16. Основываясь на этом вопросе, мы можем предположить, что shared_ptr состоит из двух указателей.Нетрудно понять, что первый указатель - это указатель на данные.Собрав все воедино, все, что нужно, - это разыменование двух указателей.


На нижнем сайте этот метод нельзя использовать в производственной среде или в крупных проектах.Он может сломаться в будущем выпуске или привести к серьезным ошибкам и дырам в безопасности.

...