September 22, 2012
On 2012-09-21 21:59, Andrei Alexandrescu wrote:

> I think you have discovered a major issue. Ideas on how to attack this?

The standard way to solve this would be to move the initialization code from a static constructor to a function what will be called and do the initialization lazy. But since this several layers of string mixins what will make it more complicated.

Can you give a short example of how the mixed in code will look like?

-- 
/Jacob Carlborg
September 22, 2012
On Friday, 21 September 2012 at 20:43:13 UTC, Jonathan M Davis wrote:
> On Friday, September 21, 2012 15:59:31 Andrei Alexandrescu wrote:
>> On 9/19/12 4:11 PM, "Øivind" wrote:
>> > New question for you :)
>> > 
>> > To register benchmarks, the 'scheduleForBenchmarking' mixin inserts a
>> > shared static initializer into the module. If I have a module A and a
>> > module B, that both depend on eachother, than this will probably not
>> > work..? The runtime will detect the init cycle and fail with the
>> > following error:
>> > 
>> > "Cycle detected between modules with ctors/dtors"
>> > 
>> > Or am I wrong now?
>> 
>> I think you have discovered a major issue. Ideas on how to attack this?
>
> Some of us have been asking for ages for the ability to mark a static
> constructor as not depending on anything so that the runtime _doesn't_ think
> that there's a circular dependency, but Walter has been against the idea when
> it's been brought up. That would _really_ help here.
>
> Without redesigning std.benchmark so that it doesn't use static constructors,
> I don't know how you can fix that. Normally, if you really need a static
> constructor, you go through the pain of creating a separate module which does
> the initialization for you (like std.stdio does). But that won't work in this
> case, because you're mixing it in. So, unless you can redesign it so that
> std.benchmark doesn't require static constructors, it may have to be a
> limitation of std.benchmark that it can't be used where it would create a
> circular dependency.
>
> Unfortunately, the circular dependency issue makes static constructors almost
> useless outside of isolated cases, even though they rarely actually have
> circular dependencies. It's one of the few places in D that I'd say that
> there's a major design flaw.
>
> - Jonathan M Davis


Is there a way to solve the dependency issue without forbidding static constructors in modules with cyclic dependencies?

I.e. for two modules with static init referencing eachother, an analysis of the static init code could be used to detect circular dependencies instead of the import statements?
September 22, 2012
On 9/22/12 8:28 AM, "Øivind" wrote:
> Is there a way to solve the dependency issue without forbidding static
> constructors in modules with cyclic dependencies?

I think an idea just occurred to me. The rules for static ctors and dtors were invented before "import" was allowed inside a scope. We could have taken advantage of that.

Say we restrict symbol visibility inside static cdtors to ONLY symbols within the current module. If some static cdtor needs a symbol from a different module, it must import it explicitly (even if the current module already imports it).

In this setup it should be possible to compute, in a fine-grained manner, the dependencies of static cdtors.

Unfortunately that would be a breaking change.


Andrei
September 22, 2012
On Saturday, 22 September 2012 at 13:03:06 UTC, Andrei Alexandrescu wrote:
> On 9/22/12 8:28 AM, "Øivind" wrote:
>> Is there a way to solve the dependency issue without forbidding static
>> constructors in modules with cyclic dependencies?
>
> I think an idea just occurred to me. The rules for static ctors and dtors were invented before "import" was allowed inside a scope. We could have taken advantage of that.
>
> Say we restrict symbol visibility inside static cdtors to ONLY symbols within the current module. If some static cdtor needs a symbol from a different module, it must import it explicitly (even if the current module already imports it).
>
> In this setup it should be possible to compute, in a fine-grained manner, the dependencies of static cdtors.
>
> Unfortunately that would be a breaking change.
>
>
> Andrei

It gets a bit ugly maybe, but we could do a mix of the proposals that have come before and this one, e.g. add a @nocycliccheck (or similar) to the static constructor,  and in that case only allow access to current module and those imorted inside the ctor scope..
September 22, 2012
On Saturday, 22 September 2012 at 13:25:47 UTC, Øivind wrote:
> On Saturday, 22 September 2012 at 13:03:06 UTC, Andrei Alexandrescu wrote:
>> On 9/22/12 8:28 AM, "Øivind" wrote:
>>> Is there a way to solve the dependency issue without forbidding static
>>> constructors in modules with cyclic dependencies?
>>
>> I think an idea just occurred to me. The rules for static ctors and dtors were invented before "import" was allowed inside a scope. We could have taken advantage of that.
>>
>> Say we restrict symbol visibility inside static cdtors to ONLY symbols within the current module. If some static cdtor needs a symbol from a different module, it must import it explicitly (even if the current module already imports it).
>>
>> In this setup it should be possible to compute, in a fine-grained manner, the dependencies of static cdtors.
>>
>> Unfortunately that would be a breaking change.
>>
>>
>> Andrei
>
> It gets a bit ugly maybe, but we could do a mix of the proposals that have come before and this one, e.g. add a @nocycliccheck (or similar) to the static constructor,  and in that case only allow access to current module and those imorted inside the ctor scope..

We would probably not call it '@nycycliccheck' since you propose to still do these checks, but only local imports :) Would need another name for it.

September 22, 2012
On 2012-09-22, 15:28, Øivind wrote:

>> It gets a bit ugly maybe, but we could do a mix of the proposals that have come before and this one, e.g. add a @nocycliccheck (or similar) to the static constructor,  and in that case only allow access to current module and those imorted inside the ctor scope..
>
> We would probably not call it '@nycycliccheck' since you propose to still do these checks, but only local imports :) Would need another name for it.

@noglobalimports. :p

-- 
Simen
September 22, 2012
On 2012-09-22, 15:04, Andrei Alexandrescu wrote:

> On 9/22/12 8:28 AM, "Øivind" wrote:
>> Is there a way to solve the dependency issue without forbidding static
>> constructors in modules with cyclic dependencies?
>
> I think an idea just occurred to me. The rules for static ctors and dtors were invented before "import" was allowed inside a scope. We could have taken advantage of that.
>
> Say we restrict symbol visibility inside static cdtors to ONLY symbols within the current module. If some static cdtor needs a symbol from a different module, it must import it explicitly (even if the current module already imports it).
>
> In this setup it should be possible to compute, in a fine-grained manner, the dependencies of static cdtors.
>
> Unfortunately that would be a breaking change.

That *is* neat. I guess putting it on the deprecation path could work.
This is a change we'd really, *really* like to see.

-- 
Simen
September 22, 2012
On 9/19/12 3:13 AM, Jacob Carlborg wrote:
> On 2012-09-17 23:13, Jens Mueller wrote:
>
>> Post all feedback to this thread. Constructive feedback is very much
>> appreciated.
>>
>> To conclude in more Andrei like words: Happy destruction!
>
> * Why is "scheduleForBenchmarking" a string? Can't it be a template mixin?

Good point, I'll look into it.

> * What's the most appropriate way of just timing a block of code?
> Something like this:
>
> auto time = benchmark!({ /* some code */ })(1);
>
> If that's the case then I suggest setting a default value of "1" for the
> "n" parameter.

A default value of n would depend on the speed of the function and the granularity of the system's timer. That overload of benchmark is imperfect (rather rigid) but we must keep it for backwards compatibility.

> * If I want to format the printed result differently, say in HTML, how
> would I do that? Should I use the "benchmark" function and iterate the
> BenchmarkResult array?

That is correct. printBenchmarks() does not use any magic - it just looks at that same array.

> * BTW why doesn't benchmark return the BenchmarkResult array?

Will look into it.

> * Is this module so important to keep it as a top level module? I'm
> thinking something like a utility package or a time/date package. How
> about std.util.benchmark?

Not sure.



Andrei
September 22, 2012
>> - It is very strange that the documentation of printBenchmarks uses
>> neither of the words "average" or "minimum", and doesn't say how many
>> trials are done.... I suppose the obvious interpretation is that it
>> only does one trial, but then we wouldn't be having this discussion
>> about averages and minimums right? Øivind says tests are run 1000
>> times... but it needs to be configurable per-test (my idea: support a
>> _x1000 suffix in function names, or _for1000ms to run the test for at
>> least 1000 milliseconds; and allow a multiplier when when running a
>> group of benchmarks, e.g. a multiplier argument of 0.5 means to only
>> run half as many trials as usual.) Also, it is not clear from the
>> documentation what the single parameter to each benchmark is (define
>> "iterations count".)
>
> I don't think it's a good idea because the "for 1000 ms" doesn't say anything except how good the clock resolution was on the system. I'm as strongly convinced we shouldn't print useless information as I am we should print useful information.

I am puzzled about what you think my suggestion meant. I am suggesting allowing the user to configure how long benchmarking takes. Some users might want to run their benchmark for an hour to get stable and reliable numbers; others don't want to wait and want to see results ASAP. Perhaps the *same* user will want to run benchmarks quickly while developing them and then do a "final run" with more trials once their benchmark suite is complete. Also, some individual benchmark functions will take microseconds to complete; others may take seconds to complete. All I'm suggesting are simple ways to avoid wasting users' time, without making std.benchmark overly complicated.
September 22, 2012
On Saturday, September 22, 2012 09:04:09 Andrei Alexandrescu wrote:
> On 9/22/12 8:28 AM, "Øivind" wrote:
> > Is there a way to solve the dependency issue without forbidding static constructors in modules with cyclic dependencies?
> 
> I think an idea just occurred to me. The rules for static ctors and dtors were invented before "import" was allowed inside a scope. We could have taken advantage of that.
> 
> Say we restrict symbol visibility inside static cdtors to ONLY symbols within the current module. If some static cdtor needs a symbol from a different module, it must import it explicitly (even if the current module already imports it).
> 
> In this setup it should be possible to compute, in a fine-grained manner, the dependencies of static cdtors.
> 
> Unfortunately that would be a breaking change.

It's a nice thought, but it wouldn't work. If nothing else .di files completely ruin it.

1. I don't think that it's actually required that static constructors be in a .di file. So, the compiler couldn't know for sure whether the modules being imported had static constructors.

2. Even if all static constructors had to be in a .di file, local imports ruin it, because the function bodies (which can definitely be elided from .di files) could contain local imports to modules which have static constructors and cause circular dependencies.

- Jonathan M Davis