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

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

01.08.2021, 14:23. Показов 7028. Ответов 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
6449 / 5643 / 1129
Регистрация: 11.10.2019
Сообщений: 15,029
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
2625 / 1636 / 266
Регистрация: 19.02.2010
Сообщений: 4,348
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
4202 / 2658 / 654
Регистрация: 23.09.2014
Сообщений: 8,965
Записей в блоге: 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
Ответ Создать тему
Новые блоги и статьи
Восстановить юзерскрипты Greasemonkey из бэкапа браузера
damix 15.01.2026
Если восстановить из бэкапа профиль Firefox после переустановки винды, то список юзерскриптов в Greasemonkey будет пустым. Но восстановить их можно так. Для этого понадобится консольная утилита. . .
Изучаю kubernetes
lagorue 13.01.2026
А пригодятся-ли мне знания kubernetes в России?
Сукцессия микоризы: основная теория в виде двух уравнений.
anaschu 11.01.2026
https:/ / rutube. ru/ video/ 7a537f578d808e67a3c6fd818a44a5c4/
WordPad для Windows 11
Jel 10.01.2026
WordPad для Windows 11 — это приложение, которое восстанавливает классический текстовый редактор WordPad в операционной системе Windows 11. После того как Microsoft исключила WordPad из. . .
Classic Notepad for Windows 11
Jel 10.01.2026
Old Classic Notepad for Windows 11 Приложение для Windows 11, позволяющее пользователям вернуть классическую версию текстового редактора «Блокнот» из Windows 10. Программа предоставляет более. . .
Почему дизайн решает?
Neotwalker 09.01.2026
В современном мире, где конкуренция за внимание потребителя достигла пика, дизайн становится мощным инструментом для успеха бренда. Это не просто красивый внешний вид продукта или сайта — это. . .
Модель микоризы: классовый агентный подход 3
anaschu 06.01.2026
aa0a7f55b50dd51c5ec569d2d10c54f6/ O1rJuneU_ls https:/ / vkvideo. ru/ video-115721503_456239114
Owen Logic: О недопустимости использования связки «аналоговый ПИД» + RegKZR
ФедосеевПавел 06.01.2026
Owen Logic: О недопустимости использования связки «аналоговый ПИД» + RegKZR ВВЕДЕНИЕ Введу сокращения: аналоговый ПИД — ПИД регулятор с управляющим выходом в виде числа в диапазоне от 0% до. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru