thedeemon: (office)
[personal profile] thedeemon
Увидел на днях у afiskon'a гостевой пост с реализацией на Go игрушечной задачки про подбор пароля по его MD5 хэшу. По условию в пароле 5 символов из алфавита 0..9a..z, перебор надо распараллелить по ядрам. Там же есть ссылки на предыдущие инкарнации (на хаскеле и эрланге), в них я сейчас не вчитывался. А еще тот же пост увидел eao197 и сделал свой вариант на C++ с использованием его любимого SObjectizer'a. Я решил присоединиться к флэшмобу и сделал вариант на D. Варианты на С++ и D - прямые переводы с Го, поэтому за объяснением рекомендую смотреть пост про гошный вариант, там подробно расписано. Полные исходники:
Go (84 lines)
C++ (155 lines)
D (48 lines)

На моем ноуте с i3 и двумя ядрами по два гипертреда вариант на Go отрабатывает за 13 секунд, вариант на D - за 6 секунд. Оба грузят проц под 100%, имеют по 5 потоков и памяти едят меньше 2 мегов. Что занятно, на этом примере получился очень резкий контраст между компиляторами D: при сборке LDC получается вдвое быстрее Го, а при сборке DMD - вдвое медленнее.

Что касается кода, в варианте на С++ просто почерк очень размашистый, при другом форматировании было бы заметно компактнее, но и синтаксического шума, конечно, тоже заметно больше чем в остальных вариантах. В Го рабочие потоки получают задания и посылают ответ через каналы, которые они шарят меж собой. В D использован модуль из стандартной библиотеки std.concurrency, где нет в явном виде каналов, а вместо этого потоки могут посылать друг другу сообщения аки процессы в эрланге. В частности, у меня тут главный поток создает массив Tid'ов (индентификаторов потоков), запустив нужное число воркеров, а потом раскидывает им задания:

workers[i % $].send( Part( pass.idup, b ) );


$ внутри [] означает длину массива, так номер задачки i мапится на номер рабочего. Встречающиеся в коде .idup и .dup создают иммутабельную и мутабельную копии массива соответственно (посылать между потоками без глубокого копирования разрешается лишь иммутабельные данные).

