Sekarang topik pembelajaran mesin dan kecerdasan buatan sangat populer, pada saat ini, berkat kekuatan komputer, ide dan algoritma yang telah muncul sejak lama dapat diimplementasikan dan ditingkatkan secara signifikan. Hampir setiap hari Anda dapat membaca berita tentang pencapaian baru di bidang ini. Selain itu, pembelajaran mesin digunakan di hampir semua bidang ... dan pengembangan penyusun tidak terkecuali. Namun, area ini cukup spesifik dan memiliki karakteristik dan kesulitan tersendiri dalam membuat kompiler pintar. Pada saat yang sama, ada banyak studi tentang topik ini dan mereka telah dilakukan untuk waktu yang lama baik di lingkungan akademik dan di dalam berbagai perusahaan.
Di mana tepatnya mencoba menerapkan metode pembelajaran mesin saat membuat kompiler? Dan mengapa sejauh ini kompiler "pintar" belum menjadi bagian dari kehidupan sehari-hari pengembang?
Opsi untuk menggunakan pembelajaran mesin dalam pengembangan kompiler
Mari kita mulai dengan pertanyaan pertama tentang penggunaan spesifik pembelajaran mesin. Faktanya adalah bahwa kompiler modern adalah sistem yang kompleks dengan sejumlah besar optimisasi yang memungkinkan Anda untuk mendapatkan kode mesin yang lebih efisien. Namun, beberapa optimasi dan tugas lain, seperti alokasi register, adalah NP-complete, yang memaksa pengembang kompiler untuk menggunakan algoritma heuristik. Akibatnya, sebagian besar kompiler memiliki sejumlah besar bendera pengoptimalan yang memungkinkan Anda mengonfigurasi heuristik yang digunakan. Dalam LLVM, hampir setiap bagian memiliki beberapa opsi tersembunyi yang dapat mempengaruhi operasinya, mereka dapat digunakan baik menggunakan flag -mllvm saat memanggil dentang, atau dalam utilitas opt. Namun, berbagai flag ini tersembunyi di balik opsi yang jauh lebih sering digunakan, yang berisi banyak pengaturan sekaligus dan biasanya disebut level optimisasi. Untuk kompiler C / C ++, ini dikenal paling -O1, -O2, -O3 untuk mengoptimalkan runtime dan -Os untuk mengoptimalkan ukuran kode. Namun, sayangnya, kode optimal tidak selalu hasilnya (pakar assembler dapat menulis ulang kode yang dihasilkan dengan cara terbaik), banyak tergantung pada kode sumber dalam bahasa tingkat tinggi, arsitektur prosesor, fitur bahasa, dll.
Terlepas dari kenyataan bahwa saat ini prosesor modern memiliki RAM yang cukup dan kinerja yang cukup tinggi, masih ada area di mana kinerja aplikasi, efisiensi energi dan ukuran kode mesin memainkan peran kunci. Contoh-contoh bidang tersebut meliputi pengembangan perangkat lunak untuk sistem tertanam dengan jumlah RAM terbatas, pemrosesan sinyal digital, sistem waktu-nyata, dll. Oleh karena itu, dalam kasus di mana Anda perlu mendapatkan kode mesin berkinerja tinggi untuk sistem yang cukup besar, pemilihan opsi kompilasi yang benar yang memberikan hasil terbaik adalah tugas penting. Selain itu, masalah terburuk-run-time (
WCET ) belum hilang ketika sistem real-time perlu menghitung dan meminimalkan, jika mungkin, waktu pelaksanaan tugas tertentu pada platform. Hingga saat ini, programmer yang bekerja dengan sistem dengan jumlah RAM terbatas tidak dapat sepenuhnya bergantung pada kompiler, dan seringkali secara mandiri mengoptimalkan kode mesin yang dihasilkan.
Sulit bagi seseorang untuk memprediksi optimasi mana yang akan memberikan hasil yang baik dan yang dapat mengarah pada regresi, karena untuk ini Anda harus memiliki pemahaman yang baik tentang seluk-beluk algoritma heuristik yang digunakan, pengetahuan yang baik tentang struktur dan bagian-bagian dari kompiler yang digunakan, dan juga sepenuhnya mengetahui kode program yang dikompilasi, yang Proses pengembangan aplikasi saat ini tidak mungkin. Akibatnya, mengidentifikasi opsi kompilasi terbaik untuk suatu program bagi seseorang menjadi tugas pencarian lengkap berbagai kombinasi opsi dan pengukuran kinerja dan ukuran kode.
Selain itu, ada batasan dalam bentuk unit kompilasi yang dengannya Anda dapat bekerja dan untuk itu Anda dapat memilih opsi. Jadi untuk C / C ++, ini masih merupakan file yang dapat berisi banyak kode, yang, mungkin, akan bermanfaat untuk dioptimalkan dengan cara yang berbeda, tetapi saat ini ini tidak mungkin. Oleh karena itu, kompiler "pintar" yang dapat melatih dan kemudian mendapatkan kode yang dioptimalkan dengan baik untuk berbagai kasus adalah impian bagi beberapa pengembang.
Penelitian dan Solusi yang Ada
Secara alami, masalah pemilihan opsi kompilasi otomatis telah menarik bagi para peneliti selama bertahun-tahun. Salah satu proyek yang paling terkenal adalah pengembangan G. Fursin dan peneliti dari tim
MILEPOST GCC-nya , yang merupakan versi dari kompiler gcc yang dapat dengan sendirinya memilih lintasan optimasi berdasarkan pelatihan sebelumnya pada sampel data yang diperoleh. Dalam karya ini, kami menggunakan satu set 55 karakteristik untuk menyelesaikan masalah dan model yang agak sederhana berdasarkan pada gagasan untuk mendistribusikan solusi yang baik berdasarkan pada algoritma K dari tetangga terdekat. Perkembangan inilah yang menunjukkan bahwa tuning optimisasi pass dapat menghasilkan kode yang dua kali lebih cepat dari kode yang diperoleh menggunakan opsi optimalisasi maksimum standar -O3.
Ada juga studi oleh G. Pekhimenko dan A.D. Brown untuk TPO IBM (
Toronto Portable Optimizer ). Tugas utama mereka adalah memilih nilai heuristik yang dapat dipilih untuk optimisasi dan serangkaian transformasi kode. Untuk implementasi, regresi logistik digunakan, yang memungkinkan untuk membuat pengaturan denda yang efektif untuk pelatihan yang lebih cepat. Pengklasifikasi dibangun di Matlab. Probabilitas penggunaan dihitung untuk setiap pass, dan digunakan jika lebih dari 50%. Sebagai hasil dari karakteristik yang mereka coba kurangi dalam penelitian ini, itu adalah waktu kompilasi statis.
A.Askhari terlibat dalam pemilihan langsung opsi kompilasi untuk seluruh program untuk meminimalkan waktu eksekusi, waktu kompilasi, ukuran kode, dan konsumsi daya. Untuk ini,
Kerangka Kerja cTuning dan
Kerangka Kerja Kolektif yang dikembangkan oleh G. Fursin dan A. Lokhmotov (juga dikembangkan di
GitHub ) digunakan.
Ada juga studi oleh
M. Stephenson dan S. Amarasinge dalam memilih optimasi untuk algoritma tertentu yang sangat penting (alokasi register, DATA PREFETCHING, HYPERBLOCK FORMATION). Untuk setiap fungsi, karakteristiknya sendiri digunakan sesuai. Untuk solusinya, digunakan algoritma genetika. Pengujian produk yang dikembangkan dilakukan di Open Research Compiler (ORC).
Ada juga proyek
MAGEEC (Mesin Dipandu Energi Efisien Kompiler), yang tujuannya agak berbeda. Infrastruktur yang dikembangkan menggunakan pembelajaran mesin untuk memilih optimasi yang diperlukan untuk menghasilkan kode dengan efisiensi energi maksimum untuk sistem komputasi kinerja tinggi. MAGEEC dirancang untuk bekerja dengan gcc dan LLVM. Kompiler ini adalah bagian dari proyek TSERO (Total Software Energy Reporting and Optimization) yang lebih besar.
Salah satu penelitian yang berhubungan langsung dengan LLVM adalah
LLVMTuner , produk perangkat lunak yang dikembangkan di University of Illinois oleh I. Chen dan W. Adwe. Pada 2017, disajikan laporan yang menggambarkan hasil yang tersedia saat itu. Dalam pekerjaan ini, kami mengoptimalkan siklus "panas" individual. Kerangka kerja ini dirancang untuk konfigurasi otomatis program besar. LLVMTuner berjalan pada middleware LLVM IR, menggunakan profil untuk mengidentifikasi loop panas, dan kemudian secara otomatis menyesuaikan heuristik untuk mereka. Fokusnya adalah pada siklus tingkat atas. Siklus yang dipilih dan fungsi panggilan apa pun ditransfer ke modul terpisah, yang selanjutnya akan dioptimalkan. Solusi ini memungkinkan Anda untuk mendapatkan peningkatan kinerja pada program besar.
Masalah yang ada
Namun, tidak ada kompiler yang digunakan secara luas yang secara mandiri menyesuaikan heuristik untuk mengoptimalkan lintasan. Apa masalahnya? Seperti yang Anda ketahui, efektivitas metode pembelajaran mesin dan kualitas model yang diperoleh tergantung pada pilihan fitur yang benar dan kualitas data untuk pelatihan (meskipun ada algoritma yang kurang sensitif terhadap data "berisik"). Tanpa mengetahui struktur dan algoritma yang digunakan dalam kompiler, tidaklah mudah untuk memilih serangkaian karakteristik yang lengkap dan cukup untuk pelatihan, meskipun ada yang cukup jelas dan logis, misalnya, ukuran siklus, jumlah keluar dari siklus, dll. Oleh karena itu, sulit untuk mengembangkan solusi universal yang cocok untuk banyak kompiler sekaligus, dan itu bukan fakta bahwa secara umum dimungkinkan. Selain itu, kemungkinan ini tidak diperlukan.
Karena pengembangan kompiler harus efisien dan layak dalam waktu yang cukup singkat, wajar bahwa bahkan perusahaan besar mengembangkan kompiler industrinya berdasarkan pada solusi siap pakai. Sebagian besar solusi modern dapat dibagi menjadi dua kategori: berjalan pada mesin virtual, misalnya, kompiler JVM - JIT, dan kompiler berdasarkan LLVM, sistem yang mengimplementasikan mesin virtual dengan instruksi seperti RISC - kompiler statis dan dinamis. Tentu saja, masih ada solusi perusahaan sendiri, tetapi mereka menjadi kurang kompetitif karena kurangnya komunitas besar yang terlibat dalam pengembangan teknologi yang digunakan di dalamnya. Akibatnya, saat ini banyak perusahaan besar seperti Google, Apple, Adobe, ARM menggunakan LLVM untuk mengembangkan solusi mereka sendiri. Tentu saja, gcc tetap merupakan kompiler utama untuk C / C ++, solusi lain ada untuk bahasa lain, tetapi bagaimanapun, jika, misalnya, solusi untuk LLVM ditemukan, ini sudah akan mencakup bagian yang layak dari kompiler yang ada saat ini.
Pengumpulan karakteristik untuk pelatihan juga menjadi masalah besar, karena multi-pass compiler sangat mengubah representasi menengah, dan karakteristik yang dikumpulkan pada tahap awal tidak cukup relevan untuk optimisasi kompiler kemudian, karakteristik ini dapat berubah dengan probabilitas tinggi. Karakteristik, di samping itu, masuk akal untuk mengumpulkan secara terpisah untuk berbagai jenis elemen: modul, siklus, blok dasar, karena optimasi biasanya dirancang untuk mengubah jenis elemen tertentu, dalam LLVM, bahkan menurut kriteria ini, bagian-bagiannya dibagi.
Tetapi, pertama-tama, muncul pertanyaan tentang mengidentifikasi unsur-unsur yang perlu dikumpulkan dari karakteristik. Ada banyak cara untuk menghitung pengidentifikasi unik yang dapat disimpan selama semua optimasi, misalnya:
- Hash ujung depan berbasis AST
- nomor unik yang ditugaskan di parsing front-end
- Angka 64-bit yang dihasilkan atas dasar busur CFG (control-flow graph) menggunakan checksum (mirip dengan PGO (Profile-guided optimization) di LLVM)
Namun, Anda perlu menyimpan pengidentifikasi ini dengan benar selama transformasi, ketika elemen-elemen dapat bergabung menjadi satu, membelah, membuat yang baru dan menghapus yang asli, yang bukan tugas yang mudah.
Kedua, pada prinsipnya sulit untuk mengevaluasi batas siklus sumber, blok dasar, dll. Yang tertulis dalam kode sumber, pada IR yang sudah dikonversi. Misalnya, karena pembuatan kode mesin multi-tahap yang diadopsi oleh LLVM, informasi tentang unit dasar mesin hilang setelah pembuatan kode berdasarkan instruksi mesin di AsmPrinter. Dan dengan demikian, informasi tentang pengidentifikasi blok dasar dan siklus juga hilang, yang, misalnya, offset dari awal fungsi diukur, oleh karena itu, dengan metode ini, hanya pada tahap menghasilkan kode mesin dapat offset diperoleh dalam bentuk jumlah byte. Namun, pada tahap selanjutnya menghasilkan kode mesin ketika bekerja dengan fragmen mesin, berbagai keberpihakan dapat ditambahkan, yang mengubah ukuran instruksi yang diperhitungkan sebelumnya, dan instruksi nop juga ditambahkan. Karena itu, untuk blok dasar di akhir fungsi besar, kesalahan perhitungan bisa sangat besar, hingga pergeseran total ke blok / siklus lain. Dan meskipun beberapa transformasi pada tahap selanjutnya dapat dilacak dan diperhitungkan, ini tidak akan memberikan jaminan untuk akurasi pengukuran, karena ukuran instruksi dapat bervariasi hingga tautan.

