Programming in D – Tutorial and Reference
Ali Çehreli

Other D Resources

Pragmas

Pragmas are a way of interacting with the compiler. They can be for providing special information to the compiler as well as getting information from it. Although they are useful in non-templated code as well, pragma(msg) can be helpful when debugging templates.

Every compiler vendor is free to introduce their special pragma directives in addition to the following mandatory ones:

pragma(msg)

Prints a message to stderr during compilation. No message is printed during the execution of the compiled program.

For example, the following pragma(msg) is being used for exposing the types of template parameters, presumably during debugging:

import std.string;

void func(A, B)(A a, B b) {
    pragma(msg, format("Called with types '%s' and '%s'",
                       A.stringof, B.stringof));
    // ...
}

void main() {
    func(42, 1.5);
    func("hello", 'a');
}
Called with types 'int' and 'double'
Called with types 'string' and 'char'
pragma(lib)

Instructs the compiler to link the program with a particular library. This is the easiest way of linking the program with a library that is already installed on the system.

For example, the following program would be linked with the curl library without needing to mention the library on the command line:

import std.stdio;
import std.net.curl;

pragma(lib, "curl");

void main() {
    // Get this chapter
    writeln(get("ddili.org/ders/d.en/pragma.html"));
}
pragma(inline)

Specifies whether a function should be inlined or not.

Every function call has some performance cost. Function calls involve passing arguments to the function, returning its return value to the caller, and handling some bookkeeping information to remember where the function was called from so that the execution can continue after the function returns.

This cost is usually insignificant compared to the cost of actual work that the caller and the callee perform. However, in some cases just the act of calling a certain function can have a measurable effect on the program's performance. This can happen especially when the function body is relatively fast and when it is called from a short loop that repeats many times.

The following program calls a small function from a loop and increments a counter when the returned value satisfies a condition:

import std.stdio;
import std.datetime.stopwatch;

// A function with a fast body:
ubyte compute(ubyte i) {
    return cast(ubyte)(i * 42);
}

void main() {
    size_t counter = 0;

    StopWatch sw;
    sw.start();

    // A short loop that repeats many times:
    foreach (i; 0 .. 100_000_000) {
        const number = cast(ubyte)i;

        if (compute(number) == number) {
            ++counter;
        }
    }

    sw.stop();

    writefln("%s milliseconds", sw.peek.total!"msecs");
}

The code takes advantage of std.datetime.stopwatch.StopWatch to measure the time it takes executing the entire loop:

674 milliseconds

The -inline compiler switch instructs the compiler to perform a compiler optimization called function inlining:

$ dmd deneme.d -w -inline

When a function is inlined, its body is injected into code right where it is called from; no actual function call happens. The following is the equivalent code that the compiler would compile after inlining:

    // An equivalent of the loop when compute() is inlined:
    foreach (i; 0 .. 100_000_000) {
        const number = cast(ubyte)i;

        const result = cast(ubyte)(number * 42);
        if (result == number) {
            ++counter;
        }
    }

On the platform that I tested that program, eliminating the function call reduced the execution time by about 40%:

407 milliseconds

Although function inlining looks like a big gain, it cannot be applied for every function call because otherwise inlined bodies of functions would make code too large to fit in the CPU's instruction cache. Unfortunately, this can make the code even slower. For that reason, the decision of which function calls to inline is usually left to the compiler.

However, there may be cases where it is beneficial to help the compiler with this decision. The inline pragma instructs the compiler in its inlining decisions:

These pragmas can affect the function that they appear in, as well as they can be used with a scope or colon to affect more than one function:

pragma(inline, false) {
    // Functions defined in this scope should not be inlined
    // ...
}

int foo() {
    pragma(inline, true);  // This function should be inlined
    // ...
}

pragma(inline, true):
// Functions defined in this section should be inlined
// ...

pragma(inline):
// Functions defined in this section should be inlined or not
// depending on the -inline compiler switch
// ...

Another compiler switch that can make programs run faster is -O, which instructs the compiler to perform more optimization algorithms. However, faster program speeds come at the expense of slower compilation speeds because these algorithms take significant amounts of time.

pragma(startaddress)

Specifies the start address of the program. Since the start address is normally assigned by the D runtime environment, it is very unlikely that you will ever use this pragma.

pragma(mangle)

Specifies that a symbol should be name mangled differently from the default name mangling method. Name mangling is about how the linker identifies functions and their callers. This pragma is useful when D code needs to call a library function that happens to be a D keyword.

For example, if a C library had a function named override, because override happens to be a keyword in D, the only way of calling it from D would be through a different name. However, that different name must still be mangled as the actual function name in the library for the linker to be able to identify it:

/* If a C library had a function named 'override', it could
 * only be called from D through a name like 'c_override',
 * mangled as the actual function name: */
pragma(mangle, "override")
extern(C) string c_override(string);

void main() {
    /* D code calls the function as c_override() but the
     * linker would find it by its correct C library name
     * 'override': */
    auto s = c_override("hello");
}