All streams
Search
Write a publication
Pull to refresh
152
0
Григорий @bfDeveloper

Программист на C++, D, Brainfuck

Send message
Для чего-то важного и популярного типа libcurl есть свои реализации. Тот же vibe.d. Там есть даже свой event loop, оборачивающий kqueue, epoll и win32 в общий интерфейс, называется libasync. А некоторые вещи лучше вообще не переписывать, например OpenGL. Его лучше максимально дёшево слинковать.
Ещё ABI очень важен для редких задач. Упомянутый мной выше co2mon из питона и node.js люди используют стартом отдельного процесса, с которого берут stdin и stdout. Это действительно наиболее простое решение для них. А D просто линкует библиотеку, зовёт функцию и получает данные.
D позволяет решать действительно сложные и редкие задачи просто, а Go простые задачи очень просто.
Здесь всё зависит от задач и от программистов. Те, кто писал сайтики на php могут продолжить заниматься этим на Go. Те кто закапывался в алгоритмы и оптимизации в C++ радуются D.
везде можно использовать C, C++,

Как много языков имеют ABI совместимость с C и C++, не требуют никаких прослоек, используют совместимые типы и не внедряются в систему сборки?
В каком языке вы можете просто слинковать существующую системную библиотеку и начать использовать, не подключая всяких биндингов? Мне, например, для использования простой библиотеки потребовалось всего-лишь описать её интерфейс в .di файле. Это заняло не больше, чем клон самой библиотеки с гитхаба. Для более сложных случаев есть специальная утилита htod. И нет, это не автогенератор прослоек или биндингов, это просто генератор заголовков для использования из D. Никаких накладных расходов, никаких преобразований типов.
Я видел, что предлагает Rust, знаю как пишутся плагины для node.js, активно использовал JNI, на работе пишу XS для perl, и поверьте, удобнее, чем из D c C и C++ взаимодействуют только С и С++.
jin.go — отличная библиотека, но всё же select не получился. Приведённый в статье пример очень так себе. Гораздо лучше с задачей селекта справляются обычные D сообщения. С сообщениями можно писать так:
receive(
    (int val) {

    },
    (double val) {

    },
    (int i, double d) {

    },
    (MyStruct s) {

    }
);

Тогда из другой задачи можно писать
tid.send(42);
tid.send(3.14);
tid.send(10, 10.0);
tid.send(MyStruct(args));

Основной недостаток в том, что сообщение отправляется задаче, а не в некий канал. Передача данных реализуется через передачу владения, что быстро и безопасно. Просто не выглядит как поток или канал.
Унифицированный синтаксис вызова правильно отменили. Нет, идея отличная, но хотели вывернуть её с ног на голову. D продемонстрировал, что obj.foo(args) должно искать сначала метод, а потом функцию foo(obj, args). А хотели унифицированным сделать foo(obj, args), чтобы он искал метод. Это решает проблему с begin/end, но неудобно для остальных случаев, например статических расширений классов.
По статье не понял, а библиотеку Ranges всё-таки приняли?
Придётся подумать над оптимизацией размера. По видео хорошо заметно, что вся машина не входит в область расчёта, и поэтому её дальние части работать не будут.
А вообще большущее спасибо за работу. Сам хотел такое делать, но руки не доходили.
D — противоположность Go для больших проектов. Я бы даже сказал, что он заточен под большие проекты. Множество привычных фич, которых нет в Go, в других языках были придуманы для масштабирования проекта (ООП, шаблоны, compile time, возможность своих DSL), и D поддержал и развил эти идеи. Из-за этого он выглядит нагруженным, но зато поддерживает кучу разных подходов и парадигм (от процедурной до ООП и функциональной).
По первому вопросу всё непросто. D гораздо привычнее для программистов с других языков, но при этом он существенно сложнее Go. Для С++ программистов D покажется простым, и среднего программиста можно сажать писать код почти сразу.Для Java программистов — очень знакомым, но с кучей новых вещей.
С какого бы языка человек не пришёл он найдёт в D знакомые концепции, но это бывает минусом. Всё же проект должен быть однородным и все должны писать одинаково. И вот для обучения D стилю времени уйдёт точно больше чем в случае с Go.
А я вот не согласен с тем, что пользователю нужен результат. Нет, изначально гаджеты и софт для этого создавался, но уже давным давно люди покупают ощущения использования, а не результат. Кто пользуется возможностями iPhone6, которые недоступны в iPhone5? Кто из владельцев спорткаров действительно выжимал максимальную скорость?
К сожалению большинство современных гаджетов предлагают в первую очередь ощущение того, что этот гаджет вам нужен, иначе его не продать. Новомодные стартапы придумывают потребность пользователя, а потом её решают. В этом ключе оказывается важнее управление лампочкой с телефончика, чем нормальное освещение в квартире.
Я не говорю, что всё описанное выше хорошо, скорее это плохо. Но к сожалению, сейчас рынок работает так.
void mySum(int[] r, Task tid) {
    int result = r.sum;
    tid.send(result);
}

void example() {
    auto s = [7, 2, 8, -9, 4, 0];

    auto c = Task.getThis;
    runTask(toDelegate(&mySum), s[0..$/2], c);
    runTask(toDelegate(&mySum), s[$/2..$], c);
    int x = receiveOnly!int();
    int y = receiveOnly!int();

    logInfo("%d %d = %d", x, y, x+y);
}

Проект, который можно запустить, здесь.
Это почти полный эквивалент. По крайней мере делает ровно то же самое, и я постарался сделать код максимально похожим, чтобы прослеживались параллели.
Основное отличие — отсутствие канала. Вместо этого посылается сообщение. Полной аналогии типизированных каналов в vibe.d нет, есть две альтернативы:

  • Сообщения. Буферизированые в очереди, типизированные, но посылаются в сопрограмму, а не в отдельный объект. Минимальная шаблонная обёртка и это будет полная аналогия каналов
  • TaskPipe. Ведёт себя как канал, можно буфферизировать, можно не буфферизировать, но предназначен только для данных. То есть только ubyte[]

Цикл суммирования я тоже убрал, это слишком много бессмысленного кода. Благодаря нормальным шаблонам в D есть нормальные обобщённые алгоритмы. Их не надо писать каждый раз заново, при этом они не теряют в производительности. Это как раз та область, где D нет равных. Go тут тоже нет равных, но в обратном смысле — этой фичи нет вообще и без нормальных шаблонов быть не может.
И вот из того, что я видел

Мне кажется проблема в том, что асинхронность в D вы не видели.

Наконец-то можно создавать поток на каждое подключение и на каждую фоновую операцию и не бояться, что это сожрет всю память.

Это точно так же можно делать в D. Fiber по легковесности эта та же самая горутина, можете создавать десятками и сотнями тысяч.

в D предлагают опять эти уродливые события и колбеки

Я вас не понимаю, честно. Найдите в vibe.d хоть один колбек. Кроме разве что onConnection и onTimer. Но они и инициируются не кодом, а некой третьей стороной, для них нет линейного кода. Так проиходит и в Go и в C#.
А для чтения, записи, подключения к кому-то нет никаких колбеков или событий. События есть для общения между сопрограммами, но это тот же самый select в Go, только гораздо универсальнее.
Бывают ситуации, где предлагается альтернатива: колбек или возвращаемое значение. Просто так получилось, что чистые колбеки работают чуть-чуть быстрее и это API оставлено. Но код всё равно можно писать синхронно.
Всё сделано так чтобы было удобно писать именно линейный код: предоставляются асинхронные линивые диапазоны для чтения/записи, автоматически закрываются соединения при выходе из скоупа и тд, и тп. Чего-чего, а callback hell, это точно не про vibe.d
Fiber в стандартной библиотеке. Vibe.d поддерживает множество ивентлупов (libevent, libev, win32, собственная библиотека libasync). Большинство библиотек для асинхронных операций основываются на vibe.d, он стал почти стандартом, поэтому проблемы совместимости нет. Кроме того модель сопрограм такова, что если функция не выполняет асинхронных действий сама, то ей не требуется какая-то особая поддержка асинхронности. То есть любые синхронные библиотеки отлично работают в асинхронном коде.
Хорошо у него с производительностью. Сравнивал простые HTTP сервер и клиент с Go, получилось примерно одинакого. Причём масштабируются и Go и D одинакого хорошо. С учётом обработки и всяких парсингов JSON D оказывается быстрее. Есть мнение, что самый быстрый JSON как раз написан на D. Бенчмарк конечно не совсем честный, но точно претендент на лидерство.
В бенчмарке по вашей ссылке vibe.d есть, правда работал в один поток (была взята старая версия фреймвёрка с досадным багом). PR с нормальной многопоточной версией был отправлен вовремя, но почему-то его не приняли. Ждём следующего запуска, чтобы увидеть правильные результаты.
"Сложный" пример предлагал чуть выше, а за совсем простыми можете обратиться сюда. Пример оттуда:

auto conn = connectTCP("time-c.nist.gov", 13);
logInfo("The time is: %s", conn.readAllUTF8());

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

while(true) {
        c.write(buf.data);
        c.write(['\0']);
        buf.clear();
        c.readUntil(buf, ['\0'], SIZE);
    }
}

Это кусок одного очень простого теста, слегка усложнённый вариант эхо сервера. Пишет в сокет то, что получил. Что c.write, что c.readUntil — асинхронные операции, в которых произойдёт переключение волокон исполнения. С моей точки зрения Fibers — абсолютный эквивалент goroutines и, что уже субъективно, гораздо удобнее async из C#.
Про асинхронность не правда. Корутины есть (называются Fiber), кроме того есть целый фреймвёрк для асинхронного io: vibe.d
Совсем правильный пример конкатенации должен быть таким:
auto s = concat(s1,s2,s3);
, где concat — ленивый диапазон отсюда (https://github.com/ericniebler/range-v3), ну или из std, когда будет.
В этом случае вообще не произойдёт выделения памяти. Понятно, что если дальше нужен быстрый произвольный доступ, то всё равно лучше создать именно строку через range::copy(concat(...), s). Но если потом вы будете читать пару символов или только последовательно, то лучше оставить ленивый диапазон.
Согласен, язык не так популярен на хабре, стоило подробнее описать, зачем вообще писать на D. Восполню пробел парой ссылок на статьи на хабре:
https://habrahabr.ru/post/246623/
https://habrahabr.ru/post/224419/
https://habrahabr.ru/post/197480/
https://habrahabr.ru/post/154345/
Есть динамический биндинг OpenCL. http://code.dlang.org/packages/derelict-cl
Это просто описанный интрефейс к обычному OpenCL. Как хэдеры в сях. Аналогичная штука есть для CUDA
http://code.dlang.org/packages/derelict-cuda
Холиварный вброс, но всё же отвечу. Затем, что быстрее. Бытрее писать и внезапно быстрее работает.
Писать быстрее благодаря обилию алгоритмов, шаблонов, нормальной типизации, безопасности (в виде RAII, контрактов, тестов и сборке мусора) и кучи плюшек (http://dlang.org/ctod.html).
Быстрее работает — немного спорное утверждение. Эквивалентный код чуть медленнее, но на D не пишут как на C. Банальный пример: там, где в C будет массив или копия массива, в D будет ленивый диапазон. По моему опыту замена конкатенации строк на ленивый диапазон даёт прирост скорости 6 раз. Опыт портирования проектов с С или С++ говорит об ускорении результата. Например, в фейсбуке портировали небольшую библиотеку https://code.facebook.com/posts/729709347050548/under-the-hood-building-and-open-sourcing-flint/ D версия работает быстрее.
Чтобы повторить то, что даёт стандартная библиотека диапазонов в D, понадобится куча кода на C, который никто просто не будет писать. Писать быстрый код на D быстрее. Писать обычный код ещё быстрее.
Писать супероптимизированный код на D тоже проще, так как ничто не мешает опуститься до уровня C, пользоваться указателями и всеми низкоуровневыми оптимизациями вплоть до встроенного ассемблера. Вот только мощность шаблонов и прочих фич времени выполнения будет помогать даже на низком уровне и всем этим можно пользоваться.
Ситуаций, в которых D уступает C или C++, очень мало и с каждым днём становится всё меньше.
Бенчмарков не ставил, но не вижу причин, почему он должен отличаться от x86. Бэкенд у компиляторов одинаковый, сами языки примерно на одном уровне производительности, так что +- то же самое.
Где-то вы тут запутались. В целом всё верно, М1 — начальная масса, М2 — конечная. Вот только откуда М1+М3+М4=const? Из соображений, что стартовая масса всегда одинаковая? Это не совсем правда, скорее М1+М3+М4<=const.
По самой формуле (М1+М3+М4)/(М2+М3) = C = e^(V/I) = const, так как e^(V/I) от массы не зависит. Так как М1/М2 = С, то (М3+М4)/М3 = С, отсюда увеличение массы топлива для посадки М3+М4 = С*М3. Это линейная зависимость. Для удельного импульса Мерлина в 2.73 км/с и скорости расстыковки в 3.4 км/с получаем С = e^1.24542 = 3,4744. То есть надо взять дополнительно в три с половиной раза больше топлива, чем нужно для посадки.

Information

Rating
4,512-th
Location
Москва, Москва и Московская обл., Россия
Date of birth
Registered
Activity