January 06, 2014
On 1/5/14 8:44 PM, Kenji Hara wrote:
> Honestly, "lazy import" (== defer loading module file and running
> semantic analysis for symbol search) would improve compilation speed for
> selective imports and static imports, but it would have no merit for
> basic imports.
>
> So precisely, "all imports lazy would be a net large win." is not correct.

Consider:

import std.stdio;
void main() { writeln("Hello, world!"); }

Currently std.stdio and all modules transitively imported by it would be opened.

With lazy imports, std.stdio gets opened and then writeln() gets semantically analyzed. Only modules required by writeln() itself will actually be opened. Big difference.


Andrei
January 06, 2014
2014/1/6 Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org>

> On 1/5/14 8:44 PM, Kenji Hara wrote:
>
>> Honestly, "lazy import" (== defer loading module file and running semantic analysis for symbol search) would improve compilation speed for selective imports and static imports, but it would have no merit for basic imports.
>>
>> So precisely, "all imports lazy would be a net large win." is not correct.
>>
>
> Consider:
>
> import std.stdio;
> void main() { writeln("Hello, world!"); }
>
> Currently std.stdio and all modules transitively imported by it would be opened.
>
> With lazy imports, std.stdio gets opened and then writeln() gets
> semantically analyzed. Only modules required by writeln() itself will
> actually be opened. Big difference.


Thanks for explanation. Indeed it would be big difference.

Kenji Hara


January 06, 2014
On Sun, Jan 05, 2014 at 09:22:09PM -0800, Andrei Alexandrescu wrote:
> On 1/5/14 8:44 PM, Kenji Hara wrote:
> >Honestly, "lazy import" (== defer loading module file and running semantic analysis for symbol search) would improve compilation speed for selective imports and static imports, but it would have no merit for basic imports.
> >
> >So precisely, "all imports lazy would be a net large win." is not correct.
> 
> Consider:
> 
> import std.stdio;
> void main() { writeln("Hello, world!"); }
> 
> Currently std.stdio and all modules transitively imported by it would be opened.
> 
> With lazy imports, std.stdio gets opened and then writeln() gets
> semantically analyzed. Only modules required by writeln() itself
> will actually be opened. Big difference.
[...]

It would be even better if only those modules required by the specific instantiation of writeln() being used would be loaded. So if you only instantiate writeln() with string arguments, then std.format doesn't even need to be pulled in.

My guess is that this alone would reduce std.stdio template bloat by about 10-20% or so, possibly more, because most uses of writeln() actually only need a small part of the entire code that implements it.


T

-- 
Never step over a puddle, always step around it. Chances are that whatever made it is still dripping.
January 06, 2014
On 1/5/2014 9:22 PM, Andrei Alexandrescu wrote:
> On 1/5/14 8:44 PM, Kenji Hara wrote:
>> Honestly, "lazy import" (== defer loading module file and running
>> semantic analysis for symbol search) would improve compilation speed for
>> selective imports and static imports, but it would have no merit for
>> basic imports.
>>
>> So precisely, "all imports lazy would be a net large win." is not correct.
>
> Consider:
>
> import std.stdio;
> void main() { writeln("Hello, world!"); }
>
> Currently std.stdio and all modules transitively imported by it would be opened.
>
> With lazy imports, std.stdio gets opened and then writeln() gets semantically
> analyzed. Only modules required by writeln() itself will actually be opened. Big
> difference.

import bar;
import foo;

Importing foo can never be done lazily if there are unqualified references to symbols in bar.

January 06, 2014
On 1/5/14 10:31 PM, Walter Bright wrote:
> On 1/5/2014 9:22 PM, Andrei Alexandrescu wrote:
>> On 1/5/14 8:44 PM, Kenji Hara wrote:
>>> Honestly, "lazy import" (== defer loading module file and running
>>> semantic analysis for symbol search) would improve compilation speed for
>>> selective imports and static imports, but it would have no merit for
>>> basic imports.
>>>
>>> So precisely, "all imports lazy would be a net large win." is not
>>> correct.
>>
>> Consider:
>>
>> import std.stdio;
>> void main() { writeln("Hello, world!"); }
>>
>> Currently std.stdio and all modules transitively imported by it would
>> be opened.
>>
>> With lazy imports, std.stdio gets opened and then writeln() gets
>> semantically
>> analyzed. Only modules required by writeln() itself will actually be
>> opened. Big
>> difference.
>
> import bar;
> import foo;
>
> Importing foo can never be done lazily if there are unqualified
> references to symbols in bar.

Yah, but modules transitively imported in foo and bar need not be loaded eagerly. That's where the win comes from. Took me a while to figure.

Andrei

January 07, 2014
On 12/21/2013 10:43 PM, Andrej Mitrovic wrote:
> The compiler matches the missing symbol with the selective import "writeln", so it knows it only has to load std.stdio, *but not* std.algorithm.
>
> Test-case 3:
> -----
> import std.stdio : writeln;
> import std.algorithm : map;
> import std.range;  // might be here (compiler doesn't know)
> import std.container;  // or here
>
> void main()
> {
>     "foo".front;
> }
> -----
>
> The compiler tries to find a selective import "front", but it doesn't find it. What's important here is that it still does not have to load std.stdio or std.algorithm, as we're explicitly loading only a set of symbols which were never referenced from the test.d module.
>
> So the next step here is for the compiler to try and load each module in sequence (probably via the declaration order, first std.container), and if there's a match the compiler would stop loading other modules (no need to load std.container if std.range has "front").
There is a flaw in that design. What if std.range and std.container both have a matching .front? If you stop after the first you'll not find that conflict. If you search both you'll not have any speed gain.

January 07, 2014
On Monday, 6 January 2014 at 07:02:03 UTC, Andrei Alexandrescu wrote:
> On 1/5/14 10:31 PM, Walter Bright wrote:
>> On 1/5/2014 9:22 PM, Andrei Alexandrescu wrote:
>>> On 1/5/14 8:44 PM, Kenji Hara wrote:
>>>> Honestly, "lazy import" (== defer loading module file and running
>>>> semantic analysis for symbol search) would improve compilation speed for
>>>> selective imports and static imports, but it would have no merit for
>>>> basic imports.
>>>>
>>>> So precisely, "all imports lazy would be a net large win." is not
>>>> correct.
>>>
>>> Consider:
>>>
>>> import std.stdio;
>>> void main() { writeln("Hello, world!"); }
>>>
>>> Currently std.stdio and all modules transitively imported by it would
>>> be opened.
>>>
>>> With lazy imports, std.stdio gets opened and then writeln() gets
>>> semantically
>>> analyzed. Only modules required by writeln() itself will actually be
>>> opened. Big
>>> difference.
>>
>> import bar;
>> import foo;
>>
>> Importing foo can never be done lazily if there are unqualified
>> references to symbols in bar.
>
> Yah, but modules transitively imported in foo and bar need not be loaded eagerly. That's where the win comes from. Took me a while to figure.
>
> Andrei

Right, but this fails quite quickly. Consider this:

//----
module Foo;
import tons_of_imports_here;

void foo()
{
    unqualified_call();
}
//----
import Foo;

void main()
{
    foo();
}
//----

The *instant* the implementation makes an unqualified call, you *have* to import *all* the imports of the module, to figure out what to call.

Given that you *probably* imported "foo" with the plan to *use* one of its functions, you'll encounter an unqualified call sooner rather than later, and any "win" will promptly be lost.
January 07, 2014
On 1/7/14, Robert Schadek <realburner@gmx.de> wrote:
> There is a flaw in that design. What if std.range and std.container both have a matching .front? If you stop after the first you'll not find that conflict. If you search both you'll not have any speed gain.

Ah damn, you're right. It was too good to be true.
January 07, 2014
On Tuesday, 7 January 2014 at 12:02:32 UTC, Andrej Mitrovic wrote:
> On 1/7/14, Robert Schadek <realburner@gmx.de> wrote:
>> There is a flaw in that design. What if std.range and std.container both
>> have a matching .front? If you stop after the first you'll not find that
>> conflict. If you search both you'll not have any speed gain.
>
> Ah damn, you're right. It was too good to be true.

Still pretty good with static / selective imports, just no magic
;)
January 07, 2014
On 1/7/14 3:25 AM, monarch_dodra wrote:
> Given that you *probably* imported "foo" with the plan to *use* one of
> its functions, you'll encounter an unqualified call sooner rather than
> later, and any "win" will promptly be lost.

It's not a one- or two-levels win, it's a transitive win. An unqualified call in one implementation would trigger only one level of import.

That said I agree it's suboptimal, but it's a net improvement in the compiler that requires zero changes to source code. According to Walter it would also get rid of some forward declarations issues.


Andrei