LINUX.ORG.RU

Кто там кукарекал про С++?


1

8

Задача: разработать кроссплатформенное клиент-серверное приложение под Windows/Linux на С++ (boost, ace, etc.), клиент построчно считывает с консольки введёные числа, отправляет на сервер, сервер в ответ плюёт разложением чисел на простые множители. Стандартное тестовое задание, ничего интересного.

Ну что же, собрался духом, за вечер родил чуть около пол тысячи строк, чтоб всё как положено: асинхронность, многопоточность, все дела. Такое ощущение, коллеги, будто накормили грязью, кресты не умеют ни в замыкания, ни в нормальную асинхронность, ни в управление памятью, они вообще ничего не умеют. Вроде бы, написано 5 строк, а на деле почти не фига не делают, код раздут, абсолютно невыразителен, я уж не представляю что с ним будет, если его ещё раскидать на десяток классов, как это обожают делать отдельные особо одарённые личности.

К слову, скелет сервера получился не сильно отличающимся от того, что написано в доках буста.
Что это за вырвиглазие??

class server
{
public:
  server(boost::asio::io_service& io_service, short port)
    : io_service_(io_service),
      acceptor_(io_service, tcp::endpoint(tcp::v4(), port))
  {
    session* new_session = new session(io_service_);
    acceptor_.async_accept(new_session->socket(),
        boost::bind(&server::handle_accept, this, new_session,
          boost::asio::placeholders::error));
  }
  .......

Смеха ради, да и чтоб не вырвало от такого кода, накидал за полчаса решение на Haskell.
Что получилось:

  • Разбор параметров командной строки
  • Клиент-серверная архитектура
  • Полностью асинхронный многопоточный tcp-сервер
  • Поддержка unicode, IPv6 и BigInteger из коробки
  • Мемоизация (благодаря ленивости) из коробки
  • Полная кроссплатформенность (*nix, Mac OS, Windows etc.)
  • Правильность тривиально доказывается мат. индукцией по коду
  • Исходник чуть больше 60 строк (в 8 раз меньше, чем на крестах)

Если поднатужиться (я не стал) и заменить алгоритм нахождения простых чисел/простых множителей на более оптимальный, то ко всему прочему получаем автоматическую распараллелизацию алгоритмов из коробки (см. Data Parallel Haskell) и произодительность на уровне чистого Си/Фортрана.

Кто там пищал, что хаскель сугубо академический язык, что ничего реальго на нём написать невозможно? Кто там кукарекал про С++? Как вы с ним вообще работаете? Это же мазохизм в чистом виде (см. мыши и кактус)

★★★★★

Ответ на: комментарий от AIv

но во первых писать замучаешься, во вторых настраивать под конкретную машину, в третьих не факт что вообще получиться сделать эффективный код

Код можно генерить автоматически, с параметрами предварительно подобранными на тестовых проходах. Собственно, насколько я знаю, blas (а за ними LAPACK) делает нечто подобное. Да и не тайлингом единым - есть же и другие оптимизации которые тоже активно используются всякими автоматическими генерировалками кода типа cloog и паралелилками loopo. Так что не так страшен черт, как вы его изобразили.

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

А ничего что blas и LAPACK решают совсем другие задачи? Линал конечно универсальная хрень, но ничего хорошего с ним при создании софта под явные числ. схемы сделать не получится - все ложится поперек, задолбаешься циклы перекомпоновывать, а туда еще тайлинг и кодогенерацию... чур меня.

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

AIv ★★★★★
()
Ответ на: комментарий от AIv

А ничего что blas и LAPACK решают совсем другие задачи?

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

но пока никто даже близко не подошел производительности LRnLA для задач актуального размера

так простите мы то же самое считаем на персоналках

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

no-such-file ★★★★★
()
Ответ на: комментарий от geekless

А Vala чем не угодил? Статическая типизация, ООП, интерфейсы, наследование - и все это транслируется в С-код.

void_ptr ★★★★
()
Ответ на: комментарий от no-such-file

Вообще, насколько я понимаю, основная фишка вашего метода заключается

Вообще то основная фишка - в обеспечении высокой локальности данных. Остальное все следствия, в тч и эффективное распарралеливание. Даже на одном узле в одном потоке эффективность выше на порядок (от задачи зависит).

AIv ★★★★★
()
Ответ на: комментарий от marat2345

опять вы проводите агрессивный маркетинг своего LRnLA

Я просто защищаю С++! У меня идиосинкразия на индуцирующих ниасиляторов;-)

AIv ★★★★★
()
Ответ на: комментарий от geekless

Не, я себе скор накручиваю. Завидуй молча!;-)

AIv ★★★★★
()
Ответ на: комментарий от AIv

Вообще то основная фишка - в обеспечении высокой локальности данных

Высокая локальность данных в одном потоке вполне обеспечивается всем известными методами, о которых я говорил выше.

Остальное все следствия, в тч и эффективное распарралеливание

Я стесняюсь спросить, а какое вы имеете отношение к вот этой статье: http://a-iv.ru/trash/LRnLA.pdf. У меня начинает создаваться впечатление, что вы ее не читали, а если читали то не поняли.

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

Высокая локальность данных в одном потоке вполне обеспечивается всем известными методами, о которых я говорил выше.

Для описанных задач - к сожалению нет (точнее, мне лично неизвестно решение описанными Вами методами хотя бы одной из задач, решенных LRnLA, с аналогичной производительностью даже в одном потоке). Хотя гипотетически такое возможно... Характерное время вычисления одной ячейки на шаг - десятки тактов, и накладные расходы должны быть по крайней мере не выше. Это довольно жесткое ограничение на код. Узкое место - именно доступ к памяти. Тайлинг на обычных циклах уже в одномерном случае выглядит ужасно, про 3D не говорю. В общем, LRnLA код несмотря на свою оригинальность оказывается куда проще... ;-)

У меня начинает создаваться впечатление, что вы ее не читали, а если читали то не поняли.

Если Вы ее поняли полностью - снимаю шляпу. Вы будете вторым человеком после автора, насколько мне известно;-) Но автор с тех пор ушел очень далеко вперед. Хотите - приходите к нам на чай, там и поговорим.

AIv ★★★★★
()
Ответ на: комментарий от geekless

Но ведь это будет только glib и ничего более. Не такая уж большая плата за все єти плюшки.

void_ptr ★★★★
()

Как вы с ним вообще работаете?

Тянут куски с быдлокод.ру и из дрцугих мест сети и склеивают

darkshvein ☆☆
()
Ответ на: комментарий от LongLiveUbuntu

У меня к тебе большая просьба: сделай вменяемую IDE для Хаскелля и я с удовольствием буду на нем писать.

В цитаты!

darkshvein ☆☆
()
Ответ на: комментарий от AIv

Я просто защищаю С++! У меня идиосинкразия на индуцирующих ниасиляторов;-)

Что толку сейчас от твоей защиты?!

Ты нам в сотый раз приводишь в пример вычислительную задачу, где действительно у Cи++ еще есть некоторые шансы, но... исходная-то задача во многом ограничена вводом-выводом, особенно будь она помещена в реальные условия тырнета. Ее можно даже на каком-нибудь питоне решить, а лучше на том, что больше нравится, где чище и красивее получается код. Так что, делаем выводы :)

dave ★★★★★
()
Ответ на: комментарий от AIv

Если пользовать умные либы то и в hs не много. Данные передаются plain text, что увеличило код (с учётом честного разбора). На самом деле ещё нужно через те же conduits сделать вывод primes и убрать primeFactor, которая делает кучу лишнего (т.е. собирает результат вместо отправления его на вывод). Ну и ещё минус в неконтролируемой мемоизации primes. Если вечером не лень будет - запосчу полностью адекватный вариант.

import Data.Conduit
import Data.Conduit.Network
import Data.Conduit.List as CL
import Data.Conduit.Text as CT
import Data.Text.Read (decimal)
import qualified Data.ByteString.Char8 as SC
import qualified Data.Text as TT

main = runTCPServer (ServerSettings 3500 Nothing) sv

primes = 2 : sieve [3, 5..] where
    sieve (p:xs) = p : sieve [x | x <- xs, rem x p /= 0]

primeFactors :: Integer -> [Integer]
primeFactors n = factor primes n where
    factor ps@(p:pt) n | p*p > n      = [n]
                       | rem n p == 0 = p : factor ps (quot n p)
                       | otherwise    = factor pt n

sv :: Application
sv src snk = 
  src $= CT.decode CT.ascii 
      $= CL.map (\t ->
        case (decimal t) of
          Left err ->
            SC.pack ("parsing '" ++ TT.unpack t ++ "'error: "++err++"\n")
          Right (i,_) -> 
            SC.pack $ (show $ primeFactors i) ++ "\n"
        )
      $$ snk 
qnikst ★★★★★
()
Ответ на: комментарий от dave

На питоне я ее решил и даже решение привел, см пред. страницу;-)

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

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

