Более быстрое умножение матриц в C #

У меня есть небольшой проект на C #, связанный с матрицами. Я обрабатываю большие объемы данных, разбивая их на блоки длиной n, обрабатывая блоки как векторы и умножая на матрицу Вандермонда **. Проблема в том, что в зависимости от условий размер патронов и соответствующая матрица Вандермонда ** могут меняться. У меня есть общее решение, которое легко читать, но слишком медленно:

    public byte[] addBlockRedundancy(byte[] data) {
        if (data.Length!=numGood) D.error("Expecting data to be just "+numGood+" bytes long");

        aMatrix d=aMatrix.newColumnMatrix(this.mod, data);
        var r=vandermonde.multiplyBy(d);
        return r.ToByteArray();
    }//method

Это может обрабатывать около 1/4 мегабайта в секунду на моем i5 U470 @ 1,33 ГГц. Я могу сделать это быстрее, вручную вставив матричное умножение:

        int o=0;
        int d=0;
        for (d=0; d<data.Length-numGood; d+=numGood) {
            for (int r=0; r<numGood+numRedundant; r++) {
                Byte value=0;
                for (int c=0; c<numGood; c++) {
                    value=mod.Add(value, mod.Multiply(vandermonde.get(r, c), data[d+c]));
                }//for
                output[r][o]=value;
            }//for
            o++;
        }//for

Это может обрабатывать около 1 мегабайта в секунду.

(Обратите внимание, что «мод» выполняет операции над GF (2 ^ 8) по модулю моего любимого неприводимого многочлена.)

Я знаю, что это может быть намного быстрее: в конце концов, матрица Вандермонда ** в основном нули. Я должен иметь возможность создать процедуру или найти процедуру, которая может взять мою матрицу и вернуть оптимизированный метод, который будет эффективно умножать векторы на данную матрицу, но быстрее. Затем, когда я даю этой программе матрицу Вандермонда 5x5 (единичная матрица), просто не нужно выполнять арифметические операции, и исходные данные просто копируются.

** Обратите внимание: то, что я использую термин «Вандермонд», на самом деле я имею в виду матрицу идентичности с добавленным количеством строк из матрицы Вандермонда (см. Комментарии). Эта матрица прекрасна тем, что в ней все нули, и потому, что если вы удалите достаточно строк (по вашему выбору), чтобы сделать ее квадратной, это будет обратимая матрица. И, конечно же, я хотел бы использовать ту же процедуру для преобразования любой из этих инвертированных матриц в оптимизированную серию инструкций.

Как я могу сделать это умножение матриц быстрее?

Спасибо!

(отредактировано, чтобы исправить мою ошибку с матрицей Вандермонда)


person Kyle Lahnakoski    schedule 29.12.2010    source источник
comment
Единичная матрица или любая матрица с нулями не является матрицей Вандермонда согласно определениям, приведенным в en.wikipedia. org / wiki / Vandermonde_matrix или mathworld.wolfram.com/VandermondeMatrix.html ( или GVL), но вы говорите, что в вашей матрице есть нули. Вы можете уточнить свое определение?   -  person Pete Kirkham    schedule 29.12.2010
comment
Моя ошибка. Матрица, которую я ищу, - это не Вандермонд, а, скорее, матрица идентичности с добавлением Вандермонда в качестве дополнительных строк. См. cs.tau.ac.il/~ohadrode/slides/ ReedSolomon.pdf стр. 11.   -  person Kyle Lahnakoski    schedule 29.12.2010


Ответы (4)


Возможно, вы сможете определить матричный интерфейс и построить реализации во время выполнения, используя Reflection.Emit.

IMatrix m = MatrixGenerator.CreateMatrix(data);

m.multiplyBy(...)

Здесь MatrixGenerator.CreateMatrix создаст индивидуальную реализацию IMatrix с полным развертыванием цикла и дальнейшим сокращением кода (0 ячеек, идентификаторов и т. Д.). MatrixGenerator.CreateMatrix может кэшировать матрицы, чтобы избежать их повторного создания позже для того же набора данных.

person Nicolas Repiquet    schedule 29.12.2010

Я видел решения, использующие Reflection.Emit, и я видел решения, использующие TPL. Настоящий ответ здесь - в большинстве случаев вы хотите использовать существующую неуправляемую библиотеку, такую ​​как Intel MKL, через P / Invoke. В качестве альтернативы, если вы используете графический процессор, вы можете использовать подход GPGPU, который будет работать намного быстрее.

И да, SSE вместе с многоядерной обработкой - это самый быстрый способ сделать это на CPU. Но я бы не рекомендовал писать свой собственный алгоритм - вместо этого поищите что-нибудь, что уже есть. Скорее всего, это будет библиотека C ++, возможно, с оболочкой C #.

person Dmitri Nesteruk    schedule 29.12.2010
comment
+1. .NET не использует SSE, но управляемый C ++ может обернуть собственную библиотеку C ++, а затем использовать неуправляемые методы / код для выполнения операций SSE. Это настолько быстро, насколько не хватает использования графического процессора, который не будет более эффективным для отдельных преобразований (вы используете некоторое время для загрузки данных / загрузки результатов - это для ТЯЖЕЛЫХ параллельных вещей). - person TomTom; 29.12.2010
comment
Меня беспокоит SSE или GPU, это поле GF (2 ^ 8), которое я использую для выполнения этих операций. Я сомневаюсь, что они поддерживают такую ​​математику. - person Kyle Lahnakoski; 29.12.2010

Хотя это не ускорит математику, вы могли бы по крайней мере использовать все свои ядра с Parallel.For в .Net 4.0. Ссылка Microsoft

person Joel Lucsy    schedule 29.12.2010

С математической точки зрения

Вы можете посмотреть на собственные пространства, собственные векторы, собственные значения. Я не уверен, что делает ваше приложение и поможет ли оно.

Вы можете посмотреть LU Decomposition.

Все вышеперечисленные темы можно найти в Википедии.

С точки зрения программирования

Вы можете попробовать SIMD, но они предназначены для матриц 4x4 для однородных преобразований трехмерного пространства, в основном для компьютерной графики.

Вы можете написать специальные алгоритмы для наиболее распространенных измерений.

Возможно ли использование SSE в C #?

person EnabrenTane    schedule 29.12.2010
comment
К сожалению, матрицы 4x4, используемые в 3D-графике, слишком малы и не работают с нужным мне полем GF (2 ^ 8). - person Kyle Lahnakoski; 29.12.2010