digitalmars.D - voldemort stack traces (and bloat)
- Steven Schveighoffer (66/66) Feb 06 2016 I have a library where I was using very many voldemort types a la std.ra...
- deadalnix (5/6) Feb 07 2016 And no line number. But hey, these are convenience for
- Steven Schveighoffer (4/9) Feb 07 2016 Remind me never to borrow your laptop.
- Iakh (6/10) Feb 07 2016 Why "bad" foo is void?
- Steven Schveighoffer (7/16) Feb 07 2016 A possible fix for the stack printing is to use the template parameter
- Nicholas Wilson (7/26) Feb 07 2016 I think it was Manu who was complaining about symbol length some
- Steven Schveighoffer (7/13) Feb 08 2016 I modified all my voldemort-returning functions to return module-level
- wobbles (12/30) Feb 08 2016 Just to be sure, you replaced something like this:
- Steven Schveighoffer (10/39) Feb 08 2016 Yes, but with template parameters. It's not so much the moving of the
I have a library where I was using very many voldemort types a la std.range. While debugging I had an exception triggered, but I found that the library *pauses* significantly while printing the exception. What I found is essentially that using voldemort types results in horrible stack traces. To demonstrate the problem: struct S(T) { void foo(){ throw new Exception("1");} } auto s(T)(T t) { struct Result { void foo(){ throw new Exception("2");} } return Result(); } void main(string[] args) { version(bad) auto x = 1.s.s.s.s.s; else S!(S!(S!(S!(S!(int))))) x; x.foo; } Building without bad version, and running, I get this as the stack frame for the foo call: 4 testexpansion 0x0000000103c3fc14 pure safe void testexpansion.S!(testexpansion.S!(testexpansion.S!(testexpansion.S!(testexpansion.S!(int) S).S).S).S).S.foo() + 144 Now, if I compile with version=bad: 4 testexpansion 0x000000010fb5dbec pure safe void testexpansion.s!(testexpansion.s!(testexpansion.s!(testexpansion.s!(testexpansion.s!(int).s(int).Result).s(testexpansion.s!(int).s(int).Result).Result).s(testexpansion.s!(testexpansion.s!(int).s(int).Result).s(testexpansion.s!(int).s(int).Result).Result).Result).s(testexpansion.s!(testexpansion.s!(testexpansion.s!(int).s(int).Result).s(testexpansion.s!(int).s(int).Result).Result).s(testexpansion.s!(testexpansion.s!(int).s(int).Result).s(testexpansion.s!(int).s(int).Result).Result).Result).Result).s(testexpansion.s!(testexpansion.s!(testexpansion.s!(testexpansion.s!(int).s(int).Result).s(testexpansion.s!(int).s(int).Result).Result).s(testexpansion.s!(testexpansion.s!(int).s(int).Result).s(testexpansion.s!(int).s(int).Result).Result).Result).s(testexpansion.s!(testexpansion.s!(testexpansion.s!(int).s(int).Result).s(testexpansion.s!(int).s(int).Result).Result).s(testexpansion.s!(testexpansion.s!(int).s(int).Result).s(testexpansion.s!(int).s(int).Result).Result).Result).Result).Result.foo () + 144 I believe what is happening is both the template parameter and the argument type are being printed, but both are the same! And each level of nesting results in another doubling of the printouts. So you have an exponential effect, and the resulting stack trace is horrendously useless. what's more, the template bloat factor skyrockets: dmd -c testexpansion.d ls -l testexpansion.o -rw-r--r--+ 1 steves staff 5664 Feb 7 00:06 testexpansion.o dmd -c -version=bad testexpansion.d ls -l testexpansion.o -rw-r--r--+ 1 steves staff 15312 Feb 7 00:07 testexpansion.o as a final test, I tried this: auto s(T)(T t) { return S!(T)(); } And the resulting .o file: -rw-r--r--+ 1 steves staff 7104 Feb 7 00:11 testexpansion.o With obviously the exception code printing in the less verbose form. So the cost in template bloat of using a voldemort type over a private type is 8k here, more than double the existing size. With more nesting, I'm sure that factor gets worse. Is there a better way we should be doing this? I'm wondering if voldemort types are really worth it. They offer a lot of convenience, and are much DRYer than separate private template types. But the bloat cost is not really worth the convenience IMO. Thoughts? -Steve
Feb 06 2016
On Sunday, 7 February 2016 at 05:18:39 UTC, Steven Schveighoffer wrote:Thoughts?And no line number. But hey, these are convenience for youngsters. We real program, who type on the keyboard using our balls, don't need such distractions.
Feb 07 2016
On 2/7/16 5:20 AM, deadalnix wrote:On Sunday, 7 February 2016 at 05:18:39 UTC, Steven Schveighoffer wrote:Remind me never to borrow your laptop. But really, if you can't figure out what +144 is, *eyeroll*. -SteveThoughts?And no line number. But hey, these are convenience for youngsters. We real program, who type on the keyboard using our balls, don't need such distractions.
Feb 07 2016
On Sunday, 7 February 2016 at 05:18:39 UTC, Steven Schveighoffer wrote:4 testexpansion 0x000000010fb5dbec pure safe void testexpansion.s!(testexpansion.s!(testexpansion.s!(testexpansion.s!(testexpansion.s!Why "bad" foo is void?Is there a better way we should be doing this? I'm wondering ifYeah would by nice to auto-repacle with testexpansion.S!(...)(...).Result.foo or even with ...Result.foo
Feb 07 2016
On 2/7/16 10:42 AM, Iakh wrote:On Sunday, 7 February 2016 at 05:18:39 UTC, Steven Schveighoffer wrote:Huh? foo returns void in both instances.4 testexpansion 0x000000010fb5dbec pure safe void testexpansion.s!(testexpansion.s!(testexpansion.s!(testexpansion.s!(testexpansion.s!Why "bad" foo is void?Yeah would by nice to auto-repacle with testexpansion.S!(...)(...).Result.foo or even with ...Result.fooA possible fix for the stack printing is to use the template parameter placeholders: testexpansion.s!(T = testexpansion.s!...)(T).Result.foo But this doesn't fix the object-file bloat. -Steve
Feb 07 2016
On Monday, 8 February 2016 at 01:48:32 UTC, Steven Schveighoffer wrote:On 2/7/16 10:42 AM, Iakh wrote:I think it was Manu who was complaining about symbol length some time ago and we ended up discussing symbol compression as a possible solution. Did anything ever come of that? If so this seems like an obvious candidate for recursive compression. NicOn Sunday, 7 February 2016 at 05:18:39 UTC, Steven Schveighoffer wrote:Huh? foo returns void in both instances.4 testexpansion 0x000000010fb5dbec pure safe void testexpansion.s!(testexpansion.s!(testexpansion.s!(testexpansion.s!(testexpansion.s!Why "bad" foo is void?Yeah would by nice to auto-repacle with testexpansion.S!(...)(...).Result.foo or even with ...Result.fooA possible fix for the stack printing is to use the template parameter placeholders: testexpansion.s!(T = testexpansion.s!...)(T).Result.foo But this doesn't fix the object-file bloat. -Steve
Feb 07 2016
On 2/7/16 12:18 AM, Steven Schveighoffer wrote:I have a library where I was using very many voldemort types a la std.range.[snip]Is there a better way we should be doing this? I'm wondering if voldemort types are really worth it. They offer a lot of convenience, and are much DRYer than separate private template types. But the bloat cost is not really worth the convenience IMO.I modified all my voldemort-returning functions to return module-level types. One of my example programs (compiled optimized/inline) went from 10MB to 1MB. The other example program went from 2.1MB to 900k. -Steve
Feb 08 2016
On Monday, 8 February 2016 at 13:01:44 UTC, Steven Schveighoffer wrote:On 2/7/16 12:18 AM, Steven Schveighoffer wrote:Just to be sure, you replaced something like this: auto myFunc(int x){ struct MyStruct{ int a; } return MyStruct(x); } with? private struct MyStruct{ int a; } auto myFunc(int x){ return MyStruct(x); }I have a library where I was using very many voldemort types a la std.range.[snip]Is there a better way we should be doing this? I'm wondering if voldemort types are really worth it. They offer a lot of convenience, and are much DRYer than separate private template types. But the bloat cost is not really worth the convenience IMO.I modified all my voldemort-returning functions to return module-level types. One of my example programs (compiled optimized/inline) went from 10MB to 1MB. The other example program went from 2.1MB to 900k. -Steve
Feb 08 2016
On 2/8/16 8:19 AM, wobbles wrote:On Monday, 8 February 2016 at 13:01:44 UTC, Steven Schveighoffer wrote:Yes, but with template parameters. It's not so much the moving of the struct that reduced the bloat, but the nature of how the template parameters are used. Each function that returns one of these structs wraps another such struct, so the template bloat is exponential because of the repeat of the template parameter for the function argument. By moving the struct into the module, there is only one specification of the template parameter for the name mangling. Basically, I changed a.b.c bloat factor from 2^3 to 1^3. -SteveOn 2/7/16 12:18 AM, Steven Schveighoffer wrote:Just to be sure, you replaced something like this: auto myFunc(int x){ struct MyStruct{ int a; } return MyStruct(x); } with? private struct MyStruct{ int a; } auto myFunc(int x){ return MyStruct(x); }I have a library where I was using very many voldemort types a la std.range.[snip]Is there a better way we should be doing this? I'm wondering if voldemort types are really worth it. They offer a lot of convenience, and are much DRYer than separate private template types. But the bloat cost is not really worth the convenience IMO.I modified all my voldemort-returning functions to return module-level types. One of my example programs (compiled optimized/inline) went from 10MB to 1MB. The other example program went from 2.1MB to 900k.
Feb 08 2016