Форум программистов, компьютерный форум, киберфорум
C/С++ под Linux
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.75/4: Рейтинг темы: голосов - 4, средняя оценка - 4.75
 Аватар для SomniPhobia
602 / 439 / 137
Регистрация: 22.11.2017
Сообщений: 1,407

Распараллеливание вычислений с задействованием SymEngine

20.01.2025, 15:28. Показов 2176. Ответов 8
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Всем привет!

Я пишу конструктор нейронных сетей на
ЯП C++,
ОС Linux,
сборщик CMake,
IDE QtCreator (саму библиотеку Qt не юзаю, просто в QtCreator удобно писать код)
SymEngine - библиотека, которая позволяет выполнять символьные вычисления. Например, 2 * x + 4 * y хранить. А потом брать производную и подставлять значения вместо неизвестных. Очень удобно в обучении нейросети (взятие, хранение производных и постановка в них значений)

Код, который я приведу ниже, работает в однопоточном режиме нормально
Я решил распараллелить на n-потоков нахождение градиента по неизвестным
Например, у нас есть неизвестные x1, x2, x3, x4. Если бить на два потока, то первый считает производные по x1 и x2, второй одновременно считает производные по x3 и x4. Потом мёржим их результат

Уже несколько часов бьюсь над тем, как это настроить. SymEngine имеет базовый класс Basic. Basic обёрнутый в умный указатель (RCP) я назвал SE_Basic. В качестве Basic могут выступать разные сущности математики - int, double, сумма, произведение, синус, косинус и так далее. SymEngine выражение хранит в виде графа. Где мы имеем доступ к его коренному узлу, который хранится в SE_Basic - это и есть отражение нашего выражения

Вот код. Попытка распараллелить поиск градиентов по заранее посчитанным производным (dir)
Кликните здесь для просмотра всего текста

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
void compute_gradients(
    const size_t index_thread,
    const size_t index_start,
    const size_t index_finish,
    const std::vector<SE_Basic> &xs_names,
    const map_basic_basic &fun_loss_derivatives,
    const map_basic_basic &memory_values,
    std::vector<SE_Basic> &grad,
    std::mutex &mtx)
{
    std::cout << "Start compute_gradients() in "
              << index_thread << " thread\n";
    std::cout.flush();
    
    const size_t count = index_finish - index_start + 1;
    std::vector<SE_Basic> grad_temp(count);
    
    for (size_t i = index_start; i <= index_finish; ++i)
    {
        
        const SE_Basic &name = xs_names[i];
        
        const SE_Basic &der = fun_loss_derivatives.at(name);
        
        const std::string str = der->__str__();
        const size_t len = str.length();
        
        std::cout << index_thread << " der length: " << len << "\n";
        std::cout.flush();
        
        // const SE_Basic &der_value = der->subs(memory_values);
        
        // std::cout << index_thread << " der_value OK\n";
        // std::cout << der_value << "\n";
        // std::cout.flush();
        
        // grad_temp[i - index_start] = std::move(der_value);
    }
    
    // std::lock_guard<std::mutex> lock(mtx);
    // for (size_t j = index_start; j <= index_finish; ++j)
    // {
    //     grad[j] = grad_temp[j - index_start];
    // }
}
 
void train (/* args */)
{
    // ...
    
    // Threads
    std::mutex mtx;
    const size_t count_threads =
        std::thread::hardware_concurrency();
    std::cout << "count_threads: " << count_threads << "\n";
    std::cout.flush();
    threads = std::vector<std::unique_ptr<std::thread>>(count_threads);
 
    // Запуск потоков
    for (size_t t = 0u; t < count_threads; ++t)
    {
        const size_t start = t * (count_x / count_threads);
        const size_t end = (t == count_threads - 1) ?
            count_x - 1 : start + (count_x / count_threads);
        
        std::cout << start << " -> " << end << "\n";
        std::cout.flush();
        
        std::thread *ptr_thread = new std::thread(
            InSomnia::compute_gradients,
            t,
            start,
            end,
            std::cref(xs_names),
            std::cref(fun_loss_derivatives),
            std::cref(memory_values),
            std::ref(grad),
            std::ref(mtx));
                        
        threads[t] = std::unique_ptr<std::thread>(ptr_thread);
        
    }
 
    std::cout << "Start join\n";
    std::cout.flush();
 
    // Ожидание завершения всех потоков
    for (std::unique_ptr<std::thread> &t : threads)
    {
        t->join();
    }
 
    std::cout << "Finish join\n";
    
    // ...
    
}


