Saya mengalami masalah dalam mendapatkan GHC untuk mengkhususkan fungsi dengan batasan kelas. Saya punya contoh minimal masalah saya di sini: Foo.hs dan Main.hs . Dua file dikompilasi (GHC 7.6.2, ghc -O3 Main
) dan jalankan.
CATATAN:
Foo.hs
benar-benar dilucuti. Jika Anda ingin melihat mengapa kendala diperlukan, Anda dapat melihat lebih banyak kode di sini . Jika saya memasukkan kode dalam satu file atau membuat banyak perubahan kecil lainnya, GHC hanya akan mengikutsertakan panggilan masuk plusFastCyc
. Ini tidak akan terjadi dalam kode nyata karena plusFastCyc
terlalu besar untuk GHC untuk di-inline, bahkan ketika ditandai INLINE
. Intinya adalah untuk mengkhususkan panggilan plusFastCyc
, bukan inline itu. plusFastCyc
disebut di banyak tempat dalam kode nyata, jadi menduplikasi fungsi besar seperti itu tidak diinginkan bahkan jika saya bisa memaksa GHC untuk melakukannya.
Kode minat adalah plusFastCyc
di Foo.hs
, direproduksi di sini:
{-# INLINEABLE plusFastCyc #-}
{-# SPECIALIZE plusFastCyc ::
forall m . (Factored m Int) =>
(FastCyc (VT U.Vector m) Int) ->
(FastCyc (VT U.Vector m) Int) ->
(FastCyc (VT U.Vector m) Int) #-}
-- Although the next specialization makes `fcTest` fast,
-- it isn't useful to me in my real program because the phantom type M is reified
-- {-# SPECIALIZE plusFastCyc ::
-- FastCyc (VT U.Vector M) Int ->
-- FastCyc (VT U.Vector M) Int ->
-- FastCyc (VT U.Vector M) Int #-}
plusFastCyc :: (Num (t r)) => (FastCyc t r) -> (FastCyc t r) -> (FastCyc t r)
plusFastCyc (PowBasis v1) (PowBasis v2) = PowBasis $ v1 + v2
The Main.hs
berkas memiliki dua driver: vtTest
, yang berjalan di ~ 3 detik, dan fcTest
, yang berjalan di ~ 83 detik ketika dikompilasi dengan O3 menggunakan forall
'spesialisasi d.
The inti menunjukkan bahwa untuk vtTest
tes, kode Selain sedang khusus untuk Unboxed
vektor lebih Int
s, dll, sementara kode vektor generik digunakan untuk fcTest
. Pada baris 10, Anda dapat melihat bahwa GHC memang menulis versi khusus plusFastCyc
, dibandingkan dengan versi generik pada baris 167. Aturan untuk spesialisasi adalah pada baris 225. Saya percaya aturan ini harus diaktifkan pada saluran 270. ( main6
panggilan iterate main8 y
, begitu main8
juga di mana plusFastCyc
harus dikhususkan.)
Tujuan saya adalah membuat fcTest
secepat vtTest
dengan spesialisasi plusFastCyc
. Saya telah menemukan dua cara untuk melakukan ini:
- Panggilan eksplisit
inline
dariGHC.Exts
dalamfcTest
. - Hapus
Factored m Int
kendala padaplusFastCyc
.
Opsi 1 tidak memuaskan karena dalam basis kode yang sebenarnya plusFastCyc
adalah operasi yang sering digunakan dan fungsi yang sangat besar, sehingga tidak boleh diuraikan pada setiap penggunaan. Sebaliknya, GHC harus memanggil versi khusus plusFastCyc
. Opsi 2 sebenarnya bukan opsi karena saya perlu kendala dalam kode nyata.
Saya sudah mencoba berbagai pilihan menggunakan (dan tidak menggunakan) INLINE
, INLINABLE
dan SPECIALIZE
, tapi sepertinya tidak ada pekerjaan. ( EDIT : Saya mungkin telah menelanjangi terlalu banyak plusFastCyc
untuk membuat contoh saya kecil, jadi INLINE
mungkin menyebabkan fungsi menjadi inline. Ini tidak terjadi dalam kode asli saya karena plusFastCyc
sangat besar.) Dalam contoh khusus ini, saya tidak mendapatkan peringatan apa pun match_co: needs more cases
atau RULE: LHS too complicated to desugar
(dan di sini ), meskipun saya mendapatkan banyak match_co
peringatan sebelum memperkecil contoh. Agaknya, "masalah" adalah Factored m Int
kendala dalam aturan; jika saya membuat perubahan pada batasan itu, fcTest
jalankan secepat vtTest
.
Apakah saya melakukan sesuatu yang tidak disukai GHC? Mengapa GHC tidak akan mengkhususkan plusFastCyc
, dan bagaimana saya bisa membuatnya?
MEMPERBARUI
Masalahnya tetap ada di GHC 7.8.2, jadi pertanyaan ini masih relevan.
m
, yaituM
. Ini menyelesaikan pekerjaan, tetapi saya tidak dapat mengkhususkan untuk jenis hantu tertentu dalam program nyata karena mereka diverifikasi.