У каждого потока свой почтовый ящик, откуда он полученные сообщения достает функцией receive, передавая ей разные функции-обработчики, receive по типу этих обработчиков выбирает подходящий, кому отдать данные из сообщения. Соответственно, главный цикл рабочего потока, по мотивам гошного варианта, выглядит
while(true)	
  receive( (Part p) { 
    ... тут вложенная функция, вызывающаяся при получении структуры Part


Как и в гошном варианте, когда главный поток получает сообщение с ответом, он его выводит и завершается. Как я понял, в Go остальные потоки при этом тут же умирают в мучениях. В D при завершении главного потока потоки-воркеры продолжают свою работу. При таком бесконечном цикле ожидания сообщений, как здесь, в какой-то момент мэйлбокс потока оказывается пуст, и в этот момент если receive видит, что родительский поток помер, эта функция бросает определенное исключение. На него можно при желании осмысленно реагировать, но в моем случае я просто говорю тихонько завершиться:
scope(failure) return;

Такое вот баловство.

Date: 2015-04-09 01:27 pm (UTC)
From: [identity profile] sober-space.livejournal.com
Тут надо учесть, что все суслики пользуются go fmt, что резко увеличивает количество строк за счет форматирования. Если бы был аналогично устроенный d fmt, у Вас тоже строк было бы побольше.
Edited Date: 2015-04-09 01:27 pm (UTC)

Date: 2015-04-09 03:29 pm (UTC)
From: [identity profile] thedeemon.livejournal.com
Да, верно, у меня код плотнее напихан. Плюс еще угол срезал, не стал выносить генерацию заданий в отдельный поток/актор.

Date: 2015-04-09 02:44 pm (UTC)
From: [identity profile] swizard.livejournal.com
Я бы для этой задачи просто тупо завёл счётчик, который бы воркеры атомарно инкрементировали и из значения формировали бы себе пароль для подбора (5 символов из алфавита в 36 значений совсем немного получается).

Но раз предлагается померяться толщиной каналов, то щас попробую изобразить аналог на Rust с использованием mpsc, посмотрим, что получится.

Date: 2015-04-09 04:14 pm (UTC)
From: [identity profile] thedeemon.livejournal.com
В посте про Го было сразу показано, что каналы там не шибко толстые, поэтому в упомянутых тут вариантах передается совсем мало данных.

Date: 2015-04-09 05:35 pm (UTC)
From: [identity profile] swizard.livejournal.com
Так, добрался до задачки, получается как-то так для раста (переписал твой алгоритм с D): https://gist.github.com/swizard0/6f4df8bdea0db32833fd

70 строк, но по-сути один хрен, я придерживался стиля, но можно и сжать, это непринципиально. На моём ноуте получается 4.5 сек на подбор.

Но, вообще, я бы изменил бенчмарк -- в этом виде он мало чего показывает. Например, можно было бы не интервалы пересылать, а каждый пароль отдельно (чтобы измерить производительность каналов), ну или убрать требование про каналы, чтобы получить максимальную производительность кода.

Date: 2015-04-09 06:11 pm (UTC)
From: [identity profile] yauheni akhotnikau (from livejournal.com)
Вообще да, в таком виде это больше соревнование на скорость реализации md5. Если в реализации есть адаптация под особенности процессора (или архитектуры), то это большой плюс, т.к. основное время тратится внутри md5.

Date: 2015-04-13 03:56 pm (UTC)
From: [identity profile] thesz.livejournal.com
Вот.

И тут мог бы победить тот, кто сделает векторную MD5. Или, даже, bit sliced SSE-vectorized MD5.

Date: 2015-04-09 06:30 pm (UTC)
From: [identity profile] yauheni akhotnikau (from livejournal.com)
Еще подумалось: первоначальный же смысл этого бенчмарка был вовсе не в скорости. А в том, насколько просто и быстро с помощью конкретного инструмента можно написать распределение задач между воркерами. В итоге изначальный смыл потерялся, а все свелось к выжиманию тактов.

Можно было бы вернуться "к истокам" и попробовать пойти в другом направлении: резко увеличить количество диапазонов (например, чтобы диапазон определялся не по порядковому номеру цифры, а по количеству значений в нем -- скажем, по 5 или 10 тысяч). Затем сделать обработку ситуации, когда пароль не найден. Сейчас-то все программы просто зависнут, что не есть правильно. Затем еще вот такое ограничение: как только пароль найден, все рабочие нити должны завершиться при окончании обработки своего текущего блока (либо они новые блоки не запрашивают, либо же им не отвечают, либо же им отсылается специальное значение, запрещающее работать).

Тогда этот пример сможет быть показателем гибкости удобства использования инструмента не с точки зрения скорости конкретной реализации md5, а с точки зрения написания и сопровождения кода.

Date: 2015-04-09 07:54 pm (UTC)
From: [identity profile] swizard.livejournal.com
А ещё тогда надо чтоб воркеры имели тенденцию падать с ошибкой в рандомный момент, и чтоб мастер мог эти моменты отлавливать, логгировать ошибку и респавнить процесс :) Я всецело за, только чур я тогда эрланг за собой застолбил! =)

Date: 2015-04-09 07:58 pm (UTC)
From: [identity profile] yauheni akhotnikau (from livejournal.com)
А с чего бы воркерам здесь падать? :)

Date: 2015-04-09 08:59 pm (UTC)
From: [identity profile] swizard.livejournal.com
Эмуляция реальных условий типа! Допустим, воркеры не просто md5 считают, а натурально ходят к какой-то веб-форме логина и подбирают пароль :)

Date: 2015-04-09 09:01 pm (UTC)
From: [identity profile] swizard.livejournal.com
...или спам рассылают, или капчу распознают =)

Date: 2015-04-10 04:29 am (UTC)
From: [identity profile] yauheni akhotnikau (from livejournal.com)
Понятно. Но хотелось бы, чтобы падения были как-то детерминированы, а не рандомно возникали. Чтобы сценарий падений не зависел от языка реализации.

Date: 2015-04-10 08:25 am (UTC)
From: [identity profile] yauheni akhotnikau (from livejournal.com)
Придумался такой сценарий. Есть N тасков (где N > 10, чило можно подобрать). Каждый таск -- это md5-хеш и диапазон возможных значений. Например {"c4ca4238a0b923820dcc509a6f75849b", ["0","9")} или {"698d51a19d8a121ce581499d7b701668", ["000","zzz")}.
Менеджер раздает таски воркерам когда воркеры освобождаются. Воркер получает таск, пытается подобрать значение из указанного в таске диапазона, которое даст такой же md5-хеш. Такое значение может быть, а может и не быть. О результате воркер сообщает менеджеру. Получение результата менеджером означает, что воркер освободился и может получить следующий таск (если таковые еще есть).

Диапазоны могут быть заданы некорректно. Например, ["zzz", "000") -- нарушение правила start меньше end. Или ["0000", "zz") -- в end меньше символов, чем в start. Или ["aaaa", "aaAz") -- символ A не входит в разрешенный алфавит.

Столкнувшись с некорректным диапазоном воркер должен "упасть". Эту ситуацию должен отловить менеджер и запустить вместо упавшего воркера нового. Сбойный таск при этом выбрасывается.

Вроде должно получиться не слишком сложнее исходной задачки. При этом это и не чисто на параллельные вычисления, и не чисто на многозадачность. Ну и как-то ближе к реальности. Список тасков можно сформировать и зафиксировать для использования одинакового для всех реализаций.

Date: 2015-04-10 12:10 pm (UTC)
From: [identity profile] swizard.livejournal.com
Ага, вроде получилась интересная постановка задачи, готов попробовать изобразить на rust.

Я бы тогда ещё чуток модифицировал:
  • чтобы программа интервалы получала на stdin, и запуск чтоб производился как % ./program < intervals.txt
  • чтоб на stderr программа логгировала некорректные интервалы с причиной ошибки из мастер-потока

Date: 2015-04-10 12:34 pm (UTC)
From: [identity profile] yauheni akhotnikau (from livejournal.com)
А вот читать stdin нужно сразу при старте, или в процессе работы?

Если будет чтение из потока, то нужно тогда использовать совсем простой формат, чтобы не усложнять код примера парсингом и проверками.

На счет вывода в stderr согласен.

Date: 2015-04-10 01:56 pm (UTC)
From: [identity profile] swizard.livejournal.com
Там данные вполне простые, можно что-нибудь вроде "<md5><пробел><начало интервала><пробел><конец интервала><\n>" на каждой строке.

Чтоб можно было просто сделать сплит по пробелам и нормально (соответственно, пробел и перенос строки не должны входить в алфавит).
Edited Date: 2015-04-10 01:57 pm (UTC)

Date: 2015-04-10 02:04 pm (UTC)
From: [identity profile] yauheni akhotnikau (from livejournal.com)
Ok. Не уверен, что смогу заняться завтра. Скорее это будет понедельник-вторник на следующей неделе.

Date: 2015-04-10 02:56 pm (UTC)
From: [identity profile] swizard.livejournal.com
Окей, а я, видимо, сегодня вечерком что-нибудь накидаю.

Date: 2015-04-11 07:37 am (UTC)
From: [identity profile] yauheni akhotnikau (from livejournal.com)
Предлагаю, для простоты, диапазоны для поиска задавать так, чтобы и левая и правая граница входили в поиск. Т.е. в виде [l,r], а не [l,r).

