Как стать автором
Обновить

Комментарии 49

Напомню, что IKVM — это реализация Java для Mono и Microsoft .NET Framework, т.е. по сути мы имеем возможность конвертировать jar в dll и запускать Java-код под .NET. Задумка хороша, но библиотека долгое время практически не развивалась и её состояние было далеко от возможности использования в реальных проектах.
Почему же? Мы вот используем.
Ну, тут вопрос в масштабах. С какими-то простыми вещами IKVM работает нормально, но я сомневаюсь, что можно просто так взять и перенести на Mono какую-нибудь здоровую Java-библиотеку. Когда я последний раз экспериментировал с IKVM, то встретил ряд трудностей, которые не дали мне вывести это начинание на уровень продакшена. Но меня безусловно радует то, что у вас есть позитивный опыт использования.
Это не просто «позитивный опыт», IKVM нас просто спасла! Интегратор требовал от нас подключения к IBM WebSphere MQ — а там клиентская библиотека для .NET просто уродская. Начнем с того, что при подключении имя пользователя и пароль обязаны совпадать с логином и паролем текущего пользователя — а это означает, что сторонняя компания будет знать логин и пароль от наших стендов, а в худшем случае — еще и от компьютеров разработчиков.

Кроме того, их клиент еще и надо отдельно ставить в систему — без этого библиотека работать не будет. Прощай нормальные деплои, прощай нормальная загрузка всех зависимостей через nuget…

И тут вдруг на SO в самом низу страницы кто-то подсказывает решение — взять JMS-драйвер и запустить его под IKVM… И все сразу же заработало! Без установок клиента, без всяких совпадающих паролей! И почти все зависимости есть в nuget, а чего нет — то можно и добавить в репозиторий!
Все еще не работает нормально minecraft на ikvm :)
Вроде же уже работает… Или «нормально» — это в режиме сервера?
# 1 ДЕКАБРЯ 2014 Г.
With the fixes I did in the snapshot released on October 29, it is now possible to run Minecraft on IKVM.NET (on Windows). To be clear, I'm talking about the Minecraft client, not the server that has been running on IKVM.NET for a long time.
Судя по вашей цитате, режим сервера «работает уже давно»
Точно. Ну так тем более…
Более того, следующий текст не соответствует действительности

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

Чтобы в этом убедиться, достаточно заглянуть в блог разработчика:
weblog.ikvm.net/
Спасибо за замечание, поправил.
Меня смутило то, что на вики написано Stable release: December 10, 2012, a NuGet-пакет как-то не очень обновлялся долго время.
Mono в версии 4.0.0 полностью перейдёт на CoreCLR и CoreFX

Может я что-то не так прочитал, но там нигде не сказано о переходе на майкросовтовский рантайм. Только о замене некоторых частей кода, которые были криво реализованы в mono.
Ну, пишут вот что: «In the future, we will deliver a “Mono Core” along the same lines of .NET Core to allow the use of the Mono runtime with the new library distribution system that is being developed with CoreFX». Быть может, там не полностью перейдут на CoreCLR+CoreFx, но, как я понимаю, эти проекты лягут в основу, а будет не просто замена некоторых частей кода.
Спасибо, добавил в пост.
Хм, продолжу общую тенденцию комментариев в посте, сначала цитата:
старая реализация ядра .NET в Mono была, мягко говоря, не очень удачной

затем вопрос — почему не очень удачной? Какие критерии? В попугаях она на некоторых тестах опережала официальную реализацию.
GC — страх, ужас, кошмар (sgen вполне юзабелен, но всё же), JIT местами не очень, AOT-компиляцию на классы фреймворка не накладывали (а если посмотреть на .NET, то там есть даже инлайнинг между сборками, если указан соотв. атрибут). Ещё большие проблемы с асинхронным I/O, но тут Mono не особо виноват, сама модель с Begin/End плохо вписывается в работу через epoll/kqueue (у меня статейка недописанная на эту тему, надо бы добить).
Ну и для интереса можно глянуть бенчмарки LINQ, которые приводят для LinqOptimizer (до и после обработки, .NET и Mono).
сама модель с Begin/End плохо вписывается в работу через epoll/kqueue
Чем же оно не вписывается-то? Begin/End элементарно реализуются через задачи (благодаря тому, что Task реализует интерфейс IAsyncResult) — а задачи вписываются в эту модель прекрасно благодаря стандартному же классу TaskCompletionSource.

Я, конечно, понимаю, что Mono начинался с более ранней версии .NET, чем та, в которой появились нормальные задачи — но и без задач реализовать все то же самое — не настолько уж невыполнимо.

Или все то, что я расписал — и попадает в категорию «больших проблем»?
Чем же оно не вписывается-то? Begin/End элементарно реализуются через задачи (благодаря тому, что Task реализует интерфейс IAsyncResult) — а задачи вписываются в эту модель прекрасно благодаря стандартному же классу TaskCompletionSource.
Обернуть можно что угодно во что угодно, производительности это не прибавит. Windows сама умеет вызывать асинхронный колбек на потоке из пула, причём умеет на уровне ядра. Mono вынужден сначала на одном потоке получить событие, как-то его предварительно обработать, а потом вызвать колбек на потоке из пула. Получаем нехилый оверхед.
В той же Майкросовтовской реализации, за файлы не поручусь, но при работе с сокетами по умолчанию используется IOCompletionPort — то есть все так же есть потоки, которые ожидают на порту завершения ввода-вывода и вызывают колбеки.

