У меня есть разреженная матрица, содержащая только нули и единицы в качестве записей (и, например, с формой 32k x 64k и 0,01% ненулевых записей и без шаблонов для использования с точки зрения того, где находятся ненулевые записи). Матрица известна во время компиляции. Я хочу выполнить умножение матрицы на вектор (по модулю 2) с неразреженными векторами (неизвестными во время компиляции), содержащими 50% единиц и нулей. Я хочу, чтобы это было эффективно, в частности, я пытаюсь использовать тот факт, что матрица известна во время компиляции.
Хранение матрицы в эффективном формате (сохранение только индексов единиц) всегда будет занимать несколько мегабайт памяти, и прямое встраивание матрицы в исполняемый файл кажется мне хорошей идеей. Моей первой идеей было просто автоматически сгенерировать код C++, который просто присваивает всем элементам результирующего вектора сумму правильных элементов ввода. Это выглядит так:
constexpr std::size_t N = 64'000;
constexpr std::size_t M = 32'000;
template<typename Bit>
void multiply(const std::array<Bit, N> &in, std::array<Bit, M> &out) {
out[0] = (in[11200] + in[21960] + in[29430] + in[36850] + in[44352] + in[49019] + in[52014] + in[54585] + in[57077] + in[59238] + in[60360] + in[61120] + in[61867] + in[62608] + in[63352] ) % 2;
out[1] = (in[1] + in[11201] + in[21961] + in[29431] + in[36851] + in[44353] + in[49020] + in[52015] + in[54586] + in[57078] + in[59239] + in[60361] + in[61121] + in[61868] + in[62609] + in[63353] ) % 2;
out[2] = (in[11202] + in[21962] + in[29432] + in[36852] + in[44354] + in[49021] + in[52016] + in[54587] + in[57079] + in[59240] + in[60362] + in[61122] + in[61869] + in[62610] + in[63354] ) % 2;
out[3] = (in[56836] + in[11203] + in[21963] + in[29433] + in[36853] + in[44355] + in[49022] + in[52017] + in[54588] + in[57080] + in[59241] + in[60110] + in[61123] + in[61870] + in[62588] + in[63355] ) % 2;
// LOTS more of this...
out[31999] = (in[10208] + in[21245] + in[29208] + in[36797] + in[40359] + in[48193] + in[52009] + in[54545] + in[56941] + in[59093] + in[60255] + in[61025] + in[61779] + in[62309] + in[62616] + in[63858] ) % 2;
}
Это на самом деле работает (требуется много времени для компиляции). Однако на самом деле он кажется очень медленным (более чем в 10 раз медленнее, чем то же разреженное векторно-матричное умножение в Джулии), а также значительно увеличивает размер исполняемого файла, чем я считал необходимым. Я пробовал это как с std::array
, так и с std::vector
, а также с отдельными записями (представленными как Bit
), равными bool
, std::uint8_t
и int
, но никакого прогресса, о котором стоит упомянуть. Я также попытался заменить модуль и добавить XOR. В заключение скажу, что это ужасная идея. Однако я не уверен, почему - настолько ли размер кода замедляет его? Исключает ли такой код оптимизацию компилятора?
Альтернативы пока не пробовал. Следующей идеей, которая у меня есть, является сохранение индексов в виде постоянных массивов времени компиляции (по-прежнему дающих мне огромные файлы .cpp
) и их циклическое выполнение. Первоначально я ожидал, что это приведет к тому, что оптимизация компилятора сгенерирует тот же двоичный файл, что и из моего автоматически сгенерированного кода C++. Как вы думаете, стоит ли попробовать (думаю, я все равно попробую в понедельник)?
Другая идея состоит в том, чтобы попытаться сохранить входной (и, возможно, также выходной?) вектор в виде упакованных битов и выполнить вычисления таким образом. Я ожидаю, что нельзя обойти множество битовых сдвигов или операций и, и в конечном итоге это будет медленнее и хуже в целом.
Есть ли у вас другие идеи о том, как это можно сделать?