В этом случае проще задавать файлы заданий:

95ebc3c7b3b9f1d2c40fec14415d3cb8 yyyyy zzzzz

Вместо

95ebc3c7b3b9f1d2c40fec14415d3cb8 yyyyy 100000

Date: 2015-04-11 11:40 am (UTC)
From: [identity profile] swizard.livejournal.com
ага, согласен

Date: 2015-04-10 01:32 am (UTC)
From: [identity profile] thedeemon.livejournal.com
Так это и не бенчмарк вовсе, а флэшмоб по показу того, как код с конкаренси на разных языках выглядит.

Date: 2015-04-10 02:56 am (UTC)
From: [identity profile] thedeemon.livejournal.com
У, там зависимости внешние. :(
На одной стандартной библиотеке нельзя выехать?

Попробовал с такими зависимостями:
[dependencies]
rust-crypto = "0.2.31"
sys-info = "0.3.3"
rustc-serialize = "0.3.12"

Но один из пакетов, от которых они зависят, не собрался, чего-то в моем mingw не хватило.

Date: 2015-04-10 12:00 pm (UTC)
From: [identity profile] swizard.livejournal.com
У меня написано просто вот так:

[dependencies]
rust-crypto = "*"
rustc-serialize = "*"
sys-info = "*"

Что касается выехать на стандартной библиотеке -- уже нет :) Там щас новая политика партии: всё по-максимуму вынести из stdlib в crates.io. Вот эти crypto, serialize, и даже rand -- всё это раньше было в стандартной библиотеке.

Не сказал бы, что меня радует такой порядок вещей, но сейчас вот так вот.

Date: 2015-04-09 03:12 pm (UTC)
From: [identity profile] yauheni akhotnikau (from livejournal.com)
Флэш-моб, так флэш-моб :)
Т.к. мое вчерашнее решение было сделано с несколько другим прицелом, то вот более близкие к вашему варианту: eao197.blogspot.com/2015/04/progsobjectizer-md5bruteforce.html

Date: 2015-04-09 05:24 pm (UTC)
From: [identity profile] simsun.livejournal.com
На плюсах то сколько шустрит?:)

Date: 2015-04-09 07:27 pm (UTC)
From: [identity profile] yauheni akhotnikau (from livejournal.com)
На моей машине go-ный вариант отрабатывает за 8.1s, плюсовый -- за 6.7s.
Правда в go я не копенгаген, может компилятору какие-то хитрые ключики для оптимизации можно подставить...
Плюсовый вариант собран MSVS2013 Express с ключиком -02.
Go -- версии 1.4.2 (amd64).

Date: 2015-04-09 10:19 pm (UTC)
From: [identity profile] simsun.livejournal.com
спс!

Date: 2015-04-09 06:20 pm (UTC)
From: [identity profile] sleepy-drago.livejournal.com
интереснее было бы если бы обсуждались кластерные варианты. мол на 7 машинках по 8 цпу 4секунды.

Date: 2015-04-09 07:20 pm (UTC)
From: [identity profile] kodt-rsdn.livejournal.com
pastebin.com забанен в России
http://minjust.ru/ru/extremist-materials?field_extremist_content_value=pastebin.com

Конечно, поставлю себе проксю ради любопытства, но всё же... :\

Date: 2015-04-10 01:29 am (UTC)
From: [identity profile] thedeemon.livejournal.com
Не знал. Там в постах были альтернативные ссылки, вроде.

Date: 2015-04-10 03:21 pm (UTC)
From: [identity profile] http://users.livejournal.com/_slw/
в роскомнадзоровском списке его нет

Date: 2015-04-11 09:09 am (UTC)
From: [identity profile] kodt-rsdn.livejournal.com

Домру отслеживает три списка. В минъюстовском он есть, как видно.

Date: 2015-04-11 09:15 am (UTC)
From: [identity profile] http://users.livejournal.com/_slw/
домру -- не россия

Date: 2015-04-11 10:59 am (UTC)
From: [identity profile] kodt-rsdn.livejournal.com
Зато МинЮст - Россия.

Хз, считать ли "Федеральный список экстремистских материалов http://www.minjust.ru/nko/fedspisok/" обязательным для бана, или это домру бежит впереди паровоза.

Два других списка - http://eais.rkn.gov.ru/ и http://nap.rkn.gov.ru/

Date: 2015-04-11 11:02 am (UTC)
From: [identity profile] http://users.livejournal.com/_slw/
короче, ростелек в лице сзт -- не банит

Date: 2015-04-10 07:43 am (UTC)
From: [identity profile] 109.livejournal.com
ubyte[] ourHash = hashString.chunks(2).map!text.map!(s => ofHex(s[0])*16 + ofHex(s[1])).map!(to!ubyte).array;
auto workers = iota(totalCPUs).map!(i => spawn(&worker, thisTid, cast(immutable)ourHash)).array;

в таком стиле я на C# берусь в десять строчек написать. ну в 11 точно.

а вообще честнее число символов считать.

и непонятно, какое отношение к задаче имеет bandwidth обмена между тредами. задача же полностью параллелится с самого начала.

Date: 2015-04-10 09:32 am (UTC)
From: [identity profile] thedeemon.livejournal.com
Да, есть у меня пристрастие такие выражения в одну строчку писать.
Впрочем, в окошко гитхаба влезает, в редактор тоже, не вижу смысла делать существенно короче.
Вариант на С# тоже было бы интересно увидеть и потестить для сравнения. Необязательно в 11 строк (они уйдут на using'и), можно и в 12. :)

Число символов:
Go 1434
C++ 3377 исходный вариант, 2859 обновленный
D - 1345
Rust 2057

bandwidth обмена тут не при чем действительно.

Date: 2015-04-10 08:25 am (UTC)
From: [identity profile] alexandr alexeev (from livejournal.com)
Спасибо, интересно.

С интересом посматриваю на D. Скажите, а какая IDE для D в это время суток считается наиболее продвинутой?

Date: 2015-04-10 09:38 am (UTC)
From: [identity profile] thedeemon.livejournal.com
Вероятно, Mono-D (на базе XamarinStudio/MonoDevelop), хотя своим опытом подтвердить не могу, у меня с ней как-то не сложилось.
Сам использую VisualD (плагин для Visual Studio, сильно продвинутым сложно назвать) под виндой и Vim в линуксе.
В народе популярнее всего Vim, похоже.

Date: 2015-04-10 10:44 am (UTC)
From: [identity profile] fluidz123.livejournal.com
Вариант на scala+akka: https://gist.github.com/romangrebennikov/60eaee612bc37715d87a

У меня отрабатывает за 3.9с

Date: 2015-04-12 07:22 am (UTC)
From: [identity profile] thedeemon.livejournal.com
Занятно, спасибо. А зачем samehash вручную реализован, штатное сравнение массивов такое медленное что-ли?
From: [identity profile] livejournal.livejournal.com
Пользователь [livejournal.com profile] swizard сослался на вашу запись в своей записи «Весёлые приключения одной аллокации (http://swizard.livejournal.com/196920.html)» в контексте: [...] и форкнувшегося здесь [...]

Разделяй и властвуй

Date: 2015-04-13 10:19 am (UTC)
From: [identity profile] livejournal.livejournal.com
Пользователь [livejournal.com profile] a_jelly сослался на вашу запись в своей записи «Разделяй и властвуй (http://a-jelly.livejournal.com/473588.html)» в контексте: [...] Прочел вот тут [...]

Profile

thedeemon: (Default)
Dmitry Popov

December 2025

S M T W T F S
 12 3456
789101112 13
14151617181920
21222324252627
28293031   

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Jan. 30th, 2026 11:34 am
Powered by Dreamwidth Studios