Другое дело, что на этом порту ждет совсем не один поток — а потому можно позволить себе вызывать колбек в нем же, избегая передачи колбека на выполнение в пул потоков. Но такого же поведения можно попытаться добиться и с epoll/kqueue.

В целом я не понимаю вот чего. Почему особенность ОС выдается за недостаток Mono? Такое ощущение, что стоит Microsoft открыть коды CLR — и linux сразу же научится вызывать асинхронный колбек сразу в потоке пула :)
Но такого же поведения можно попытаться добиться и с epoll/kqueue.
Нельзя. В случае с I/O completion на поток приходит одно событие и можно спокойно начать делать толстую синхронную операцию минуты этак на две, чем часто пользуются. На epoll тоже можно при желании повесить несколько потоков, но поток получает от ядра несколько событий, если обработчик зависнет на некоторое время, они будут точно так же висеть. То есть, автоматически получаем несовместимую с .NET реализацию. Дополнительно возникает проблема синхронизации всего этого добра.
А если в параметр maxevents передать 1?
Получим тормозной epoll.
То есть epoll в режиме получения событий по-одному тормозит, а IOCompletionPort — вроде как совсем не тормозит?

Тут одно из трех.
1. или epoll будет не настолько тормозить, чтобы перекрыть положительный эффект от выполнения колбека в том же потоке;

2. или колбек надо просто передавать в другой поток на выполнение — все равно быстрый epoll перекроет негативный эффект от лишних синхронизаций;

3. или надо признать, что IOCompletionPort попросту работает быстрее epoll — и Mono тут совсем ни при чем.
epoll работает быстрее IOCompletionPort за счёт возможности получить много событий за раз, под которую, собственно, и заточен. Из-за этого по бенчмаркам лучшее решение под Linux обыгрывает лучшее решение под Windows в несколько раз. Но, если через него пытаться эмулировать виндовую модель на линуксе, получаем тормоза при любом раскладе. При прочих равных Mono-вский сетевой стек будет проигрывать Java-вскому NIO. Собственно, из-за этого Kestrel и пишут на libuv, а не на System.Net.Sockets.
Если измерять способность перегонять байты из одного сокета в другой — не спорю, так и есть. Я бы тоже для такой задачи не стал использовать System.Net.Sockets.

Но в других задачах, где требуется еще и затратить немного процессорного времени на обработку — отрыв будет далеко не таким значительным. А асимптотика что у epoll, что у IOCompletionPort одинаковая — так что для масштабируемости ни то, ни другое — не помеха.

Кроме того, нельзя забывать, что NIO требует представления алгоритма в виде автомата — в то время как для System.Net.Sockets можно писать нормальный структурный код, с ветвлениями и циклами. Это библиотеки совершенно разного уровня сложности — и лучше подходят для разных задач.

Прошу не рассматривать этот комментарий как выражающий ненависть к NIO — лично я был бы только рад, появись аналог NIO в .NET. Точно так же как аналог асинхронных сокетов, я уверен, сильно помог бы многим Java-программистам.
См. FrameworkBenchmarks по ссылке выше. Там не сырые данные, там HTTP + JSON-сериализация, то есть, то самое «процессорного времени на обработку». Mono-вский сетевой стек очень медленно работает и является ботлнеком.
Попрошу обратить внимание, что в этих бенчмарках проверялся не столько сетевой стек Mono, сколько сетевой стек ASP.NET… Там и правда все довольно печально порой получается.
Кроме того, нельзя забывать, что NIO требует представления алгоритма в виде автомата
Вот тут как раз можно применить магию с async/await, позволяющую await-ить не только Task, а вообще совершенно произвольные вещи, была бы реализация await-ера.
Только вот любой await в конечном итоге сведется к колбеку, который надо вызвать по завершению операции… И мы опять возвращается к проблеме, в каком потоке его вызывать :)
Тут как раз можно в том же самом, т. к. проблема совместимости уже не стоит — мы заранее в спеках говорим о том, что из такого потока нельзя делать синхронные вызовы.
Можно, кстати, и в случае System.Net.Sockets в один поток работать — только надо эти синхронные вызовы отслеживать, и в случае вызова, который может оказаться синхронным, передавать задачу по прослушиванию epoll/kevents вместе со всеми необработанными событиями новому потоку из пула…
Можно, кстати, и в случае System.Net.Sockets в один поток работать — только надо эти синхронные вызовы отслеживать, и в случае вызова, который может оказаться синхронным, передавать задачу по прослушиванию epoll/kevents вместе со всеми необработанными событиями новому потоку из пула…
Это блокировка любого мьютекса и любой вызов unmanaged-кода. Невозможно предсказать, где поток может встать.