Seperti yang Anda lihat, bahkan koleksi atribut berdasarkan pelatihan yang dibutuhkan cukup rumit dan memakan waktu, dan yang di masa depan kemungkinan akan menjadi set input untuk model yang terlatih untuk pengambilan keputusan. Dan tidak ada solusi yang jelas untuk masalah ini, yang menyulitkan pekerjaan langsung yang terkait dengan pembelajaran mesin dan menarik sejumlah besar orang karena kurangnya dataset yang memadai. Nah, kesulitan khas dalam menemukan solusi untuk masalah pembelajaran mesin, memilih model, metode, menentukan subset atribut yang benar dengan sejumlah besar dari mereka, dll. ada dalam hal ini. Hampir setiap orang yang menemukan pembelajaran mesin mengetahui tentang mereka dan, mungkin, sesuatu yang unik dan spesifik untuk penyusun tidak ada di sini.
Sulit diprediksi kapan kompiler pintar akan menyebar luas. Kompiler modern juga memiliki masalah lain yang tidak mungkin diselesaikan dengan metode ini, dan yang saat ini mungkin lebih diprioritaskan. Namun, kompiler telah menjadi jauh lebih cerdas daripada mereka pada awal penampilan mereka, dan proses ini akan terus berlanjut, meskipun mungkin agak lambat dari yang diinginkan kebanyakan pengembang.