Форум программистов, компьютерный форум, киберфорум
Visual C++
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.59/34: Рейтинг темы: голосов - 34, средняя оценка - 4.59
 Аватар для kirill7785
380 / 5 / 1
Регистрация: 25.07.2021
Сообщений: 57

Оптимизация произведения двух разреженных матриц в CRS формате

01.08.2021, 14:23. Показов 7064. Ответов 5
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// Разреженное матричное умножение двух разреженных матриц.
// Алгоритм Ф. Густавсона IBM 1978.
template <typename doublerealT>
void my_parallel8_sparse_matrix_by_matrix_multiplication_RA(Ak2& Amat,
    Ak1*& P, integer& istartAnew, integer*& istartAnew_m,
    integer*& row_ind_SR,
    integer*& row_ind_ER,
    integer*& row_startA,
    integer numberofcoarcenodes,
    const int iKnumber_thread,
    bool**& hash_table_m,
    integer**& index_visit_m,
    doublerealT**& vector_sum_m,
    integer*& index_size_m,
    integer& nsizeA,
    integer n, integer nnz, integer ilevel,
    bool bprint_mesage_diagnostic,
    integer*& n_a, doublereal AccumulqtorA_m_SIZE8,
    Ak1**& AccumulqtorA_m
    //, integer istartAnew_mem
) {
 
#ifdef _OPENMP
    int i_my_num_core_parallelesation = omp_get_max_threads();
    
 
    integer inum_core = number_cores();
    omp_set_num_threads(inum_core);
    
 
    const integer iSIZE =  n + 2;
 
    // Данные используемые для частичного формирователя суммы.
 
    for (int i_9 = 0; i_9 < iKnumber_thread; ++i_9) {
 
#pragma omp parallel for schedule (static)
        for (integer i_91 = 0; i_91 < iSIZE; ++i_91) {
            hash_table_m[i_9][i_91] = false;// inicialization
        }
        index_size_m[i_9] = 0;
        istartAnew_m[i_9] = 0;
    }
 
    const integer LIMIT_ANEW_m = (integer)((1.0/(1.0* iKnumber_thread)) * AccumulqtorA_m_SIZE8 * nnz + 1);
 
    //omp_set_num_threads(iKnumber_thread); // только 8 потоков.
 
    // Сканируем первый операнд построчно.
    // глобальные переменные не перечисляются.
#pragma omp parallel for schedule (static) 
    for (integer istr = 1; istr <= numberofcoarcenodes; ++istr) {
 
#ifdef _OPENMP 
        const int tid = omp_get_thread_num();
#else
        const int tid = 0;
#endif
 
        // на основе хеш-таблицы. 
 
        // Сканируем текущую i-ую строку поэлементно
        for (integer ii = row_ind_SR[istr]; ii <= row_ind_ER[istr]; ++ii) {
            const integer col_ind = P[ii].j;
            // Сканируем col_ind строку второго операнда
 
            // Общую переменную объявим на уровень выше.
            const doublerealT left_operand = P[ii].aij;
 
            const integer i_1start = row_startA[col_ind];
            const integer i_1end = row_startA[col_ind + 1] - 1;
            for (integer i_1 = i_1start; i_1 <= i_1end; ++i_1) {
 
                const doublerealT right_operand = Amat.aij[i_1];
                const integer iaddind = Amat.j[i_1];
 
                if (hash_table_m[tid][iaddind]) {
 
                    vector_sum_m[tid][iaddind] += left_operand * right_operand;
                }
                else {
                    // Первое добавление.
 
                    index_size_m[tid]++;
 
                    index_visit_m[tid][index_size_m[tid]] = iaddind;
 
                    hash_table_m[tid][iaddind] = true;
 
                    vector_sum_m[tid][iaddind] = left_operand * right_operand;
 
                }
            }
        }
        
        
 
        const integer ism = index_size_m[tid];
        if (nsizeA > istartAnew + ism) {
            if (istartAnew_m[tid] + ism < LIMIT_ANEW_m) {
                for (integer i_6 = 1; i_6 <= ism; ++i_6) {
                    const integer jstr = index_visit_m[tid][i_6];
 
                    hash_table_m[tid][jstr] = false; // инициализируем хеш-таблицу для следующих проходов.
 
                    const doublerealT vs1 = vector_sum_m[tid][jstr];
 
                    // 7 ноября 2016 игнорируем чистые нули:
                    if (fabs(vs1) > 1.0e-37) {
                        // Мы не записываем в матрицу чистый ноль.
                        Ak1 Atemp;
                        Atemp.aij = vs1;
                        Atemp.i = istr;
                        Atemp.j = jstr;
 
 
                        AccumulqtorA_m[tid][istartAnew_m[tid]++] = Atemp;
                    }
                }
            }
            else {
                printf("AccumulqtorA_m index overflow\n");
                system("pause");
            }
        }
        else {
            // Слишком мало памяти выделено под матрицу А и в неё не умещаются все данные.
            // Нужно увеличить объём выделяемой памяти для А и перезапустить приложение.
            printf("Amat lack of memory\n");
            printf("yuo mast increase the size of the matrix Amat and restart solver\n");
            printf("please, press any key to exit.\n");
            system("pause");
            exit(1);
        }
 
 
 
        index_size_m[tid] = 0; // Сброс индикатора, строка обработана.           
 
    }
 
 
    omp_set_num_threads(i_my_num_core_parallelesation);
 
#endif
 
}