Перечитал наш диалог… Почему вы утверждаете, что в случае с NIO достаточно в спеках написать о недопустимости синхронных вызовов — а в случае с System.Net.Sockets "невозможно предсказать, где поток может встать."?


Если я умею "предсказывать" какой вызов синхронный в NIO — то я и в System.Net.Sockets сделаю то же самое. А если я считаю блокирующим любой вызов нативного кода — то и в NIO не смогу в один поток работать.

Я неправильно выразился и речь шла о реализации однопоточного режима для System.Net.Sockets внутри самого фреймворка, когда колбек, переданный в BeginReceive/BeginSend вызывается из I/O-потока, а не передаётся в пул. Этого нельзя было делать из соображений совместимости с имеющейся спекой, которая не накладывает ограничений вида «не занимай поток почём зря».

Да, с этой точки зрения согласен. И правда, упихнуть все в один поток когда спецификация говорит что колбеки вызываются в пуле — не получится.

Пусть это и было полтора года назад, но все-равно отпишусь. Увидел ваш комментарий, решил поискать как оно сделано в Kestrel. Нашел https://github.com/dotnet/corefxlab/tree/master/src/System.Net.Libuv
Судя по коммитам, код используется и обновляется, потому я решил провести пару собственных экспериментов. И эксперименты как-то гадко фейлились, пока я не обнаружил это:
            internal void Dispose()
            {
                unsafe
                {
                    Length = 0;
                    Buffer = IntPtr.Zero;
                    FreeBuffer(new Bytes((byte*)Buffer.ToPointer(), (int)Length));
                }
            }


В целом, подобная работа с памятью может перечеркнуть вообще весь смысл использования libuv и быть причиной плохих результатов aspnet-mvc-mono в приведенном бенчмарке (если там таки Kestrel).
Размышляю теперь, как бы помягче сообщить разработчикам, что они абьюзят свой буферпул, возвращая в него нули. Ну или может я чего-то не понимаю, мало ли.
Kestrel в те времена в природе не было.
По абузу буферпула — лучше всего создать issue у них же на гитхабе, приведя ссылку на код, показав, что буфер возвращается в пул после зануления переменных, они достаточно быстро на это дело отвечают.
После беглого просмотра так же могу сказать, что приведённый код не используется в Kestrel, это экспериментальный репозиторий.
В целом, ваш начальный комментарий как бы намекает, что Kestrel на то время был или его уже пилили.

Рад, что у них больше миллиона запросов :) Надеюсь, что это таки не на коде, приведенном выше.
Тикет на гитхаб написал. Может пригодится кому.
Ну его именно что пилили, в продакшне на тот момент использовать это было нельзя.
Ключевое словосочетание — «на некоторых тестах». В Mono очень большое количество проблем. Надо отдать разработчикам должное, за последнее время они многие проблемы победили, но, скажем, реализация GC и JIT уступает микрософтовской реализации. Да и некоторые отличия в реализации стандартных классов заставляют грустить.
Впрочем, в целом с вами согласен — моя позиция субъективна, она вызвана большим количеством шишек, которые я в своё время набил с Mono. Исправил на более объективный вариант.
Я уже отписывался в одной из новостей насчет JIT'a в .NET от MS — его официально заявленный объем (300к строк кода) примерно эквивалентен объему кода исполняющей машины моно целиком. Старались, выжали все, что могли.
Новости про CoreCLR и CoreFX хорошие. Надеюсь в будущем Mono будет лишь достойным аналогом Visual Studio для Linux, а все потроха отдаст на откуп нативному .Net
Уж лучше плагин к студии с поддержкой отладки через MDB (когда-то был, но его сейчас Xamarin замылили и никому не дают), а саму студию в wine запустить. MonoDevelop, увы, никогда не обеспечит тот же уровень комфорта.
Если бы к MonoDevelop можно было бы прикручивать плагины от VisualStudio, думаю, это решило бы почти все проблемы с комфортом. Увы, на данный момент с плагинами там крайне туго.
Mono 4 (а скорее всего и все версии), Mac OS X. Обнаружил высокий CPU Usage в программе. Изучение показало, что жрет Thread.Sleep(1). Смотрим в код, а там использован clock_nanosleep, отсутствующий из коробки в Mac OS X, FreeBSD, Solaris, etc. Видимо, он где-то подперт костылем, чтобы хоть как-то работал, но в реальности замена на usleep с другим коэффициентом решает проблему целиком. Но я не думаю, что буду пересобирать каждый раз свой Mono, значит в программе добавляется костыль с Interop на libc/usleep и проверкой ОС, а потенциально еще и с нарушениями работы Interrupt и Join. Вот так понемногу кросс-платформенная программа превращается в костыльного монстра. :(

А разработчикам Mono вы сообщили?

Еще нет. Только вчера нашел, утром перепроверил на свежую голову — никуда проблема не пропала и замена clock_nanosleep на usleep ее лечит.
Для начала просто тикет в гитхабе открою, а дальше уже посмотрим.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий