GHC: Ticket #2416: Optimization defeated by merging module into main
http://ghc.haskell.org/trac/ghc/ticket/2416
<p>
I have a library, <code>TheModule</code>, and an application that uses <code>TheModule</code>, called <code>Main</code>. When <code>TheModule</code> lives in <code>TheModule.hs</code> and <code>Main</code> lives in <code>Main.hs</code>, optimization works as expected. When I copy all of <code>TheModule</code>'s definitions and all of <code>Main</code>'s definitions into <code>Both.hs</code>, much less optimization occurs, as can be seen from -ddump-simpl, as well as the drastic difference in running times.
</p>
<p>
The program performs a lot of small matrix-vector multiplications. The single command line argument tells how many to do.
</p>
<pre class="wiki">time ./Main 1000000
20.0 :. (59.0 :. (100.0 :. (137.0 :. ())))
real 0m0.389s
user 0m0.152s
sys 0m0.232s
time ./Both 100000
20.0 :. (59.0 :. (100.0 :. (137.0 :. ())))
real 0m3.462s
user 0m3.368s
sys 0m0.068s
</pre><p>
So that makes Both about 100 times slower. Compiled with -O2 and -fexcess-precision
</p>
en-usGHChttp://ghc.haskell.org/trac/ghc/chrome/site/ghc_logo.png
http://ghc.haskell.org/trac/ghc/ticket/2416
Trac 1.2sedillardFri, 04 Jul 2008 04:52:58 GMTattachment set
http://ghc.haskell.org/trac/ghc/ticket/2416
http://ghc.haskell.org/trac/ghc/ticket/2416
<ul>
<li><strong>attachment</strong>
set to <em>Main.hs</em>
</li>
</ul>
TicketsedillardFri, 04 Jul 2008 04:53:23 GMTattachment set
http://ghc.haskell.org/trac/ghc/ticket/2416
http://ghc.haskell.org/trac/ghc/ticket/2416
<ul>
<li><strong>attachment</strong>
set to <em>TheModule.hs</em>
</li>
</ul>
TicketsedillardFri, 04 Jul 2008 04:53:46 GMTattachment set
http://ghc.haskell.org/trac/ghc/ticket/2416
http://ghc.haskell.org/trac/ghc/ticket/2416
<ul>
<li><strong>attachment</strong>
set to <em>Both.hs</em>
</li>
</ul>
TicketsedillardFri, 04 Jul 2008 04:58:39 GMT
http://ghc.haskell.org/trac/ghc/ticket/2416#comment:1
http://ghc.haskell.org/trac/ghc/ticket/2416#comment:1
<p>
The main reason Main.hs is so much faster is this:
</p>
<pre class="wiki">ghc --make Main.hs -O2 -fexcess-precision -ddump-simpl | grep D#
[1 of 2] Compiling TheModule ( TheModule.hs, TheModule.o )
[2 of 2] Compiling Main ( Main.hs, Main.o )
Linking Main ...
(no hits)
</pre><p>
There are no calls D# in the compiled Main.hs, whereas Both.hs ends up with many D# constructors in the main loop.
</p>
TicketdolioFri, 04 Jul 2008 05:07:22 GMT
http://ghc.haskell.org/trac/ghc/ticket/2416#comment:2
http://ghc.haskell.org/trac/ghc/ticket/2416#comment:2
<p>
What happens if you put:
</p>
<p>
<code> module Main (main) where </code>
</p>
<p>
at the top of Both.hs? The obvious difference between the two setups is that Main.hs exports only main, m4 and v4, whereas Both exports all the functions in TheModule as well. When things are exported like that, GHC is more conservative about optimizations.
</p>
TicketsedillardFri, 04 Jul 2008 05:10:59 GMT
http://ghc.haskell.org/trac/ghc/ticket/2416#comment:3
http://ghc.haskell.org/trac/ghc/ticket/2416#comment:3
<p>
Adding
</p>
<blockquote>
<p>
<code>module Main (main) where</code>
</p>
</blockquote>
<p>
does not change the behavior.
</p>
TicketsimonpjFri, 04 Jul 2008 09:28:55 GMTdifficulty set
http://ghc.haskell.org/trac/ghc/ticket/2416#comment:4
http://ghc.haskell.org/trac/ghc/ticket/2416#comment:4
<ul>
<li><strong>difficulty</strong>
set to <em>Unknown</em>
</li>
</ul>
<p>
Thank you very much for a nice test case. Usually splitting things across modules makes things worse, not better, so it's an interesting case.
</p>
<p>
I'll look into it. But probably not immediately -- POPL deadline then trip.
</p>
<p>
Simon
</p>
TicketsedillardFri, 04 Jul 2008 15:08:55 GMT
http://ghc.haskell.org/trac/ghc/ticket/2416#comment:5
http://ghc.haskell.org/trac/ghc/ticket/2416#comment:5
<p>
Thanks for looking into it. It has something to do with the Storable instance. I've noticed consistently flaky behavior from the combination of that class and my vector data type. Until the NDP array libraries mature, or until someone helps me with this <a class="ext-link" href="http://www.haskell.org/pipermail/haskell-cafe/2008-July/044879.html"><span class="icon"></span>http://www.haskell.org/pipermail/haskell-cafe/2008-July/044879.html</a>, Storable is all I've got and I use it heavily.
</p>
<p>
Originally, I meant to submit another bug, and I was trying to create a simple test case, which is when I ran across this behavior while trying to merge everything into one file. That original bug, also involving Storable, is even weirder:
</p>
<p>
I have a type Vec4 a, which is polymorphic in a, and a Storable instance for that. (Same as in this file.) I also have a specialized types, Vec2D - Vec4D, with unpacked doubles, and functions pack/unpack which convert between the polymorphic and specialized types. The weirdness is this: when I declare a Storable instance for Vec2D, using pack/unpack and the Storable methods of Vec2 a, then the optimization fails for Vec4 a's Storable methods. The test case is the same as here, Main.hs doing many 4x4 matrix/vector multiplies. So when I define type B's Storable instance using type A's, then optimization fails for type A's methods, even though no values of type B are ever used. Totally bizarre. I can reproduce this, but like I said, I ran into this here bug when trying to make a single-file test case, so first things first I guess.
</p>
TicketsimonpjFri, 04 Jul 2008 15:13:54 GMT
http://ghc.haskell.org/trac/ghc/ticket/2416#comment:6
http://ghc.haskell.org/trac/ghc/ticket/2416#comment:6
<p>
If you can boil down your Storable problem, do put it in a separate Trac ticket.
</p>
<p>
S
</p>
TicketsedillardFri, 04 Jul 2008 17:42:21 GMTattachment set
http://ghc.haskell.org/trac/ghc/ticket/2416
http://ghc.haskell.org/trac/ghc/ticket/2416
<ul>
<li><strong>attachment</strong>
set to <em>MainOnly.hs</em>
</li>
</ul>
TicketsedillardFri, 04 Jul 2008 17:43:37 GMTattachment set
http://ghc.haskell.org/trac/ghc/ticket/2416
http://ghc.haskell.org/trac/ghc/ticket/2416
<ul>
<li><strong>attachment</strong>
set to <em>LibOnly.hs</em>
</li>
</ul>
TicketsedillardFri, 04 Jul 2008 17:44:16 GMTattachment set
http://ghc.haskell.org/trac/ghc/ticket/2416
http://ghc.haskell.org/trac/ghc/ticket/2416
<ul>
<li><strong>attachment</strong>
set to <em>MainAndLib.hs</em>
</li>
</ul>
TicketsedillardFri, 04 Jul 2008 17:55:09 GMT
http://ghc.haskell.org/trac/ghc/ticket/2416#comment:7
http://ghc.haskell.org/trac/ghc/ticket/2416#comment:7
<p>
Added new, smaller test case. MainOnly.hs, LibOnly.hs and MainAndLib.hs. The problem is definitely with the Storable instance. In MainAndLib none of the methods are inlined, in MainOnly they are.
</p>
TicketsedillardSat, 05 Jul 2008 17:28:14 GMT
http://ghc.haskell.org/trac/ghc/ticket/2416#comment:8
http://ghc.haskell.org/trac/ghc/ticket/2416#comment:8
<p>
I've filed the original <code>Storable</code> weirdness as <a class="closed ticket" href="http://ghc.haskell.org/trac/ghc/ticket/2422" title="#2422: bug: Unrelated instance foils optimizer (closed: fixed)">#2422</a>. In trying to narrow it down some more I also discovered <a class="closed ticket" href="http://ghc.haskell.org/trac/ghc/ticket/2420" title="#2420: bug: Multi-method classes are inlined/specialized better than single-method ... (closed: fixed)">#2420</a>, which may or may not be related.
</p>
TicketiglooSun, 06 Jul 2008 00:26:24 GMTmilestone set
http://ghc.haskell.org/trac/ghc/ticket/2416#comment:9
http://ghc.haskell.org/trac/ghc/ticket/2416#comment:9
<ul>
<li><strong>milestone</strong>
set to <em>6.10 branch</em>
</li>
</ul>
TicketsimonpjMon, 05 Jan 2009 13:56:01 GMT
http://ghc.haskell.org/trac/ghc/ticket/2416#comment:10
http://ghc.haskell.org/trac/ghc/ticket/2416#comment:10
<p>
Indeed <a class="closed ticket" href="http://ghc.haskell.org/trac/ghc/ticket/2422" title="#2422: bug: Unrelated instance foils optimizer (closed: fixed)">#2422</a> is a much more tractable example. No point in looking at <a class="closed ticket" href="http://ghc.haskell.org/trac/ghc/ticket/2416" title="#2416: bug: Optimization defeated by merging module into main (closed: fixed)">#2416</a> further until <a class="closed ticket" href="http://ghc.haskell.org/trac/ghc/ticket/2422" title="#2422: bug: Unrelated instance foils optimizer (closed: fixed)">#2422</a> is dealt with.
</p>
<p>
Simon
</p>
TicketiglooMon, 13 Apr 2009 15:46:06 GMTmilestone changed
http://ghc.haskell.org/trac/ghc/ticket/2416#comment:11
http://ghc.haskell.org/trac/ghc/ticket/2416#comment:11
<ul>
<li><strong>milestone</strong>
changed from <em>6.10 branch</em> to <em>6.12 branch</em>
</li>
</ul>
TicketiglooFri, 30 Apr 2010 16:40:29 GMTmilestone changed
http://ghc.haskell.org/trac/ghc/ticket/2416#comment:12
http://ghc.haskell.org/trac/ghc/ticket/2416#comment:12
<ul>
<li><strong>milestone</strong>
changed from <em>6.12 branch</em> to <em>6.12.3</em>
</li>
</ul>
TicketiglooSat, 19 Jun 2010 16:57:06 GMTpriority, milestone changed
http://ghc.haskell.org/trac/ghc/ticket/2416#comment:13
http://ghc.haskell.org/trac/ghc/ticket/2416#comment:13
<ul>
<li><strong>priority</strong>
changed from <em>normal</em> to <em>low</em>
</li>
<li><strong>milestone</strong>
changed from <em>6.12.3</em> to <em>6.14.1</em>
</li>
</ul>
TicketiglooThu, 24 Jun 2010 16:43:54 GMTstatus changed; failure, resolution set
http://ghc.haskell.org/trac/ghc/ticket/2416#comment:14
http://ghc.haskell.org/trac/ghc/ticket/2416#comment:14
<ul>
<li><strong>status</strong>
changed from <em>new</em> to <em>closed</em>
</li>
<li><strong>failure</strong>
set to <em>None/Unknown</em>
</li>
<li><strong>resolution</strong>
set to <em>fixed</em>
</li>
</ul>
<p>
Performance of both is now the same, and both have <code>D#</code>s in the <code>-ddump-simpl</code> output.
</p>
Ticket