DIP10005: Dependency-Carrying Declarations is now available for community feedback

Chris Wright via Digitalmars-d digitalmars-d at puremagic.com
Thu Dec 22 18:53:37 PST 2016


On Thu, 22 Dec 2016 10:17:33 +0000, Joakim wrote:
> Opening a file or 10 is extremely cheap compared to all the other costs
> of the compiler.  Purely from a technical cost perspective,
> I'm not sure even scoped imports were worth it, as my simple
> investigation below suggests.

The compiler doesn't merely have to open the file. It has to run at least 
partial semantic analysis on it in order to locate symbols. When 
templates are involved, this can become quite expensive.

With static and selective imports, this step can be entirely avoided -- 
the current file tells you which files contain which declarations. With 
either, the compiler can tell exactly which file contains which 
declarations, so it can avoid pulling in anything beyond what's strictly 
necessary.

With declaration-scoped imports, you avoid the same step. However, it 
requires the maintainer's discipline to reduce the declaration's imports 
to the minimal possible set of imports.

For instance, I have a declaration:

  import myapp.users, std.socket;
  bool isUserOnline(User user, Socket userSocket);

I decide that this needs compilation time optimizations. The current way:

  static import myapp.users, std.socket;
  bool isUserOnline(myapp.users.User user, std.socket.Socket socket);

And Andrei's way:

  bool isUserOnline(User user, Socket userSocket)
  import myapp.users, std.socket;

I refactor things so that this check finds the user socket on its own. 
The current way:

  static import myapp.users, std.socket;
  bool isUserOnline(myapp.users.User user);

Eh, I forgot I don't need std.socket anymore, but this costs a few 
microseconds of compiler time to add it to the current symbol table. It 
has to allocate a lazily expanded module stub. Shouldn't be a huge deal.

Andrei's way:

  bool isUserOnline(User user)
  import myapp.users, std.socket;

Again, I forgot to update the imports, but this time the compiler has to 
read std.socket from disk, parse it, run semantic on it, and import all 
its top-level symbols into the scope's symbol table. Because there's 
nothing here that says the 'User' type is in myapp.users instead of 
std.socket.

---

In point of fact, selective and static imports should be *faster* than 
Andrei's way. Consider:

  static import myapp.users, std.socket;
  bool isUserOnline(myapp.users.User user, std.socket.Socket socket);

This has to locate a declaration named `User` in myapp.users, and it has 
to locate a declaration named `Socket` in std.socket.

But let's look at Andrei's way:

  bool isUserOnline(User user, Socket userSocket)
  import myapp.users, std.socket;

Here, the compiler has to search *both* myapp.users and std.socket for a 
declaration named `User`, then it has to search both for `Socket`. (Even 
if it finds `User` in the first, it still needs to search the second in 
case both define that symbol.)

You go from O(distinct number of types referenced) lookups to O(types * 
imports).

Granted, you'll usually have between one and three types, between one and 
three imports, so the point is a bit less salient.


More information about the Digitalmars-d mailing list