Guardando il nucleo dal 7.6.1, con -O2
e -dsuppress-uniques
, la funzione che fa il lavoro, Main.main_$spoly_$wa
strutturalmente (quasi) identica sia che uso int
o Int64
come tipo di indice. Dato che il nucleo è lunga e complessa, ecco la diff
uscita:
$ diff Int_core.dump-simpl Int64_core.dump-simpl
11,12c11,12
< (Data.Array.Base.STUArray s GHC.Types.Int GHC.Types.Int)
< (Main.C (Data.Array.Base.STUArray s GHC.Types.Int GHC.Types.Int))
---
> (Data.Array.Base.STUArray s GHC.Int.Int64 GHC.Types.Int)
> (Main.C (Data.Array.Base.STUArray s GHC.Int.Int64 GHC.Types.Int))
26,27c26,27
< (Data.Array.Base.STUArray s GHC.Types.Int GHC.Types.Int)
< (Main.C (Data.Array.Base.STUArray s GHC.Types.Int GHC.Types.Int)))
---
> (Data.Array.Base.STUArray s GHC.Int.Int64 GHC.Types.Int)
> (Main.C (Data.Array.Base.STUArray s GHC.Int.Int64 GHC.Types.Int)))
Diversi tipi di indice, questi sono ovviamente differenti.
33,40d32
< l :: GHC.Types.Int
< [LclId]
< l = GHC.Types.I# sc } in
< let {
< u :: GHC.Types.Int
< [LclId]
< u = GHC.Types.I# sc1 } in
< let {
Per indice tipo Int
, GHC produce errori alquanto più informativi per indici fuori dal campo, per cui necessita il limite inferiore e superiore degli indici validi. (L'implementazione predefinita di index
non viene sostituito nel instance Ix Int64
.)
45,46c37
< GHC.Types.False ->
< case poly_$w$j5 (GHC.Types.I# a) l u of wild2 { };
---
> GHC.Types.False -> case GHC.Arr.hopelessIndexError of wild1 { };
errore diverso, indexError
vs. hopelessIndexError
. Le seguenti differenze riguardano anche solo gli errori di indice.
49,50c40
< GHC.Types.False ->
< case poly_$w$j5 (GHC.Types.I# a) l u of wild2 { };
---
> GHC.Types.False -> case GHC.Arr.hopelessIndexError of wild2 { };
58c48
< case poly_$w$j4 y (GHC.Types.I# sc2) of wild3 { };
---
> case poly_$w$j3 y (GHC.Types.I# sc2) of wild4 { };
62c52
< case poly_$w$j4 y (GHC.Types.I# sc2) of wild5 { };
---
> case poly_$w$j3 y (GHC.Types.I# sc2) of wild5 { };
77,78c67
< GHC.Types.False ->
< case poly_$w$j3 (GHC.Types.I# a1) l u of wild6 { };
---
> GHC.Types.False -> case GHC.Arr.hopelessIndexError of wild6 { };
81,82c70
< GHC.Types.False ->
< case poly_$w$j3 (GHC.Types.I# a1) l u of wild7 { };
---
> GHC.Types.False -> case GHC.Arr.hopelessIndexError of wild7 { };
Ora, una volta di più il diverso tipo di indice:
110c98
< GHC.Types.Int
---
> GHC.Int.Int64
152c140
< s GHC.Types.Int GHC.Types.Int>)>)
---
> s GHC.Int.Int64 GHC.Types.Int>)>)
E, infine, 0
e 1
hanno ottenuto diversi nomi di primo livello.
177,178c165,166
< 0 -> (# sc5, lvl5 #);
< 1 -> (# sc5, lvl6 #)
---
> 0 -> (# sc5, lvl #);
> 1 -> (# sc5, lvl1 #)
Quindi l'intero codice che esegue il lavoro effettivo è identico. Eppure quello causa uno stack overflow (anche se solo, -K9M
è sufficiente [-K8731K
è sufficiente qui, -K8730K
non]) e l'altro no.
La differenza è effettivamente causata dagli errori dell'indice. Il codice con Int
indici alloca due scatolati Int
s in ogni chiamata ricorsiva che il codice Int64
non alloca, perché
Main.main_$spoly_$wa [Occ=LoopBreaker]
:: forall s.
GHC.Prim.Int#
-> GHC.Prim.Int#
-> GHC.Prim.Int#
-> GHC.Prim.MutableByteArray# s
-> (GHC.Prim.~#)
*
(Data.Array.Base.STUArray s GHC.Types.Int GHC.Types.Int)
(Main.C (Data.Array.Base.STUArray s GHC.Types.Int GHC.Types.Int))
-> GHC.Prim.Int#
-> GHC.Prim.State# s
-> (# GHC.Prim.State# s, GHC.Types.Int #)
porta vicino due riferimenti alla matrice.
Questo utilizza più stack, e questi boxing Int
s devono essere raccolti con garbage collection, il che causa cifre GC molto più grandi. Inoltre, il thunk per l'errore di indice è un po 'più grande del thunk hopelessIndexError
.
Ora, se aiutare il compilatore da
- rimuovere l'involucro newtype
- rendendo la funzione rigorosa nella matrice (da modelli Bang o
data C a = C !a
)
o qualche altro modo, produce un codice migliore che gestisce senza un overflow di stack per l'argomento specificato, dal momento che c'è solo un riferimento all'array nel worker, e quindi l'allocazione del boxed Int
s per i limiti non è necessario.
Si noti che questo algoritmo causa uno stack overflow per argomenti leggermente più grandi anche con l'aiuto del compilatore.
Utilizzare -ddump-simpl, non -fext-core –
Come aggiungere ulteriori tipi di firme in futuro? – leftaroundabout