И мышонок, и лягушка. Универсальный Компилятор

    В серии про надежное программирование [1], [2] остался незаслуженно забытым Swift. Я, честно говоря, просто не считал его кроссплатформенным, а работающим исключительно для macOS/iOS. Тут случайно выяснилось, что Swift также поддерживается такой средой разработки, как RemObjects Elements.

    Оказалось, у неё Универсальный Компилятор. Умеет компилировать программы на C#, Go, Java, Oxygene Object Pascal, Swift для: Android, Cocoa (MacOS, iOS, tvOS), JVM, Linux (x64, armv6, aarch64), .NET/.NET Core/Mono, Native Windows(x86/x64), WebAssembly.

    Причем делает это практически в любых комбинациях язык → целевая система! Например, можно написать программу на Java, которая будет использовать WPF для целевой платформы .NET, и это все есть в примерах, идущих в поставке.

    Итак, представляю мини-заметку про RemObjects Elements, а заодно про надежность двух поддерживаемых в ней языков — Swift и Oxygene.

    Рисунок с сайта radionetplus


    А еще в дополнение к обсуждениям о проблеме интероперабельности разных сред исполнения — нативных, JVM, .NET, GC, ARC она оказалась просто невероятной, причем в обоих смыслах — так как при обсуждениях пришли к тому, что нормальная ИО невозможна.

    Вкратце о RemObjects Elements


    Это либо независимая среда разработки с отладчиком, в том числе и удаленным, и автодополнением кода для Windows или macOS, либо же интегрируется с Visual Studio, а для Cocoa обязателен XCode. Для Swift’a она бесплатна, для остального стоит от 199$ за версию для личных проектов, до 799$ за коммерческую лицензию для всех языков. За работу с клиент — серверными СУБД придется доплатить уже существенно.

    Water — так называется версия для Windows, довольно аскетична, визуального редактора форм в ней нет, но симпатизирует строгостью, да и установщик занимает всего 700МБ, не включая конечно же JRE, .NET, Android NDK итп.

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

    Бонусом к IDE идет много:

    • минимальный профайлер (в Water не работает, только текстовый файл вызовов ф-ций)
    • обфускатор
    • поддержка юнит-тестов
    • межъязыковой транслятор исходного кода с поддержкой C#, Swift, Java, Delphi, C, Obj-C в Oxygene, C#, Swift, Java, Go
    • утилита импорта для Go-библиотек с зависимостями. Кстати Go тулчейн пока в Бете
    • библиотека для аспектного-ориентированного программирования
    • некоторая поддержка миграции с Delphi/VCL

    Интероперабельность


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

    ИО здесь заключается в том, что программа умеет использовать только лишь возможности своей RTL _и_ целевой платформы для всех языков и их комбинаций. То есть для цели .NET можно использовать WPF, но не rt.jar, а для Native — только WinAPI или GTK соответственно. Но базовая библиотека, написанная на Oxygene, доступна везде, как и прослойка миграции с Delphi — кстати они доступны на гитхабе.

    Проект же может включать в себя подпроекты на разных языках, поддерживаемых RO. И с импортом внешних библиотек тоже есть проработанные варианты.

    Управление памятью будет использоваться от целевой платформы, например ARC для Cocoa. В детали я не вдавался, для Native по умолчанию используется GC, хотя есть выбор — проверено, есть в моем проекте на github.

    Добавлю одно наблюдение. Если программа писалась под ручное управление памятью (malloc/free), она элементарно заработает под ARC. А если программа была под ARC, то она без изменений будет работать под GC (кроме расчета на немедленную деструкцию).

    Надежность


    Oxygene


    Это в принципе старый знакомый Object Pascal, но на стероидах. Версия Oxygene for .NET также продавалась как Delphi Prism.

    Выглядит безопаснее за счет:

    • GC
    • поддержки контроля nullable
    • наличия контрактов и инвариантов
    • контроля регистра идентификаторов
    • возможности контроля отступов пар begin/end (непонятно как работает)
    • locked/lazy потокобезопасных методов класса
    • наличия using, в смысле RAII

    Минусы. GC же имеет и обратную сторону — временную недетерминированность, избыточное потребление памяти. Также в минусы можно записать, что масштаб фирмы и сообщества — небольшой и потому ошибок реализации вероятно больше, чем для техномонстров. Документация слабая, а родной фреймворк минимален, что может потянуть кучу “велосипедов” в проекты, если ваш таргет не .NET или JRE.

    С технической же стороны, в нем добавлено сахара относительно Delphi:

    • async/await/future, await — пока только для .NET
    • LINQ
    • generic/dynamic типы (отличаются от Delphi)
    • tuples
    • sequence/yield
    • string interpolation

    Интерлюдия. C#


    Ранее был отсеен из-за ограниченной кроссплатформенности, но с выходом .NET Core 3, и появлением нативного компилятора в том числе для Linux, стало чуть лучше. Дополнительно, язык наравне поддерживается в RemObjects с произвольным выбором таргетов.

    Собственно, с надежностью у C# более-менее все в порядке. Красоту портит GC, не очень удобная работа с nullable, что приводит к регулярным NPE, возможность получить исключение из невинно выглядящей функции, и проблемы при неудачном использовании LINQ.

    Вследствии GC и ресурсопотребления, не очень то подходит для Embedded. Тем более, что для ембеддед только интерпретатор без JIT .NET Micro Framework заброшен с 2015г, хотя и получил развитие как TinyCLR

    Теперь о Swift


    В общем и целом, C-подобный компилируемый в машинный код язык, один из новейших и созданный с учетом истории предшественников. Сделан Apple для себя, официальный компилятор есть еще под Ubuntu, переведен в Open Source.

    Возможности на уровне, надежности уделено приличное внимание. Могу придраться к возможности изменения константных классов и возможности спутать ссылочный класс со структурой — значением, впрочем тут как в C#.

    Управление памятью — в оригинале ARC, а у RemObjects для платформ, отличных от Apple — GC.
    Тяжеловат для Embedded, нет реалтаймовых способностей.

    Остальное, что бы я хотел видеть — почти все есть.Контрактов нет (в RemObjects есть нестандартные расширения), но возможна какая то их эмуляция через Property Observers или появившиеся в 5.2 @propertyWrapper (в RemObjects еще не реализовано)

    Стоит отметить тьюплы, именование параметров функций, array.insert(x, at: i) явный unwrap для Optional, да и в целом продуманная с ним работа.

    Существенным нюансом я бы назвал эволюцию языка. Многие примеры из официального учебника (5.2) просто не работают на том же Repl.it (5.0), не говоря уж о RemObjects, которые непонятно какой точно версии соответствуют.

    Реализация в RemObjects отличается от эталонной — Array тут ссылочный, а String иммутабельный, интерфейсы причем тоже отличаются от описанных в учебнике, да и интерфейсы библиотек мало того что свои, так и немного варьируются между платформами. Что привносит некоторое разнообразие в скучном процессе кодописания =)

    Итоговая сводная обновленная таблица надежности

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

    Качество компиляции


    Попробуем сделать программу на Swift, аналогичную Расстоянию Левенштейна в статье 0xd34df00d и сравнить ее с программами оттуда же.

    Swift
    /*  Нахождение расстояния Левенштейна
    	Версия для Windows.Native, Elements.Island
    */
    
    class Program {
    	 public static func LevDist(_ s1: [UInt8], _ s2: [UInt8]) -> Int32! {
    		 let m = s1.count
    		 let n = s2.count
    		 //  Edge cases.
    		 if m == 0 {
    			 return n
    		 } else {
    			 if n == 0 {
    				 return m
    			 }
    		 }
    
    		 var range = Range(0 ... (n + 1))
    		 var v0: Swift.Array<Int64> = range.GetSequence()  //17ms
    
    //         var v1 = v0;    // in Swift must copy, but RO make a ref
    		 var v1 = Swift.Array<Int64>(v0);
    
    		 var i = 0
    		 while i < m {
    			 v1[0] = i + 1
    			 var j = 0
    			 while j < n {
    				 let substCost = (s1[i] == s2[j] ? v0[j] : v0[j] + 1)
    				 let delCost = v0[j + 1] + 1
    				 let insCost = v1[j] + 1
    				 v1[j + 1] = substCost < delCost ? substCost : delCost
    				 if insCost < v1[j + 1] {
    					 v1[j + 1] = insCost
    				 }
    				 j += 1
    			 }
    
    			 let temp = v0; v0 = v1; v1 = temp // copy refs
    			 i += 1
    		 }
    		 return v0[n]
    	 }
     }
    
    
    var b1 = [UInt8](repeating: 61 as! UInt8, count: 20000)
    var b2: [UInt8] = b1
    var b3 = Swift.Array<UInt8>(repeating: UInt8(63), count: 20000)
    
    print("Start Distance");
    
    var execTimer: StopWatch! = StopWatch()
    execTimer.Start()
    
    print(Program.LevDist(b1, b2))
    print(Program.LevDist(b1, b3))
    
    execTimer.Stop()
    
    var execTime: Float64 = execTimer.ElapsedMilliseconds / 1000.0 / 10
    
    print("\(execTime) s")
    return 0


    Остальные с минимальными изменениями, хотя конечно же наиболее жесткие огрехи я подровнял, на гитхабе. Кто не боится и лень качать среду и компилировать, там же есть и бинарники, при желании можно позапускать относительно безопасные JVM и NET варианты, или же в песочнице.

    Изменения касаются правок для RO, выноса Encoding.UTF8.GetBytes из замерной части, ну и выяснилось, что в паскалевской версии формирование строк в виде for i := 1 to 20000 do s1 := s1 + 'a'; может занимать до 4х минут (вне замерной части).

    Таблица результатов (отнормирована по Java, как универсальной)
    Язык Платформа Компилятор Время, с Норма Параметры
    C# NET4.7 NET4.7.3062 13,075 445% -o+
    C# NET Core 3.1 Elements 10.0 11,327 385% release, >dotnet cs.dll
    C# Win x64 Elements 10.0 6,312 215% release
    Java JVM JDK 1.8.0.242 2,941 100% -g:none
    $java LevDist
    Java JVM Elements 10.0 2,851 97% release
    $java -jar java8.jar
    Oxygene NET4.7 Elements 10.0 22,079 751% release, range checking off
    Oxygene Win x64 Elements 10.0 9,421 320% release, range checking off
    Swift JVM Elements 10.0 23,733 807% release
    $java -jar swiftjvm.jar
    Swift Win x64 Elements 10.0 131,400 4468% release
    Go (Beta) Win x64 Elements 10.0 89,243 3034% release
    Было бы интересно еще протестировать WASM, но я упустил этот момент.

    Измерения проводились в виртуальной машине с Win7, примерно втрое замедляющей эту задачу относительно реального моего железа, из 5 замеров брались средние 3.

    Вывод пока один — жизнь на Swift’e возможна и за пределами яблочной экосистемы. Но пока небыстрая. Гипотеза, что есть проблема с Win7, поскольку для нативной компиляции используется Win10 SDK, не подтвердилась — на Server 2016 1607 времена примерно те же.

    [1] Надежное программирование в разрезе языков — нубообзор. Часть 1
    [2] Надежное программирование в разрезе языков. Часть 2 — Претенденты
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

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

      0
      А Swift для Windows как там реализован?
      Какие вообще есть реализации?
      Есть ли реализации через MinGW?
        0
        Там, кроме Go, везде собственный Elements Compiler, есть библиотека RemObjects.Elements.LLVM.dll, но где она используется, я через ProcessExplorer не увидел.

        Второй вопрос не понят
          0

          Второй вопрос — возможно кто-то знает какие нибудь реализации swift для windows кроме RemObjects. Я предположил что может быть существует реализация типа mingw, возможно были попытки портировать с ubuntu?

        +1
        Насколько я понял, это псевдокомпиляция в свою виртуальную машину RemObjects, а под платформы у них просто свой runtime. Или я что-то упустил?
          0
          К сожалению, я видимо недостаточно подробно пишу(

          Нет. Компиляция производится в код целевой платформы — соответствующий машинный (x86/x64/ARM) для Island и Cocoa, байт код JVM или .NET. Бинарники есть (вернул) на гитхабе, можно посмотреть заодно и размеры или сунуть в декомпилятор.

          Совершенно аналогично, компилируются кроссплатформенные рантайм-библиотеки.

          При необходимости, родные для платформы библиотеки подключаются в зависимостях.

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

        Самое читаемое