Здесь SE_Basic представляет из себя символ. Например, "x1". Такое работает нормально
Code
1
const SE_Basic &name = xs_names[i];
Здесь SE_Basic представляет из себя сумму (Add). Потому что der - это огромное выражение производной по одной из неизвестных.
Code
1
const SE_Basic &der = fun_loss_derivatives.at(name);
Это дерево. А его верхушка - это сумма в переменной der
Вот эта штука не работает при распараллеливании

Любое обращение к der вызывает ошибку и вылет программы. Даже сериализация объекта der (const std::string str = der->__str__()

RTTI symbol not found for class 'SymEngine::Add'
The inferior stopped because it received a signal from the operating system.
Signal name :
SIGABRT
Signal meaning :
Aborted
Пробовал в CMakeLists.txt добавить:
Code
1
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -frtti")
не помогло

Помогите настроить SymEngine для многопоточного выполнения
Спасибо за помощь!
0
Лучшие ответы (1)
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
20.01.2025, 15:28
Ответы с готовыми решениями:

Распараллеливание вычислений
Здравствуйте. Может кто сможет подсказать как мне решить следующую задачу: необходимо распараллелить следующий последовательный код:...

Распараллеливание вычислений
Вычисляю произведение матриц несколькими потоками (количество задаётся пользователем). Потоки &quot;засыпают&quot; на 1 мс. При...

Объясните нубу про распараллеливание вычислений. thread
я попытался цикл разбить пополам на 2 потока, по идее скорость должна увеличиться в 2 раза, но, что мы видим... без потоков #include...

8
 Аватар для SomniPhobia
602 / 439 / 137
Регистрация: 22.11.2017
Сообщений: 1,407
21.01.2025, 08:11  [ТС]
C++
1
2
3
4
5
6
7
8
9
            SymEngine::RCP<const SymEngine::Integer> i0 = SymEngine::integer(8);
            SymEngine::RCP<const SymEngine::Symbol> i1 = SymEngine::symbol("k");
            SymEngine::RCP<const SymEngine::Basic> res = SymEngine::add(i0, i1);
            if (SymEngine::is_a<SymEngine::Add>(*res))
            {
                std::cout << "res is Add\n";
            }
            std::cout << res->__str__() << "\n"; // 8 + k
            std::cout << res->subs({{ SymEngine::symbol("k"), SymEngine::integer(18) }})->__str__() << "\n"; // 26
Однако, такой код выполняется корректно в отдельных потоках (с распараллеливанием)
Здесь мы получаем объект SymEngine::Add, представленный базовым классом SymEngine::Basic
Дальше делаем подстановку (sub) значения в переменную

В моём проблемном фрагменте кода механика та же, только ошибка появляется
Пока не решено. Кто сможет - помогите. Я пока сам покопаю в чём может быть дело

Добавлено через 22 минуты
C++
1
2
            const SE_Basic &der = fun_loss_derivatives.at(name);
            const std::string str = der->__str__();
C++
1
2
            const SE_Basic der = fun_loss_derivatives.at(name);
            const std::string str = der->__str__();
Что в первом, что во втором случае программа вылетает с ошибкой. Вылет на вызове метода __str__(). То есть, даже если я создаю копию объекта в локальную область видимости

Добавлено через 6 минут
Если написать так, без потока, то работает нормально
C++
1
2
3
4
5
6
7
8
9
                InSomnia::compute_gradients(
                t,
                start,
                end,
                std::cref(xs_names),
                std::cref(fun_loss_derivatives),
                std::cref(memory_values),
                std::ref(grad),
                std::ref(mtx));
Как сделать в отдельных потоках?
C++
1
2
3
4
5
6
7
8
9
10
11
12
                std::thread *ptr_thread = new std::thread(
                    InSomnia::compute_gradients,
                    t,
                    start,
                    end,
                    std::cref(xs_names),
                    std::cref(fun_loss_derivatives),
                    std::cref(memory_values),
                    std::ref(grad),
                    std::ref(mtx));
                                
                threads[t] = std::unique_ptr<std::thread>(ptr_thread);
Так вылетает

Добавлено через 3 минуты
Qt disassembler показывает в момент вылета на строку
0x555555766495 <+ 69> 41 ff 90 a8 00 00 00 call *0xa8(%r8)
Добавлено через 56 минут
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
            // Запуск потоков
            for (size_t t = 0u; t < count_threads; ++t)
            {
                const size_t start = t * (count_x / count_threads);
                const size_t end = (t == count_threads - 1) ?
                    count_x - 1 : start + (count_x / count_threads);
                
                std::cout << start << " -> " << end << "\n";
                std::cout.flush();
                
                std::thread *ptr_thread = new std::thread(
                    InSomnia::compute_gradients,
                    t,
                    start,
                    end,
                    std::cref(xs_names),
                    std::cref(fun_loss_derivatives),
                    std::cref(memory_values));
                
                threads[t] = std::unique_ptr<std::thread>(ptr_thread);
                
                usleep(7 * 1000 * 1000);
                
            }
Так работает 1 поток в течение 7 секунд. Но при добавлении второго потока (через 7 секунд) - программа падает
Я пробовал копировать все передаваемые объекты. Тоже не помогло
0
 Аватар для Annemesski
2674 / 1336 / 480
Регистрация: 08.11.2016
Сообщений: 3,693
21.01.2025, 08:43
SomniPhobia, а где сам СимЭнжин взяли? Судя по исходникам, если предполагается использование либы в многопотоке, то её надо собирать с макросом WITH_SYMENGINE_THREAD_SAFE, при объявлении которого инварианты Basic'а заворачиваются в атомики.
0
 Аватар для SomniPhobia
602 / 439 / 137
Регистрация: 22.11.2017
Сообщений: 1,407
21.01.2025, 09:00  [ТС]
Цитата Сообщение от Annemesski Посмотреть сообщение
SomniPhobia, а где сам СимЭнжин взяли?
Взял с Git Hub
Ссылка на репозиторий SymEngine на GitHub

Добавлено через 1 минуту
Цитата Сообщение от Annemesski Посмотреть сообщение
при объявлении которого инварианты Basic'а заворачиваются в атомики
Что такое "атомики"?

Добавлено через 7 минут
Annemesski, так?
cmake
-DCMAKE_BUILD_TYPE=Release
-DWITH_SYMENGINE_THREAD_SAFE=ON
-DWITH_OPENMP=ON ???
-DWITH_MPFR=ON
-DBUILD_TESTS=OFF
-DBUILD_BENCHMARKS=OFF
..
0
 Аватар для Annemesski
2674 / 1336 / 480
Регистрация: 08.11.2016
Сообщений: 3,693
21.01.2025, 09:11
Лучший ответ Сообщение было отмечено Annemesski как решение

Решение

Цитата Сообщение от SomniPhobia Посмотреть сообщение
Что такое "атомики"?
std::atomic - примитивы межпоточной синхронизации, которые, в отличии от мьютексов, позволяют делать многопоточное приложение без блокировок ресурсов (lock-free) что во многих случаях работает значительно быстрее.
Цитата Сообщение от SomniPhobia Посмотреть сообщение
так?
Цитата Сообщение от SomniPhobia Посмотреть сообщение
-DWITH_SYMENGINE_THREAD_SAFE=ON
да, оно.
1
 Аватар для SomniPhobia
602 / 439 / 137
Регистрация: 22.11.2017
Сообщений: 1,407
21.01.2025, 09:18  [ТС]
Цитата Сообщение от Annemesski Посмотреть сообщение
позволяют делать многопоточное приложение без блокировок ресурсов (lock-free) что во многих случаях работает значительно быстрее.
То есть, здесь мне не нужно использовать мьютекс?
C++
1
2
3
4
5
        // std::lock_guard<std::mutex> lock(mtx);
        // for (size_t j = index_start; j <= index_finish; ++j)
        // {
        //     grad[j] = grad_temp[j - index_start];
        // }
Спасибо за ответ!
SymEngine пересобрал, установил. Сейчас проект пересоберу и проверю

Добавлено через 1 минуту
Annemesski, ура! Всё стало выполняться отлично! Спасибо большое за помощь!

Добавлено через 1 минуту

Не по теме:


Как выбрать ответ лучшим? Что-то поменялось на форуме?

0
 Аватар для Annemesski
2674 / 1336 / 480
Регистрация: 08.11.2016
Сообщений: 3,693
21.01.2025, 09:24
Цитата Сообщение от SomniPhobia Посмотреть сообщение
То есть, здесь мне не нужно использовать мьютекс?
не факт что не нужно, атомики не волшебная пилюля которая гарантирует потокобезопасность приложения, с данным флагом гарантируется только потокобезопасность самого типа Basic и его производных, а в приведенном коде вы перебираете некий участок памяти - это уже вам решать защищать ли этот участок памяти мьютексами или какими-нибудь спинлокерами.

Добавлено через 1 минуту
Цитата Сообщение от SomniPhobia Посмотреть сообщение
Как выбрать ответ лучшим?
под сообщением должна быть кнопка (как и раньше)
0
2742 / 1668 / 269
Регистрация: 19.02.2010
Сообщений: 4,413
21.01.2025, 21:29
Цитата Сообщение от SomniPhobia Посмотреть сообщение
SymEngine - библиотека, которая позволяет выполнять символьные вычисления. Например, 2 * x + 4 * y хранить. А потом брать производную и подставлять значения вместо неизвестных.
С датасетом MNIST не баловался? (в смысле - не на задаче XOR же тесты/бенчи гонять, надо что-то посложнее, пусть и из прошлого века)
Если баловался - сколько времени в однопотоке занимает 1 эпоха обучения MLP с одним скрытым слоем из 100 нейронов?
Я чисто ради любопытства интересуюсь.
0
 Аватар для SomniPhobia
602 / 439 / 137
Регистрация: 22.11.2017
Сообщений: 1,407
22.01.2025, 06:22  [ТС]
VTsaregorodtsev, привет!
Не пробовал MNIST пока что
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
22.01.2025, 06:22
Помогаю со студенческими работами здесь

Распараллеливание вычислений
Здравствуйте! Есть симулятор клеточного автомата. &quot;Поле действий&quot; - двухмерный массив класса Cell. Эти клетки имеют кучу параметров, а...

Распараллеливание вычислений
У меня написана программа (Метод Монте-Карло). Что лучше всего использовать, чтобы уменьшить скорость выполнения программы при большой...

Распараллеливание вычислений
Доброго времени форумчане. Может кто-нибудь подсказать по классу Parallel из TPL. Я создал вычисление интеграла Sin(x) от 0 до Пи,...

Распараллеливание вычислений с openmp
Задача в сложении чисел от 1 до N, дополнительное условие не использовать for, и тут у меня возникает проблема с нитями, для двух нитей...

OpenMP распараллеливание вычислений
Выручите, перепробовал уже с бубном плясать не выходит. Матрицу создаю рандомно, элементов должно быть много, а работает раз через раз....


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

Или воспользуйтесь поиском по форуму:
9
Ответ Создать тему
Новые блоги и статьи
Уведомление о неверно выбранном значении справочника
Maks 06.04.2026
Алгоритм из решения ниже реализован на примере нетипового документа "НарядПутевка", разработанного в конфигурации КА2. Задача: уведомлять пользователя, если в документе выбран неверный склад. . .
Установка Qt Creator для C и C++: ставим среду, CMake и MinGW без фреймворка Qt
8Observer8 05.04.2026
Среду разработки Qt Creator можно установить без фреймворка Qt. Есть отдельный репозиторий для этой среды: https:/ / github. com/ qt-creator/ qt-creator, где можно скачать установщик, на вкладке Releases:. . .
AkelPad-скрипты, структуры, и немного лирики..
testuser2 05.04.2026
Такая программа, как AkelPad существует уже давно, и также давно существуют скрипты под нее. Тем не менее, прога живет, периодически что-то не спеша дополняется, улучшается. Что меня в первую очередь. . .
Отображение реквизитов в документе по условию и контроль их заполнения
Maks 04.04.2026
Алгоритм из решения ниже реализован на примере нетипового документа "ПланированиеСпецтехники", разработанного в конфигурации КА2. Данный документ берёт данные из другого нетипового документа. . .
Фото всей Земли с борта корабля Orion миссии Artemis II
kumehtar 04.04.2026
Это первое подобное фото сделанное человеком за 50 лет. Снимок называют новым вариантом легендарной фотографии «The Blue Marble» 1972 года, сделанной с борта корабля «Аполлон-17». Новое фото. . .
Вывод диалогового окна перед закрытием, если документ не проведён
Maks 04.04.2026
Алгоритм из решения ниже реализован на примере нетипового документа "СписаниеМатериалов", разработанного в конфигурации КА2. Задача: реализовать программный контроль на предмет проведения документа. . .
Программный контроль заполнения реквизитов табличной части документа
Maks 02.04.2026
Алгоритм из решения ниже реализован на примере нетипового документа "СписаниеМатериалов", разработанного в конфигурации КА2. Задача: 1. Реализовать контроль заполнения реквизита. . .
wmic не является внутренней или внешней командой
Maks 02.04.2026
Решение: DISM / Online / Add-Capability / CapabilityName:WMIC~~~~ Отсюда: https:/ / winitpro. ru/ index. php/ 2025/ 02/ 14/ komanda-wmic-ne-naydena/
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru