1. 10

  2. 3

    Perhaps it is worth pointing out that these “native” executables essentially wrap pre-jitted code into a convenient single file with fast startup time. These two features (single executable and fast startup time) can be attractive, but the price paid in most cases is runtime performance. In the majority of cases, dart code jitted against the VM will be more performant due to local type information and runtime optimization, which is (to a degree)) unavailable in a pre-jitted version.

    Contrast the needs of a smallish command-line utility, versus a long running complex server application.

    It should be noted that the AOT compiled code is always run within the context of a minimal Dart VM. In that way it is for many something different than “a traditional native executable”.

    1. 1

      I wish Dart (and Go and Rust) could link against their runtime in a shared library (like C does) rather than embedding them.

      1. 0

        There is -linkshared flag in Go, but the support is limited to Linux and requires CGo/external linker.

        I don’t find it useful for dev environment and usually stick with Go linker that produces statically linked executables and occasionally adds dynamic libs as dependencies, e.g. on Darwin/macOS public system API is exposed via /usr/lib/libSystem.B.dylib instead of direct syscalls. Unlike the traditional ld (i.e. external linker), this does not require the existence of target dynamic library on the host system so you can cross compile without getting errors about undefined symbols (heck, why ld even tries to verify dynamic libraries in the first place? That’s ld.so responsibility). Unfortunately, such behavior is limited to a standard library and Cgo-generated code.

      2. [Comment from banned user removed]

        1. 10

          This comment is also misleading. As a Dart developer patiently explained to you in your closed issue, it may not be a native executable, but instructions are native machine code. Better word to use for your “interpreter” is “loader”. You wouldn’t say Windows .exe is not native, because it is “interpreted” by Wine on Linux.

          1. 0

            When you compile C, C++, C#, Go, Rust or any number of other true compiled languages, they dont dump some interpreter or virtual machine inside your output.

            They compile your program into machine code, that runs natively outside the scope and abstraction of an interpreter or virtual machine.

            Thats not what dart2native does. It compiles to bytecode, then cats that with an interpreter or virtual machine. Dart marketing is being clearly misleading here, whether intentional or not. Any cursory search would reveal that:


            1. 9

              dart2native compiles to machine code, not bytecode.

              Let us avoid petty terminology war. I think we can agree on following technological points. 1. dart2native does not suffer from interpretation overhead. 2. dart2native produces a single file, easing deployment. 3. dart2native includes Dart runtime, for example for GC, but this is no different from Go. 4. dart2native executable is incompatible with strip, which is inconvenient.

              I agree 4 is desirable to fix. 3 is too, but probably impossible given GC. You are correct in a small technical point and blowing it up out of proportion. I (and no doubt others) noticed this is your modus operandi; I advise you to stop. For my life I don’t understand why you are repeating “bytecode” strawman, which is entirely false.

              1. 0
                1. dart2native does not suffer from interpretation overhead.

                Compared to what? Compared to itself using the dart tool? Sure I will cede that. But compares to any true compiled language, thats a hard no. Did you have any data to suggest otherwise?

                1. dart2native includes Dart runtime, for example for GC, but this is no different from Go.

                To my knowledge, Go doesnt have a “runtime”, it compiles code similar to C. Hence why you can do go build -ldflags -s prog.go, as Go uses a linker, unlike Dart. Again, do you have evidence otherwise?

                1. 9

                  Go runtime: https://golang.org/pkg/runtime/ Even C generally has a (small) runtime, C++ will have a slightly larger one, to support exceptions and RTTI.

                  1. 6

                    I cede that Dart, which compiles to machine code, not bytecode, is slower than other compilers. But this is no different from Go being slower than C or GCC -O0 being slower than -O3. Interpretation overhead, means comparing compiler to interpreter.

                    I cede that Dart runtime is included as a whole and not linked. Linking can remove unused portion of runtime and not supporting linking results in larger executables. I agree this is desirable to fix.

                    1. 6

                      Go has a large runtime. Why do you think a Go binary with an empty main is 788K (go1.13.4 linux/amd64)? Where do you think features like garbage collection, type assertions, panic, and reflection come from?

                      I see no evidence that Dart compiles to Bytecode in the article or the two issues you linked. One of the devs specifically and clearly states that the binary contains an “AOT [ahead-of-time] snapshot”, and states:

                      The AOT snapshot contains actual machine code which is directly executed by the CPU. This code was produced ahead of time (AOT) from your Dart source. There is no translation or interpretation. There is just a loading/linking step, which is similar to a step that OS does for you when you load a native executable format.

                      You are confusing several different things - executable format (ELF, etc) and format in which your Dart code is stored (eg machine code vs bytecode vs source).

                      As I see it, the possibilities here are that either you’re mistaken in your assumption that “includes runtime” must mean “runs bytecode”, or this developer is brazenly and unashamedly lying about all of this in your face. I think the former is considerably more likely.

                      Go uses a linker, unlike Dart. Again, do you have evidence otherwise?

                      The above comment (posted a month ago) clearly states that Dark has a linking step.

                  2. 8

                    I think you’re incorrect.

                    You can compile your dart script to a native code blob with this command line:

                    $ dart --snapshot-kind=app-jit --snapshot=out.bin yourscript.dart

                    It will create the file out.bin with the same data blob that it’s included in the executable ELF file produced by dart2native, but this time it will be without the ELF container.

                    This blob file contains lots of metadata, but on the end of this file, there’s actual native code. You can read it:

                    $ objdump -D -b binary -m i386:x86-64 -Maddr64,data64 out --start-address=$((0x371030)) | less

                    This produces normal assembly dump of x86_64 instructions. It wouldn’t be possible if it weren’t native code. Excerpt:

                      378bde:       55                      push   %rbp
                      378bdf:       48 89 e5                mov    %rsp,%rbp
                      378be2:       41 54                   push   %r12
                      378be4:       41 57                   push   %r15
                      378be6:       4d 8b 7c 24 27          mov    0x27(%r12),%r15
                      378beb:       48 83 ec 08             sub    $0x8,%rsp
                      378bef:       49 8b 86 b8 00 00 00    mov    0xb8(%r14),%rax
                      378bf6:       48 89 45 e8             mov    %rax,-0x18(%rbp)

                    I’ve obtained start-address visually by inspecting this file by a hex editor; search for long series of NULL bytes followed by short series of 0xCC bytes. 0xCC is a x86 instruction called int 3, and is commonly used either as a soft breakpoint, or padding between functions or sections.

                    If you’ll have problems locating this section, you can read a 32-bit value from offset 0x18 from the blob file (mine was 0x36f1e0), add 0x1000, and jump to resulting offset (mine was 0x3701e0). Align the offset to the next 0x1000, and you’ll be near the native code section. Search for first sequence of 0xCC bytes and you can start decompilation from here.

                2. 2

                  I am a bit befuddled as to why these are not real native executables in your eyes, and also where that sort of value judgment is coming from, e.g. what is the concern here?