Ты нам в сотый раз приводишь в пример

Ты не поверишь - многие вот не слышали;-)

AIv ★★★★★
()
Ответ на: комментарий от qnikst

Я кстати думаю, что и на плюсах оно не сильно длиньше. С чиселками будет все то же самое, и наверняка есть какие то высокоуровневые либы для организации сокетов, я их просто не знаю, т.к. никогда с сеткой на плюсах не работал. Так что где там ТС нарыл 500 строк, да еще за вечер...

AIv ★★★★★
()
Ответ на: комментарий от AIv

Ну правда. Все же понимают, что виртуозное обращение с памятью - сильная сторона Си/Си++, чего нельзя сказать о Python или Haskell. Ну дак это прямое следствие низкоуровневости языка и, по крайней мере косвенно, указывает на слабость языка для решения больших задач, где важно не виртуозное обращение с памятью, а надежность кода, скорость написания и легкость поддержки кода. Конечно, сейчас очень многие проекты по привычке пишут на Си++, но это вовсе не значит, что это правильный путь. Все больше и больше проектов выбирают Python, Java или другие языки с более высоким уровнем абстракции, которые позволяют сосредоточиться на том, что делает программа, а не как она это делает.

delete83 ★★
()
Ответ на: комментарий от AIv

Для описанных задач - к сожалению нет

Давайте возьмём конкретно FDTD, который приводится в качестве примера в статье. Там же приведен график зависимости эффективности от размера задачи. Аналогичный график на этой задаче можно наблюдать, например здесь: http://www.ece.lsu.edu/jxr/pluto/index.html, что достигается вполне известными методами.

Да и вообще, неужели вы думаете, что производители аппаратуры живут где-то в в вакууме и штампуют железки с многоуровневыми кэшами в надежде, что эту особенность как-то удастся использовать? Все-таки перед тем как лепить кэш в процессор, теоретические вопросы получения пиковой производительности всесторонне рассматриваются применительно к предполагаемым задачам.

no-such-file ★★★★★
()
Ответ на: комментарий от delete83

Все правильно. Но во первых в плюсах есть куча фенечек для того что бы не морочится с памятью, во вторых все таки хорошо пишется на связке питон (или че нить эдакое) - С++ ;-)

Другое дело, что конечно пока начнешь писать на плюсах быстро, правильно и красиво... много времени пройдет.

AIv ★★★★★
()
Ответ на: комментарий от delete83

на haskell в отличии от питона возможно общение с памятью не хуже чем в сях, правда в этих местах идеоматичность и куча бонусов будут исчезать...

qnikst ★★★★★
()
Ответ на: комментарий от no-such-file

Давайте возьмём конкретно FDTD, который приводится в качестве примера в статье

Спасибо за ссылку. То что они 5х получили на таком размере... ну молодцы конечно. Тока там размер задачи мегабайты, а у нас гигабайты (для персоналки). А это две большие разницы;-) Да и 5х на 4Мб ИМНО скорей говорит о кривизне обычного кода - там и обычный код должен вполне себе летать.

неужели вы думаете, что производители аппаратуры живут где-то в в вакууме

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

AIv ★★★★★
()
Ответ на: комментарий от qnikst

Ага, ага. В камле тоже есть указатели и все соответствующие фокусы с памятью, правда после их применения от выразительности камла остается только воспоминание.

delete83 ★★
()
Ответ на: комментарий от AIv

Тьфу, сорри, не на тот график посмотрел;-(

Все равно, размер задачи в 4000 (чего? ячеек?) ничего кроме гнусной ухмылки не вызывает;-) Если они такие крутышки, чего ж они не считают 3D и 10^9 узлов? Вот когда я увижу их тесты под такой целевой размер, тогда поверю.

AIv ★★★★★
()
Ответ на: комментарий от AIv

возможно, правда там либ сейчас >100500 так что найти подходящее будет не самой простой задачей. Особенно последний код, который будет заведомо работать в constant memory, не считая мемоизации primes, что делать с этим я честно говоря не знаю; плюс наличие лёгких процессов, которые в случае многоядерной машины будут реальными процессами исполняющимися на разных ядрах, плюс (я не знаю как с этим в плюсах) придётся побороться с синхронизацией потоков.

qnikst ★★★★★
()
Ответ на: комментарий от AIv

Да и 5х на 4Мб ИМНО скорей говорит о кривизне обычного кода - там и обычный код должен вполне себе летать

У FDTD там не 4Mб, а размер задачи 4000. Я так понял, что 4000x4000xdouble=128Мб. У не оптимизированного варианта производительность падает при размере задачи 500x500xdouble=2Мб, что примерно согласуется с размером кэша на 1 ядро.

Кэши и пр фигня оптимизируются под всякие офисы-игры-граф редакторы и проч фигню

Процессоры бывают разные. Или в POWER7 кэши тоже втыкают чтобы крузис не тормозил? Впрочем, в том, что кэши у x86 не заточены под числодробление я с вами согласен.

no-such-file ★★★★★
()
Ответ на: комментарий от AIv

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

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

ТС местами одиозен. Не будет об этом.

Мой посыл в том, что для 90% задач сегодня Си++ является неудачным выбором, поскольку многие задачи не являются вычислительными, а ограничены прежде всего вводом-выводом.

Тут несколько особняком стоят веб-браузеры. Да, их пишут на Си++, но я считаю, что во многом из-за того, что здесь особенно критична скорость запуска приложения, да и межплатформенность часто тоже важна. Других существенных плюсов использования Си++ для этого узкого класса задач не вижу.

Ну, вот. Выяснилась еще одна причина использовать Си++: быстрый запуск приложения. Но если это критично для браузеров, которые используются сегодня повсеместно, то с медлительностью запуска многих других приложений пользователи вполне могут мириться. Поэтому для остальных задач - .NET, Java и, конечно, Common Lisp :)

Да, и где сейчас серверы приложений Corba на Си++? Что с ними сделал j2ee и говорить грешно :)

dave ★★★★★
()
Ответ на: комментарий от tailgunner

я правильно понимаю, что ctypes этож FFI? Если там можно в выделенную нужным образом память класть питоновые структуры и забирать их обратно при этом имея выбор между тем будет ли следить за памятью gc, то забираю свои слова назад, т.к. на более высоком уровне у меня не хватит квалификации, чтобы сказать что и чем лучше.

qnikst ★★★★★
()
Ответ на: комментарий от qnikst

я правильно понимаю, что ctypes этож FFI?

Да.

Если там можно в выделенную нужным образом память класть питоновые структуры

Именно питоновые структуры - список объектов, например? Наверное, можно, но не видел, чтобы кто-то такое делал. А зачем это делают в Хаскеле (если делают)? Си-программа получается зависимой от деталей реализации RTS.

tailgunner ★★★★★
()
Ответ на: комментарий от no-such-file

У FDTD там не 4Mб, а размер задачи 4000. Я так понял, что 4000x4000xdouble=128Мб. У не оптимизированного варианта производительность падает при размере задачи 500x500xdouble=2Мб, что примерно согласуется с размером кэша на 1 ядро

FDTD 2D требует 4 числа на ячейку. Но в общем да. Другое любопытно, график по Вашей ссылке никак не бьется с их же графиком из http://www.ece.lsu.edu/jxr/Publications-pdf/tr70-07.pdf fig 9а. Причем, если график по Вашей ссылке еще как то смотрится, то та фиг 9а вообще непонятно чего за казюля. Трад. код просаживается за счет проблем с памятью вдвое при увеличении задачи в 8 раз (оценка). За счет чего они получили ускорение на малых размерах, и почему оно не выросло на больших размерах, нефига не понятно... но допустим накосячили, бывает.

Но, FDTD 2D требует на ячейку 8мь сложений и 4ре умножения + загрузки. Про SSE они не говорят ни слова. И вот тута у меня вопрос - а как они считали свои флопсы?;-)

AIv ★★★★★
()
Ответ на: комментарий от anonymous

жтутф, компилятор perl6 уже давно написан на perl6 полностью

вот чёрт! и это достоинство у Haskell уже отняли :(

jtootf ★★★★★
()
Ответ на: комментарий от dave

Я могу говорить только за свою полянку;-)

И да, Вы наверное правы. Ну так лично я на плюсах пишу только критичные по быстродействию части, для всего остального есть питон;-)

AIv ★★★★★
()
Ответ на: комментарий от Deleted

Где искаропки есть priority_queue<> хотя бы? :)

что считать за коробку? так-то сложно найти, где их нет (в той или иной реализации)

jtootf ★★★★★
()
Ответ на: комментарий от dave

Еще на Си/Си++ пишут то, что встраивается в другие приложения. У VM-языков с эим проблемы.

tailgunner ★★★★★
()
Ответ на: комментарий от AIv

Если они такие крутышки, чего ж они не считают 3D и 10^9 узлов?

