Как получить память, используемую многомерным вектором - PullRequest
0 голосов
/ 14 апреля 2020

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

#include <iostream>
#include <vector>
#include <random>
#include <chrono>

class Timer
{
private:
    std::chrono::time_point<std::chrono::high_resolution_clock> start_time;
public:
    Timer(bool auto_start=true)
    {
        if (auto_start)
        {
            start();
        }
    }
    void start()
    {
        start_time = std::chrono::high_resolution_clock::now();
    }
    float get_duration()
    {
        std::chrono::duration<float> duration = std::chrono::high_resolution_clock::now() - start_time;
        return duration.count();
    }
};

class Network
{
public:
    std::vector<std::vector<std::vector<float>>> weights;
    std::vector<std::vector<std::vector<float>>> deriv_weights;
    std::vector<std::vector<float>> biases;
    std::vector<std::vector<float>> deriv_biases;
    std::vector<std::vector<float>> activations;
    std::vector<std::vector<float>> deriv_activations;
};

Network create_network(std::vector<int> layers)
{
    Network network;
    network.weights.reserve(layers.size() - 1);
    int nodes_in_prev_layer = layers[0];
    for (unsigned int i = 0; i < layers.size() - 1; ++i)
    {
        int nodes_in_layer = layers[i + 1];
        network.weights.push_back(std::vector<std::vector<float>>());
        network.weights[i].reserve(nodes_in_layer);
        for (int j = 0; j < nodes_in_layer; ++j)
        {
            network.weights[i].push_back(std::vector<float>());
            network.weights[i][j].reserve(nodes_in_prev_layer);
            for (int k = 0; k < nodes_in_prev_layer; ++k)
            {
                float input_weight = float(std::rand()) / RAND_MAX;
                network.weights[i][j].push_back(input_weight);
            }
        }
        nodes_in_prev_layer = nodes_in_layer;
    }
    return network;
}

int main() 
{
    Timer timer;
    Network network = create_network({784, 800, 16, 10});
    std::cout << timer.get_duration() << std::endl;
    std::cout << sizeof(network) << std::endl;
    std::cin.get();
}

Ответы [ 2 ]

1 голос
/ 14 апреля 2020

Я недавно обновил наш производственный код нейронной сети до AVX-512; это определенно реальный производственный код. Ключевой частью наших оптимизаций является то, что каждая матрица не a std::vector, а 1D-выровненный массив AVX. Даже без выравнивания AVX мы видим огромное преимущество в переходе к одномерному массиву, поддерживающему каждую матрицу. Это означает, что доступ к памяти будет полностью последовательным, что намного быстрее. Размер тогда будет (rows*cols)*sizeof(float).

Мы сохраняем смещение как первый полный ряд. Обычно это реализуется с помощью префикса ввода элементом 1.0, но для нашего кода AVX мы используем смещение в качестве начальных значений для операций FMA (Fused Multiply-Add). Т.е. в псевдокоде result=bias; for(input:inputs) result+=(input*weight). Это сохраняет вход также AVX-выровненным.

Поскольку каждая матрица используется по очереди, вы можете безопасно иметь std::vector<Matrix> layers.

0 голосов
/ 14 апреля 2020

Как цитата из { ссылка }:
Вектор сохраняет свои элементы во внутренне выделенном массиве памяти. Вы можете сделать это:

sizeof(std::vector<int>) + (sizeof(int) * MyVector.size())

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


В вашем случае имеет значение только фактически выделенный массив памяти, поскольку вы просто обращаетесь к ним. Также помните о том, как вы обращаетесь к памяти.
Чтобы написать код, дружественный к кешу, я настоятельно рекомендую прочитать этот пост SO: { ссылка }

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...