Spesialisasi dengan Kendala

156

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 plusFastCycterlalu besar untuk GHC untuk di-inline, bahkan ketika ditandai INLINE. Intinya adalah untuk mengkhususkan panggilan plusFastCyc, bukan inline itu. plusFastCycdisebut 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 plusFastCycdi 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.hsberkas 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 vtTesttes, kode Selain sedang khusus untuk Unboxedvektor lebih Ints, 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. ( main6panggilan iterate main8 y, begitu main8juga di mana plusFastCycharus dikhususkan.)

Tujuan saya adalah membuat fcTestsecepat vtTestdengan spesialisasi plusFastCyc. Saya telah menemukan dua cara untuk melakukan ini:

  1. Panggilan eksplisit inlinedari GHC.Extsdalam fcTest.
  2. Hapus Factored m Intkendala pada plusFastCyc.

Opsi 1 tidak memuaskan karena dalam basis kode yang sebenarnya plusFastCycadalah 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, INLINABLEdan SPECIALIZE, tapi sepertinya tidak ada pekerjaan. ( EDIT : Saya mungkin telah menelanjangi terlalu banyak plusFastCycuntuk membuat contoh saya kecil, jadi INLINEmungkin menyebabkan fungsi menjadi inline. Ini tidak terjadi dalam kode asli saya karena plusFastCycsangat besar.) Dalam contoh khusus ini, saya tidak mendapatkan peringatan apa pun match_co: needs more casesatau RULE: LHS too complicated to desugar(dan di sini ), meskipun saya mendapatkan banyak match_coperingatan sebelum memperkecil contoh. Agaknya, "masalah" adalah Factored m Intkendala dalam aturan; jika saya membuat perubahan pada batasan itu, fcTestjalankan 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.

crockeea
sumber
3
Saya baru saja mencoba mengkhususkan untuk spesifik m , yaitu M. Ini menyelesaikan pekerjaan, tetapi saya tidak dapat mengkhususkan untuk jenis hantu tertentu dalam program nyata karena mereka diverifikasi.
crockeea
Saya juga mengirimkan laporan bug GHC ghc.haskell.org/trac/ghc/ticket/8668 tetapi masalahnya masih terbuka. Proses laporan bug membantu saya sedikit membersihkan pertanyaan, jadi semoga akan lebih mudah untuk mengetahui apa yang sedang terjadi.
crockeea
@monojohnny Maaf mendengarnya, saya yakin Anda dapat menandainya seperti itu. Saya pikir saya meminta GHC untuk melakukan sesuatu yang cukup masuk akal, dan itu tidak akan berhasil. Saya tidak yakin apakah saya salah melakukannya, atau apakah ini keistimewaan dengan kompiler yang mungkin memiliki solusi. Saya telah melihat solusi untuk spesialisasi dan aturan di beberapa perpustakaan khusus tentang peretasan yang lolos dari saya saat ini, jadi saya berharap seseorang di komunitas dengan pengalaman GHC lebih daripada yang saya tahu bagaimana mencapai spesialisasi.
crockeea
1
Saya minta maaf atas nada komentar saya - ini bukan kontribusi terbaik saya ke situs ini - benar-benar tidak ada yang salah dengan posting Anda (Ini adalah kurangnya pemahaman saya yang merupakan sumber kekesalan saya, saya kira!)
monojohnny
@monojohnny Permintaan Maaf diterima, tapi sayang sekali downvote terkunci sekarang ;-)
crockeea

Jawaban:

5

GHC juga memberikan opsi untuk SPECIALIZEdeklarasi instance tipe-kelas. Saya mencoba ini dengan kode (diperluas) Foo.hs, dengan meletakkan yang berikut:

instance (Num r, V.Vector v r, Factored m r) => Num (VT v m r) where 
    {-# SPECIALIZE instance ( Factored m Int => Num (VT U.Vector m Int)) #-}
    VT x + VT y = VT $ V.zipWith (+) x y

Namun, perubahan ini tidak mencapai kecepatan yang diinginkan. Apa yang mencapai peningkatan kinerja itu secara manual menambahkan contoh khusus untuk tipe VT U.Vector m Intdengan definisi fungsi yang sama, sebagai berikut:

instance (Factored m Int) => Num (VT U.Vector m Int) where 
    VT x + VT y = VT $ V.zipWith (+) x y

Ini membutuhkan penambahan OverlappingInstancesdan FlexibleInstancesmasuk LANGUAGE.

Menariknya, dalam program contoh, speedup yang diperoleh dengan instance yang tumpang tindih tetap ada meskipun Anda menghapus setiap SPECIALIZEdan INLINABLEpragma.

Diego E. Alonso-Blas
sumber
Jelas tidak optimal, tapi ini solusi pertama yang benar-benar mencapai tujuan, jadi saya kira saya akan menerimanya sekarang ...
crockeea