Нашли или выдавили из себя код, который нельзя назвать нормальным,
на который без улыбки не взглянешь?
Не торопитесь его удалять или рефакторить, — запостите его на
говнокод.ру, посмеёмся вместе!
//библиотеки cuda_runtime.h и device_launch_parameters.h
//для работы с cyda
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include<vector>
#include<string>//для getline
#include <stdio.h>
#include<fstream>
using namespace std;
__global__ void Upload_to_GPU(unsigned long long *Number,unsigned long long *Stepn, bool *Stop,unsigned long long *INPUT,unsigned long long *max) {
int thread = threadIdx.x;
unsigned long long MAX_DEGREE_OF = max[0];
int X = thread;
unsigned long long Calculated_number = 1;
unsigned long long Current_degree_of_number = 2;
unsigned long long Original_numberP = INPUT[0];
Stop[thread] = false;
bool BREAK = false;
if (X!=0&&X!=1) {
while (!BREAK) {
if (Current_degree_of_number <= MAX_DEGREE_OF) {
Calculated_number = 1;
for (int counter = 0; counter < Current_degree_of_number; counter++) {
Calculated_number *=X;
}
if (Calculated_number == Original_numberP) {
Stepn[thread] = Current_degree_of_number;
Number[thread] = X;
Stop[thread] = true;
BREAK = true;
}
Current_degree_of_number++;
}
else { BREAK = true; }
}
}
}
Как видно из таблицы, время выполнения алгоритма на GPU немного больше, чем на CPU.
Однако, отмечу, что вовремя работы алгоритма использующего для вычислений GPU загрузка
им CPU, в Диспетчере задач, не превышала 30%, в то время как алгоритм использующий для
вычислений CPU, загружал его на 68-85%, что в свою очередь иногда приводило к замедлению
других приложений.
Стоит отметить, что cyda имеет ограничения по количеству запускаемых потоков,
поэтому в обоих алгоритмах я взял одинаковое количество потоков, равное 1000.
Да, особенно это помогает, когда в коде для CPU очень эффективно проводится ожидание конца работы:
thread *T = new thread[size];
Running_thread_counter = 0;
for (int i = 0; i < size; i++) {
T[i] = thread(Upload_to_CPU, Number, Stepn, Stop, INPUT, max, i);
T[i].detach();
}
while (Running_thread_counter < size - 1);//дождаться завершения выполнения всех потоков
P. S. Перевёл алгоритм автора на «Python» с «Numba», третий с конца результат, который у него считался 16 секунд, вычислил за 65.
>>> @numba.jit(nopython=True)
... def calc():
... for x in range(2, 1001): # У него каждый поток проверяет x с собственным номером
... for deg in range(1, 8500):
... if x ** deg == N:
... yield (x, deg)
...
>>> start = time.time(); res = list(calc()); end = time.time(); print(end - start)
0.1561572551727295
>>> res
[(108, 4)]
>>> start = time.time(); res = list(calc()); end = time.time(); print(end - start)
0.06783914566040039
>>> start = time.time(); res = list(calc()); end = time.time(); print(end - start)
0.08180832862854004
>>> start = time.time(); res = list(calc()); end = time.time(); print(end - start)
0.06485199928283691
Без «JIT» ВМ Питона просто берёт вот эти вот опкоды и тупо их исполняет до победного в одном внутреннем евале.
UPD: Только сейчас заметил: он же в самом горячем месте цикла делает LOAD_GLOBAL, то есть идёт в словарь и ищет в нём глобалку по имени. Какой анскилл )))
Ма-те-ма-ти-ки любят «Питон», потому что числа в нём переменного размера (bigint). Можно записать целое число с огромным количеством знаков и не думать о типах данных.
Barabashkad сегодня в 19:13
для начала, CPU код можно еще оптимизиорвать если использовать векторные AVX инструкции
но и для GPU… можно не мерять время выделения памяти и копирования…
и тогда картина станет более многогранной и пестрой :-)
И ни один петух не заметил, что для CPU-bound задачи цыплёнок запускает 1000 потоков…
А где здесь cpu bound? Для брутфорса корня данных почти не надо передавать на видюху, число для проверки можно по индексам треда получить. В обратную сторону разве что мешок флажков придётся передать. И то я думаю можно reduce прям на видюхе сделать за несколько проходов.
Для чисел от 0 до 999 (по одному на поток) и степеней от 1 до N (N задаётся в конкретном тесте) найти все пары (число, степень), которые при вычислении «число^степень» дают заданное в тесте число.
Дождаться пока все треды на gpu закончат и пробежаться по массиву в 4 килобайта - не такая уж тяжёлая задача для cpu (на входе массив нинужен т.к. номер треда можно заюзать). Параллелится этот брутфорс вроде неплохо, память не напрягает (не то чтобы не было более красивого решения без брутфорса).
З.Ы. А, понял, ты про версию на cpu, где тыща обычных тредов поднимается. Ну да, выглядит пиздецово. С другой стороны в них IO нету, ось не будет контекст сильно часто переключать. Так что сойдёт. Вон даже gpu обогнать умудрились.
Да, про них.
> ось не будет контекст сильно часто переключать
Я недавно ради интереса одну CPU-bound задачку распараллелил на 4 потока (чтобы все ядра забить), померял, добавил ещё один и получил просадку в полтора раза. Добавил ещё то ли два, то ли три — просело во что-то вроде десяти раз. Так что не всё так однозначно (да и по заверениям автора, у него во время работы CPU-версии проц был загружен на 85%, лол).
Ну и как бы вся задача — это несколько миллионов умножений, это считается реально за миллисекунды, если, конечно, считать, а не ебать планировщик.
Ну это делается прозрачно для шедулера всё равно: с его точки зрения это отдельные ядра, просто физически они делят какие-то куски цпу, и пока один питух что-то считает на ALU, другой может из памяти читать, вроде бы.
Хотя шедулер ведь может знать, что ядра на самом деле не настоящие, как-то это использовать.
Вообще же ГТ включает операционка. Некоторые операцинки его принципиально не включают из за безопастности)
OpenBSD has disabled Intel's hyper-threading technology, citing security concerns – seemingly, Spectre-style concerns. As detailed in this mailing list post, OpenBSD maintainer Mark Kettenis wrote that “SMT (Simultaneous Multi Threading) implementations typically share TLBs and L1 caches between threads
Если треды из разных процессов, то можно как-то сайд эффект атакой что-то спиздить...
А вот вопрос: нужно ли защищать треды друг от друга?
На юниксах обычно треды имеют одинаковые (общие для процесса) права.
А на windows бывает имперсонация, когда тред получает дескриптор безопасности пользователя, так что в одном веб-сервере, на который зашли админ и гость, могут быть треды, работающие от имени гостя и админа. Если запустить их на одном ядре, то выполнение левого кода от имени гостя поможет хакеру угадать что лежит в "L1 caches" для админа, не?
Эм, ну ты же не даёшь гостю запускать его произвольный код в своём процессе под этой имперсонацией (это совсем ССЗБ, имхо, он же тебе в память насрёт без всякой спектры)? Ты просто для ядерного API выступаешь от его лица и всё. А код то твой в обоих тредах.
Кстати, давайте сравним модели безопасности у UNIX и Windows.
У классического UNIX каждый процесс может иметь user id, причем user id можно заместить, так что есть real и effective user id.
У файлов есть user id и group id и пермишены для них (я сейчас не говорю про ACL, это нашлёпка os-specific).
Некоторые драйверы могут реально проверять user id пославшего им ioctl процесса, и слать нахуй, если это не root.
У Windows каждый объект имеет ACL с указанием кому что можно, а каждый процесс (и даже тред) имеет 1 или более токенов безопасности.
Такой токен имеет user sid и список привелегий (их миллион). Но такие токкены можно передавать другим процессам через IPС.
Выдача токена называется logon session, и таких сессий может быть тоже миллион (на каждый вызов LogonAsUser), причем они все могут иметь разные привилегии и разные типы (interactive session, service итд)
CUDA-конпелятор, который юзает чел из статьи, как раз таки позволяет в одном исходнике и для cpu и для gpu писать...
Но у gpu очень специфичная архитектура и как попало под неё писать нельзя, иначе все терафлопсы вылетят в трубу. Сложный код, где много переменных, много нелокальных обращений к памяти, циклы переменной длины с внезапными бряками и т.п. видюхи очень плохо переносят. Им нужны тысячи и миллионы простых, независимых, однообразных задач.
Всё хуже. "Треды" исполняются группами по 16 штук, по сути это SIMD. Сам понимаешь, что control flow у них один на всех. Поэтому пока половина исполняет then, вторая пинает хуи. А потом наоборот. Ну и выйти из цикла они могут только всей группой.
А у памяти большое латенси и маленькие кеши, что-то в духе сотни килобайт на "тыщу ядер" (один мультипроцессор). Поэтому рандомный доступ куда попало очень дорого обходится.
Ну и регистры статически распределяются, поэтому чем больше переменных - тем меньше тредов ты сможешь загнать на однин мультипроцессор.
Ага, настолько царские, что иногда не влезает. У меня на гитхабе есть множество жюлиа на шейдерах. На старых интеловских карточках тупо не конпелируется из-за огромного цикла с бряком.
у обычных PE и ELF вроде бы есть секция .text, которую загрузчик мапит в сегмент code, причем странички всегда чистые (потому что самомодифицирующийся код это чаще всего харам) и их можно выкинуть из памяти
Видюшным задачам не особо нужна когерентность. У тебя на каждом "проходе" вход и выход алгоритма с разными буферами работают. Поэтому кешам на входе можно не париться о кешах на выходе. А после прохода можно и флашнуть, один хер кеши маленькие.
Хочу в крестах возможность дефиницировать все поля объекта в теле конструктора. Почему я должен явно заполнять поля сразу после двоеточия инициализатором? Пусть бы они были неинициализированные пока (а не с дефолтными значнеиями), я бы просто обещал, что к концу конструктора я их чем-то заполню. Или так можно, и я просто мудак?
Ну а если не заполню, так сам пидор, и сломал RAII
class Foo final
{
const int m_foo; //хочу const
Foo()
{
// Do all
m_foo = 12;
}
}
Хочешь конст - ебись с двоеточием. Без конста можно и просто в теле конструктора присвоить. Жаль конечно, что они как в джавке не сделали, конпелятор вполне мог бы проверить, что ты ровно 1 раз присваиваешь и соблюдаешь правильный порядок.
З.Ы. Можно и int m_foo = 12; если кресты свежие. Но с const'ом это не имеет особого смысла, только память зря тратить. Зачем тебе поле в котором всегда одно и то же?
А без конст могу?
>Можно и int m_foo = 12;
нет, не хочу.
Хочу m_foo = BlueMoon() ? 12 : 42;
>Жаль конечно, что они как в джавке не сделали,
в джавке поля все равно имеют дефолтные значения: int будет 0, поинтер будет null.
Можно даже передать this кому-то в констуркторе и получить багра: см leak of this
Ага, но в теле конструктора это будет уже присваивание, а не конструирование как в инициализаторе. Не то чтобы это реально мешало, но помнить об этом стоит.
ну как не мешает? вызовется ДРУГОЙ конструктор же?
а рантаайм не заполнит сначала деволтным значением?
Я хочу заполнить объектом, который НЕ ПОДДЕРЖИВАЕТ копирование (оператор присваивания), потому что я автор этого класса, и не хочу поддерживать копирование
Для int'ов и прочего говна - нет. Там будет мусор пока что-нибудь не присвоишь.
У объектов сработает дефолтный конструктор, а потом ты в них присвоишь новое значение. Реальная проблема только если дефолтный конструктор тяжёлый, с побочками или его вообще нет. Но такого на практике почти не бывает.
Я не хочу дефолтный конструктор, он может быть тяжелый, да.
В общем понятно: проще сделать инициализатор через статическую функицю (или в анонимном неймспейсе) и потечь.
Вообще заметил, что в крестах создавая класс всегда нужно ответить на два вопроса:
* поддерживает ли он копирование/присваивание? Если да, то как?
* поддерживает ли он move? Если да, то как?
в других языках нет
А еще создавая шаблон всегда нужно понимать когда и как он будет инстанциироваться.
Это да. Я вот вижу, что в питоне какое-то поведение не описано в доке. И вообще не юзаю его. А питонисты говорят: "чувак, да просто запусти и посмотри что будет".
именно) я знаю, что если что-то работает у меня в джавке, то скорее всего и на другой ос оно будет работать так же. нет смысла искать параграф в JLS.
Исключение составляют разве что гонки при несинхронизированных потоках и атомарность некоторых операций
А в крестах нифига: если у тебя что-то работает, то оно может тупо сломаться при обновлении компилятора, если ты не знаешь какой параграф спеки это гарантирует
Причем джава и джаваскрипт и сишарп хотябы имеют спеку, а питон например -- нет. Как работает сипайтон -- так и правильно.
Какая глубокая мысль
P. S. Перевёл алгоритм автора на «Python» с «Numba», третий с конца результат, который у него считался 16 секунд, вычислил за 65.
…миллисекунд.
какой багор) бизи вейт
Мда. Выебать преждевременного оптимизатора наивным кодом на питоне - это бесценно.
JS со своим float тоже был бы медленный, когдаб не джит.
Без «JIT» ВМ Питона просто берёт вот эти вот опкоды и тупо их исполняет до победного в одном внутреннем евале.
UPD: Только сейчас заметил: он же в самом горячем месте цикла делает LOAD_GLOBAL, то есть идёт в словарь и ищет в нём глобалку по имени. Какой анскилл )))
А явисты, <как> долбоёбы, ставят буковку "f" после дробных чисел )))
И ни один петух не заметил, что для CPU-bound задачи цыплёнок запускает 1000 потоков…
это логично, если у тебя 1000 ядер
З.Ы. Я код не читал если что, там много буков.
По модулю 2**64?
Показалось сначала, что по модулю UB. Какой багор )))
Дождаться пока все треды на gpu закончат и пробежаться по массиву в 4 килобайта - не такая уж тяжёлая задача для cpu (на входе массив нинужен т.к. номер треда можно заюзать). Параллелится этот брутфорс вроде неплохо, память не напрягает (не то чтобы не было более красивого решения без брутфорса).
> ось не будет контекст сильно часто переключать
Я недавно ради интереса одну CPU-bound задачку распараллелил на 4 потока (чтобы все ядра забить), померял, добавил ещё один и получил просадку в полтора раза. Добавил ещё то ли два, то ли три — просело во что-то вроде десяти раз. Так что не всё так однозначно (да и по заверениям автора, у него во время работы CPU-версии проц был загружен на 85%, лол).
Ну и как бы вся задача — это несколько миллионов умножений, это считается реально за миллисекунды, если, конечно, считать, а не ебать планировщик.
А делать потоков больше, чем ядер, вообще смысла нету кмк: только шедулер зря грузить
На cpu нету. А вот на gpu есть. Там шедулер намного тоньше, другие треды смогут поработать пока первая группа ответ от памяти ждёт.
Вообще, Задержки при обращении к памяти прозрачны для шедулера, потому профилировать их можно только с помощью специальных фич процессора
Хотя шедулер ведь может знать, что ядра на самом деле не настоящие, как-то это использовать.
Вообще же ГТ включает операционка. Некоторые операцинки его принципиально не включают из за безопастности)
OpenBSD has disabled Intel's hyper-threading technology, citing security concerns – seemingly, Spectre-style concerns. As detailed in this mailing list post, OpenBSD maintainer Mark Kettenis wrote that “SMT (Simultaneous Multi Threading) implementations typically share TLBs and L1 caches between threads
Если треды из разных процессов, то можно как-то сайд эффект атакой что-то спиздить...
Вроде гипертреды стараются отдавать тредам одного процесса. Чтобы они могли "share TLBs and L1 caches" а не мешаться друг другу.
На юниксах обычно треды имеют одинаковые (общие для процесса) права.
А на windows бывает имперсонация, когда тред получает дескриптор безопасности пользователя, так что в одном веб-сервере, на который зашли админ и гость, могут быть треды, работающие от имени гостя и админа. Если запустить их на одном ядре, то выполнение левого кода от имени гостя поможет хакеру угадать что лежит в "L1 caches" для админа, не?
nginx всегда работает от www, так что ssh ключи рута он не спиздит.
А IIS (со включенной имперсонацией) может от имени доменного админа
>ядерного API
Токен можно использовать для обращения по сети (если клиент выдал такое разрешение)
Веб сервер может пойти в базу данных от имени пользователя, например.
Если у тебя AD, то это вообще довольно удобно.
Хотя если хакер запустил случайный код от имени админа, то уже не важно, на каком ядре он выполняется
У классического UNIX каждый процесс может иметь user id, причем user id можно заместить, так что есть real и effective user id.
У файлов есть user id и group id и пермишены для них (я сейчас не говорю про ACL, это нашлёпка os-specific).
Некоторые драйверы могут реально проверять user id пославшего им ioctl процесса, и слать нахуй, если это не root.
У Windows каждый объект имеет ACL с указанием кому что можно, а каждый процесс (и даже тред) имеет 1 или более токенов безопасности.
Такой токен имеет user sid и список привелегий (их миллион). Но такие токкены можно передавать другим процессам через IPС.
Выдача токена называется logon session, и таких сессий может быть тоже миллион (на каждый вызов LogonAsUser), причем они все могут иметь разные привилегии и разные типы (interactive session, service итд)
Какая модель лучше?
"Про бойлерплейт с явным выделением и возвратом памяти я уже вообще молчу."
Но у gpu очень специфичная архитектура и как попало под неё писать нельзя, иначе все терафлопсы вылетят в трубу. Сложный код, где много переменных, много нелокальных обращений к памяти, циклы переменной длины с внезапными бряками и т.п. видюхи очень плохо переносят. Им нужны тысячи и миллионы простых, независимых, однообразных задач.
Всё хуже. "Треды" исполняются группами по 16 штук, по сути это SIMD. Сам понимаешь, что control flow у них один на всех. Поэтому пока половина исполняет then, вторая пинает хуи. А потом наоборот. Ну и выйти из цикла они могут только всей группой.
А у памяти большое латенси и маленькие кеши, что-то в духе сотни килобайт на "тыщу ядер" (один мультипроцессор). Поэтому рандомный доступ куда попало очень дорого обходится.
Ну и регистры статически распределяются, поэтому чем больше переменных - тем меньше тредов ты сможешь загнать на однин мультипроцессор.
у обычных PE и ELF вроде бы есть секция .text, которую загрузчик мапит в сегмент code, причем странички всегда чистые (потому что самомодифицирующийся код это чаще всего харам) и их можно выкинуть из памяти
интересно, как это работает гпу
С некогерентными L1 кешами видеокарт это вообще пиздецом попахивает...
какой багор ))
Ну а если не заполню, так сам пидор, и сломал RAII
я думал тут сайт, где сразу отвечают на любой вопрос про крестам, даже на очень глупый.
Кстати, у меня были кейсы, когда мне отвечали про лоу-левел питушню на говнокоде моментально, а на SO бы ни в жизни не ответили
З.Ы. Можно и int m_foo = 12; если кресты свежие. Но с const'ом это не имеет особого смысла, только память зря тратить. Зачем тебе поле в котором всегда одно и то же?
>Можно и int m_foo = 12;
нет, не хочу.
Хочу m_foo = BlueMoon() ? 12 : 42;
>Жаль конечно, что они как в джавке не сделали,
в джавке поля все равно имеют дефолтные значения: int будет 0, поинтер будет null.
Можно даже передать this кому-то в констуркторе и получить багра: см leak of this
а рантаайм не заполнит сначала деволтным значением?
Я хочу заполнить объектом, который НЕ ПОДДЕРЖИВАЕТ копирование (оператор присваивания), потому что я автор этого класса, и не хочу поддерживать копирование
У объектов сработает дефолтный конструктор, а потом ты в них присвоишь новое значение. Реальная проблема только если дефолтный конструктор тяжёлый, с побочками или его вообще нет. Но такого на практике почти не бывает.
В общем понятно: проще сделать инициализатор через статическую функицю (или в анонимном неймспейсе) и потечь.
Вообще заметил, что в крестах создавая класс всегда нужно ответить на два вопроса:
* поддерживает ли он копирование/присваивание? Если да, то как?
* поддерживает ли он move? Если да, то как?
в других языках нет
А еще создавая шаблон всегда нужно понимать когда и как он будет инстанциироваться.
Короче, все время приходится думать
Ну дык, это как машина с ручной коробкой. Не хочешь думать - юзай джаву.
Кресты меняют психику, и делают из тебя излишне подозрительного перца
Это да. Я вот вижу, что в питоне какое-то поведение не описано в доке. И вообще не юзаю его. А питонисты говорят: "чувак, да просто запусти и посмотри что будет".
Исключение составляют разве что гонки при несинхронизированных потоках и атомарность некоторых операций
А в крестах нифига: если у тебя что-то работает, то оно может тупо сломаться при обновлении компилятора, если ты не знаешь какой параграф спеки это гарантирует
Причем джава и джаваскрипт и сишарп хотябы имеют спеку, а питон например -- нет. Как работает сипайтон -- так и правильно.
Оператор мувающего присваивания прикрути ему. По аналогии с мув конструктором.
У кого-то остались бэкапы всего того, что было насрано через guest8?