Dari dokumen untuk GHC 7.6:
[Y] ou sering kali bahkan tidak membutuhkan pragma SPESIALISASI. Saat menyusun modul M, pengoptimal GHC (dengan -O) secara otomatis mempertimbangkan setiap fungsi tingkat-atas yang dideklarasikan dalam M, dan mengkhususkannya untuk jenis yang berbeda di mana ia disebut dalam M. Pengoptimal juga mempertimbangkan setiap fungsi kelebihan beban yang tidak dapat diimpor yang diimpor, dan mengkhususkannya untuk berbagai jenis yang disebut dalam M.
dan
Selain itu, diberikan pragma SPECIALIZE untuk fungsi f, GHC akan secara otomatis membuat spesialisasi untuk semua fungsi tipe-kelas-kelebihan yang disebut oleh f, jika mereka berada dalam modul yang sama dengan pragma SPECIALIZE, atau jika mereka tidak dapat DILINCARKAN; dan seterusnya, secara transitif.
Jadi GHC secara otomatis harus mengkhususkan beberapa / kebanyakan / semua (?) Fungsi yang ditandai INLINABLE
tanpa pragma, dan jika saya menggunakan pragma eksplisit, spesialisasi adalah transitif. Pertanyaan saya adalah: apakah auto -specialization transitive?
Secara khusus, inilah contoh kecil:
Main.hs:
import Data.Vector.Unboxed as U
import Foo
main =
let y = Bar $ Qux $ U.replicate 11221184 0 :: Foo (Qux Int)
(Bar (Qux ans)) = iterate (plus y) y !! 100
in putStr $ show $ foldl1' (*) ans
Foo.hs:
module Foo (Qux(..), Foo(..), plus) where
import Data.Vector.Unboxed as U
newtype Qux r = Qux (Vector r)
-- GHC inlines `plus` if I remove the bangs or the Baz constructor
data Foo t = Bar !t
| Baz !t
instance (Num r, Unbox r) => Num (Qux r) where
{-# INLINABLE (+) #-}
(Qux x) + (Qux y) = Qux $ U.zipWith (+) x y
{-# INLINABLE plus #-}
plus :: (Num t) => (Foo t) -> (Foo t) -> (Foo t)
plus (Bar v1) (Bar v2) = Bar $ v1 + v2
GHC mengkhususkan panggilan untuk plus
, tetapi tidak mengkhususkan diri (+)
dalamQux
Num
contoh yang membunuh kinerja.
Namun, pragma eksplisit
{-# SPECIALIZE plus :: Foo (Qux Int) -> Foo (Qux Int) -> Foo (Qux Int) #-}
menghasilkan spesialisasi transitif seperti yang ditunjukkan dokumen, sehingga (+)
terspesialisasi dan kodenya 30x lebih cepat (keduanya dikompilasi dengan -O2
). Apakah ini perilaku yang diharapkan? Haruskah saya hanya berharap (+)
untuk mengkhususkan secara transitif dengan pragma eksplisit?
MEMPERBARUI
Dokumen untuk 7.8.2 belum berubah, dan perilakunya sama, jadi pertanyaan ini masih relevan.
plus
itu tidak ditandai sebagai INLINABLE dan 2) simonpj menunjukkan bahwa ada beberapa inlining terjadi dengan kode tiket, tapi inti dari contoh saya menunjukkan bahwa tidak ada fungsi yang digarisbawahi (khususnya, saya tidak bisa menyingkirkanFoo
konstruktor kedua , jika tidak, GHC inline stuff).plus (Bar v1) = \(Bar v2)-> Bar $ v1 + v2
, sehingga LHS diterapkan sepenuhnya di situs panggilan? Apakah itu masuk sebaris dan kemudian spesialisasi masuk?plus
diterapkan sepenuhnya secara khusus karena tautan-tautan itu, tetapi pada kenyataannya saya mendapat spesialisasi yang kurang : panggilan keplus
juga tidak terspesialisasi. Saya tidak punya penjelasan untuk itu, tetapi berniat meninggalkannya untuk pertanyaan lain, atau berharap itu akan diselesaikan dalam jawaban untuk yang ini.Jawaban:
Jawaban pendek:
Poin-poin kunci dari pertanyaan ini, seperti yang saya pahami, adalah sebagai berikut:
AFAIK, jawabannya adalah TIDAK, kebanyakan ya tetapi ada cara lain, dan Tidak.
Kode inlining dan ketik spesialisasi aplikasi adalah pertukaran antara kecepatan (waktu eksekusi) dan ukuran kode. Level default mendapat beberapa percepatan tanpa kembung kode. Memilih tingkat yang lebih lengkap diserahkan pada kebijaksanaan programmer melalui
SPECIALISE
pragma.Penjelasan:
Misalkan
f
adalah fungsi yang tipenya mencakup variabel tipe yanga
dibatasi oleh kelas tipeC a
. GHC secara default mengkhususkan dirif
sehubungan dengan jenis aplikasi (menggantikana
untukt
) jikaf
disebut dengan aplikasi ketik kode sumber (a) fungsi dalam modul yang sama, atau (b) jikaf
ditandaiINLINABLE
, maka setiap modul lain yang imporf
dariB
. Dengan demikian, auto-spesialisasi tidak transitif, hanya menyentuhINLINABLE
fungsi diimpor dan menyerukan dalam kode sumber dariA
.Dalam contoh Anda, jika Anda menulis ulang contoh
Num
sebagai berikut:quxAdd
tidak diimpor secara khusus olehMain
.Main
mengimpor kamus instanNum (Qux Int)
, dan kamus ini berisiquxAdd
dalam catatan untuk(+)
. Namun, meskipun kamus diimpor, konten yang digunakan dalam kamus tidak.plus
tidak memanggilquxAdd
, ia menggunakan fungsi yang disimpan untuk(+)
catatan dalam kamus contohNum t
. Kamus ini disetel di situs panggilan (masukMain
) oleh kompiler.sumber