Прошу рекомендаций как это можно ускорить на процессоре.

Добавлено через 2 минуты
number_cores() равно iKnumber_thread. Просто разные обозначения одного и того же.
0
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
01.08.2021, 14:23
Ответы с готовыми решениями:

Сложение разреженных матриц в схеме CSR / CRS / Метод разряженных строк / Схема Чанга и Густавсона
Здравствуйте, нужна ваша помощь! Стоит задача &quot;свернуть&quot; две разреженные матрицы в CRS схему (названий у нее много, в заголовке...

Найти сумму двух сильно разреженных матриц
Здравствуйте, не могли бы подсказать в чём ошибки в коде? Вот задание: &quot;Найти сумму двух сильно разреженных матриц A(m,n) и B(m,n),...

Найти сумму двух сильно разреженных матриц
Найти сумму двух сильно разреженных матриц A(m,n) и B(m,n), хранящихся в упакованном виде. Результат получить также в упакованном виде, а...

5
4 / 3 / 1
Регистрация: 28.07.2009
Сообщений: 134
02.08.2021, 08:28
А какие максимально размерности рассматриваются?
0
фрилансер
 Аватар для Алексей1153
6465 / 5679 / 1131
Регистрация: 11.10.2019
Сообщений: 15,122
02.08.2021, 08:48
Цитата Сообщение от kirill7785 Посмотреть сообщение
Ak1 Atemp;
                        Atemp.aij = vs1;
                        Atemp.i = istr;
                        Atemp.j = jstr;
AccumulqtorA_m[tid][istartAnew_m[tid]++] = Atemp;
kirill7785, не знаю версию используемого C++, но если версия ещё не знакома с перемещением, то, думаю, вот так будет быстрее (но компилятор мог это уже и сам сделать )


C++
1
2
3
4
auto& Atemp=AccumulqtorA_m[tid][istartAnew_m[tid]++];
Atemp.aij = vs1;
Atemp.i = istr;
Atemp.j = jstr;
0
2739 / 1665 / 267
Регистрация: 19.02.2010
Сообщений: 4,406
02.08.2021, 09:47
Цитата Сообщение от kirill7785 Посмотреть сообщение
Алгоритм Ф. Густавсона IBM 1978.
В чём смысл такой некрофилии?

Цитата Сообщение от kirill7785 Посмотреть сообщение
Прошу рекомендаций как это можно ускорить на процессоре.
Не писать вручную - а использовать интеловскую MKL. Там вроде было dcsrmm.
0
 Аватар для kirill7785
380 / 5 / 1
Регистрация: 25.07.2021
Сообщений: 57
03.08.2021, 08:23  [ТС]
максимальные размерности зависят от объёма оперативного накопителя на машине. Я пробовал на машине с 256 Гб озу 80 млн (миллионов) неизвестных. Умножение двух разреженных матриц которое приведено выше используется в алгебраическом многосеточном методе для построения разреженной матрицы следующего уровня. Acoarse=R*Afine*P. Размерность Acoarce меньше размерности Afine. Все матрицы A, R, P разреженные (плотных нет).

Добавлено через 5 минут
Это не некрофилия. Это дань уважения к программисту (Фреду Густавсону) придумавшему действительно быстрый алгоритм (идею формировать за раз целую строку результата избавляясь от медленных поисков). Про MKL я не спрашивал, мне нужен исходный код быстрого разреженного матричного умножения, пусть даже это будет с ассемблерными вставками для лучшего использования кеша
0
Just Do It!
 Аватар для XLAT
4211 / 2670 / 655
Регистрация: 23.09.2014
Сообщений: 9,083
Записей в блоге: 3
29.08.2021, 14:32
Цитата Сообщение от kirill7785 Посмотреть сообщение
Прошу рекомендаций как это можно ускорить на процессоре.
для начала я рекомендую вам изготовить стенд
на котором будут присутствовать ТОЛЬКО две разряженные матрицы
с функцией их умножения,

типа так:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
/// C++17
///----------------------------------------------------------------------------|
/// Стенд
/// для анализа возможностей оптимизации умножения двух разряжённых матриц.
/// version(2021.08.29)
///----------------------------------------------------------------------------:
#include <iostream>
#include <vector>
#include <string>
#include <iomanip>
 
typedef double T;
typedef std::vector<T>     vec_t;
typedef std::vector<vec_t> mat_t;
 
///----------------------------------------|
/// Конфиг.                                |
///----------------------------------------:
///--------------------|
/// Размерность.       |
///--------------------:
const size_t Ca = 14   ;
const size_t Ra = 9    ;
 
const size_t Cb = 12   ;
const size_t Rb = Ca   ; /// НЕ ТРОГАТЬ! assert(Ca == Rb)
 
///--------------------|
/// Как инициализируем.|
///--------------------:
const int    SID       = 2021; /// Набор рандома.
const size_t DISCHARGE = 96  ; /// Разряжение(в процентах): 96%
const size_t PREC      =  2  ; /// Точность вывода.
 
///------------------------|
/// Критерий разряженности.|
///------------------------:
inline bool IS_DISCHARGE(T a){return -0.0000000001 < a && a < 0.0000000001;}
 
#define l(v)      std::cout << std::setw(12) << #v << " = " << (v) << "\n";
#define ASSERT(v) if(!(v)) {std::cout << "ERROR: line = " << __LINE__ << '\n';\
                            std::cin.get();}
 
///--------------------------------------|
/// Пока тут умножаем по классике...     |
///--------------------------------------:
static mat_t mult_classic(const mat_t& a, const mat_t& b)
{   mat_t c(a.size(), vec_t(b[0].size(), 0));
 
    if (a[0].size() != b.size())
    {   std::cout << "ERROR: bad data: Cb != Ra\n";
        return mat_t(0, vec_t(0));
    }
 
    for         (size_t i = 0; i < a   .size(); i++)
    {   for     (size_t j = 0; j < b[0].size(); j++)
        {   for (size_t k = 0; k < b   .size(); k++)
            {
                c[i][j] += a[i][k] * b[k][j];
            }
        }
    }
 
    return c;
}
 
///--------------------------------------|
/// Умножаем по СУПЕР-ПУПЕР методе.     <---: Объект исследования ...
///--------------------------------------:
static mat_t mult_super_rocket(const mat_t& a, const mat_t& b)
{   mat_t c(a.size(), vec_t(b[0].size(), 0));
 
    if (a[0].size() == b.size())
    {   std::cout << "ERROR: bad data: Ca == Rb\n";
        return mat_t(0, vec_t(0));
    }
 
    ///-----------------|
    /// ...             |
    ///-----------------:
    /// TODO.
 
    return c;
}
 
///--------------------------------------|
/// Индикация либо на экран, либо в файл.|
/// Если control_big = true,             |
///     то вывод с ограничениями ...     |
///--------------------------------------:
template<class OUT>
void debug(OUT& out, const mat_t& m, const char* s ="", bool control_big = true)
{   out << "\nmat_t " << s
        << "[ " << m.size() << ", " << m[0].size() << " ] --------------------:"
        << "\n";
    int rr = 0;
    for(    const auto& r : m)
    {       if( control_big && ++rr%12 == 0){ out << "  ..."; break;}
        int cc = 0;
        for(const auto& c : r)
        {   if( control_big && ++cc%12 == 0){ out << "  ..."; break;}
            out << std::setprecision(PREC) << std::setw(7) << c << ' ' ;
        }   out                                                 << '\n';
    }       out                                                 << '\n';
}
 
///--------------------------------------|
/// Сравниваем два контейнера.           |
///--------------------------------------:
template<class C>
bool is_equal(const C& a, const C& b)
{   if(a.size() != b.size() || a[0].size() != b[0].size()) return false;
    for(auto ia = a.begin(),
             ib = b.begin(); ia != a.end() && ib != b.end(); ++ia, ++ib)
    {   if(*ia != *ib) return false;
    }
    return true;
}
 
///--------------------------------------|
/// ГПСЧ.                                |
///--------------------------------------:
#include <ctime>
#include <cstdlib>
struct Rand
{   Rand(            ){   srand(static_cast<unsigned>(time(nullptr)));}
    Rand(unsigned sid){   srand(sid)                                 ;}
    int operator(    )(int range_min, int range_max) const
    {   return rand() % (range_max - range_min) + range_min;
    }
    static int get(int range_min, int range_max)
    {   static Rand r(SID); return r(range_min, range_max);
    }
};
 
///--------------------------------------|
/// Рандомно заполняем матрицу.          |
/// с заданным разряжением DISCHARGE.    |
///--------------------------------------:
static void fillrand(mat_t& m)
{   auto R = m   .size();
    auto C = m[0].size();
 
    size_t AMVAL = R * C * (100 - DISCHARGE) / DISCHARGE;
    for(size_t i = AMVAL; i;)
    {   size_t c = static_cast<size_t>(Rand::get(0, static_cast<int>(C)));
        size_t r = static_cast<size_t>(Rand::get(0, static_cast<int>(R)));
 
        if(0.0 == m[r][c]) {m[r][c] = static_cast<T>(r * c * 1234) / 100; --i;}
    }
}
 