Потому, что это тест, а 10^9 узлов это примерно 6*2^60 байт. Для теста достаточно и того, что возможности кэша перекрыты на порядок, дальше все тоже самое пока память совсем не кончится.

Вот когда я увижу их тесты под такой целевой размер, тогда поверю.

Дык давайте и ваши результаты для 6 эксабайт. Тогда поверю.

А если серьезно, я не спроста вас спрашивал, читали вы статью или нет. Там черным по белому написано, что классические методы оптимизации вполне эффективны при отсутсвии параллелизма, а следовательно и при «дешевом» параллелизме (т.е. на классических многопроцессорниках), когда накладные расходы на синхронизацию ничтожны.

no-such-file ★★★★★
()
Ответ на: комментарий от Macil

Кстати, хочу спросить, а алгоритм LSTD на который приводятся ссылки в работе он что, засекречен?

Это устаревшее название LRnLA.

AIv ★★★★★
()
Ответ на: комментарий от no-such-file

10^9 узлов это примерно 6*2^60 байт

O_O? Под узлом я понимаю один узел сетки, а не размер сетки по одному направлению. 10^9 узлов для 3D это всего лишь 14Гб (м.б. и больше, зависит от модели среды). Результат примерно 10 тактов на узел на шаг.

Там черным по белому написано, что классические методы оптимизации вполне эффективны при отсутсвии параллелизма

Наск мне известно, сейчас автор так не думает;-) Статья то какого года... но я спрошу;-)

AIv ★★★★★
()
Ответ на: комментарий от no-such-file

дальше все тоже самое пока память совсем не кончится.

В том то и прикол LRnLA, что дальше все то же самое, пока не кончится диск (или хранилище, доступное по сети - но так далеко мы пока не заходили;-)).

AIv ★★★★★
()
Ответ на: комментарий от AIv

дальше все тоже самое пока память совсем не кончится.

И кстати, после кэша тоже есть уровни связанные со страницами и пр. фигней.

AIv ★★★★★
()
Ответ на: комментарий от AIv

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

По быстродействию - да, получается супер. По объему бинарника и времени компиляции - к сожалению, нет.

Deleted
()

[C++]

http://pastebin.com/npz2hhtU - строчек получилось побольше, чем у питона и твоего хацкеля, но там бОльшая часть - скелет. Выглядит нормально. Использует Poco и бустовый lexical_cast. Код читабелен. Есть претензии?

Deleted
()
Ответ на: комментарий от Deleted

По объему бинарника и времени компиляции - к сожалению, нет.

а чем вас объем бинарника не устраивает? если статично не линковать libc/stdc++, то вполне компактно получается

anonymous
()
Ответ на: комментарий от jtootf

За коробку считать то, что прописано в стандарте языка, либо, если стандарта нет, де-факто присутствует во всех реализациях.

Deleted
()
Ответ на: комментарий от AIv

Наск мне известно, сейчас автор так не думает;-)

Ну как же:

Явные сеточные методы с локальным шаблоном (ЯЛСМ) являются классическими методами дискретизации уравнений математической физики и лежат в основе численного решения диференциальных уравнений в частных производных гиперболического и [иногда] параболического типов. Уравнения таких типов возникают во множестве актуальных задач математической физики, включая задачи моделирования среды. Основной проблемой практического решения подобных задач является их исключительно высокая вычислительная сложность. При этом особенностью данных задач является то, что даже при использовании алгоритмов линейной сложности по данным (к которым как раз и приводят ЯЛСМ), количество обрабатываемых данных таково, что справиться с ним могут только параллельные вычислительные системы (ПВС).

То есть надо понимать, что если бы задачи не требовали применения ПВС, то все ок. Далее обозначая проблему автор пишет:

Причина же проблемы заключается в несовершенстве механизмов управления и синхронизации расчетов на отдельных узлах кластера, часто базирующихся на использовании ярусно–параллельной формы (ЯПФ) представления алгоритма, что приводит к необходимости пошаговой синхронизации и, как следствие, к проблеме “бутылочного горлышка” — простоя всей ПВС до окончания выполнения самого медленного задания на каждом ярусе (обычно временном шаге) алгоритма.

То есть если бы не гетерогенность кластера, где каждый узел считает с разной скоростью + задержки обмена между узлами, то все ок.

Выходит так, что на обычной SMP архитектуре, или скажем на CUDA, где все считается за примерно равные промежутки и особой проблемы в синхронизации нет, классические методы не будут уступать. Или как?

но я спрошу

ну поинтересуйтесь, мне тоже интересно.

no-such-file ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.