///----------------------------------------------------------------------------|
/// Старт тест.
///----------------------------------------------------------------------------:
int main()
{   ///--------|
    /// Конфиг.|
    ///--------:
    l(Ra*Ca)
    l(Rb*Cb)
    l(DISCHARGE)
 
    ///----------|
    /// На входе.|
    ///----------:
    mat_t a(Ra, vec_t(Ca, 0.)); fillrand(a);
    mat_t b(Rb, vec_t(Cb, 0.)); fillrand(b);
 
    ///-------------------------|
    /// Классика умножения.     |
    ///-------------------------:
    mat_t c = mult_classic(a, b);
    mat_t d = mult_classic(a, b);
 
    ASSERT(is_equal(c, d)) // ок
 
    mat_t e = mult_super_rocket(a, b);
  //ASSERT(is_equal(c, e)) /// тут пока ожидается срабатывание ... (TODO)
 
    debug(std::cout, a, "a");
    debug(std::cout, b, "b");
    debug(std::cout, c, "c");
 
    std::cout << "\nTEST FINISH!\n";
    std::cin.get();
}
согласны?
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
29.08.2021, 14:32
Помогаю со студенческими работами здесь

Найти сумму двух сильно разреженных матриц А и В, хранящихся в упакованном виде
Найти сумму двух сильно разреженных матриц А(m,n) и В(m,n), хранящихся в упакованном виде. Результат получить также в упакованном виде, а...

Используя функцию произведения двух матриц, найдите произведение трех матриц А(3,4) В(4,3) С(3,3)
Используя функцию произведения двух матриц, найдите произведение трех матриц А(3,4) В(4,3) С(3,3).

Вычисление степени матрицы, вычисления произведения двух матриц, вычисление суммы двух матриц
Здравствуйте, помогите решить, пожалуйста: Заданы две квадратные матрицы А и В. Вычислить матрицу...

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

Умножение треугольных матриц«Методы обработки разреженных матриц»
Нужно перемножить треугольные матрицы в обычном виде и в свёрнутом. С обычным проблем нет. Доступ к элементам свёрнутой матрицы...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
6
Ответ Создать тему
Новые блоги и статьи
Автозаполнение реквизита при выборе элемента справочника
Maks 27.03.2026
Программный код из решения ниже на примере нетипового документа "ЗаявкаНаРемонтСпецтехники" разработанного в конфигурации КА2. При выборе "Спецтехники" (Тип Справочник. Спецтехника), заполняется. . .
Сумматор с применением элементов трёх состояний.
Hrethgir 26.03.2026
Тут. https:/ / fips. ru/ EGD/ ab3c85c8-836d-4866-871b-c2f0c5d77fbc Первый документ красиво выглядит, но без схемы. Это конечно не даёт никаких плюсов автору, но тем не менее. . . всё может быть. . .
Автозаполнение реквизитов при создании документа
Maks 26.03.2026
Программный код из решения ниже размещается в модуле объекта документа, в процедуре "ПриСозданииНаСервере". Алгоритм проверки заполнения реализован для исключения перезаписи значения реквизита,. . .
Команды формы и диалоговое окно
Maks 26.03.2026
1. Команда формы "ЗаполнитьЗапчасти". Программный код из решения ниже на примере нетипового документа "ЗаявкаНаРемонтСпецтехники" разработанного в конфигурации КА2. В качестве источника данных. . .
Кому нужен AOT?
DevAlt 26.03.2026
Решил сделать простой ланчер Написал заготовку: dotnet new console --aot -o UrlHandler var items = args. Split(":"); var tag = items; var id = items; var executable = args;. . .
Отправка уведомления на почту при изменении наименования справочника
Maks 24.03.2026
Программная отправка письма электронной почты на примере изменения наименования типового справочника "Склады" в конфигурации БП3. Перед реализацией необходимо выполнить настройку системной учетной. . .
модель ЗдравоСохранения 5. Меньше увольнений- больше дохода!
anaschu 24.03.2026
Теперь система здравосохранения уменьшает количество увольнений. 9TO2GP2bpX4 a42b81fb172ffc12ca589c7898261ccb/ https:/ / rutube. ru/ video/ a42b81fb172ffc12ca589c7898261ccb/ Слева синяя линия -. . .
Midnight Chicago Blues
kumehtar 24.03.2026
Такой Midnight Chicago Blues, знаешь?. . Когда вечерние улицы становятся ночными, а ты не можешь уснуть. Ты идёшь в любимый старый бар, и бармен наливает тебе виски. Ты смотришь на пролетающие. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru