Untuk pertanyaan set

Alice: Kenapa tempat ini SANGAT aneh?
Dodo: Dan karena semua tempat lain tidak terlalu aneh. Setidaknya harus ada satu tempat yang SANGAT aneh.



Jadi, mari kita lihat teks dari kelas template BitSet dengan tujuan mengadaptasinya dengan persyaratan MK, bidang utama optimasi telah ditentukan sebelumnya. Anda dapat, tentu saja, menulis kelas Anda sendiri dari awal, tetapi jangan mengabaikan kesempatan untuk berkenalan dengan solusi yang baik, karena perpustakaan STL (jangan bingung dengan spl) telah dikenal sejak lama dan digunakan di mana-mana. Pertama, Anda perlu menemukan kode sumber, setelah perjalanan singkat di Internet, saya baru saja membuka direktori dengan MinGW saya dan mencari file yang diperlukan, yang saya ingin diskusikan lebih lanjut.

Pada awalnya, kita melihat sedikit lebih dari satu halaman dengan hak cipta dan teks lisensi, serta berbagai peringatan - yah, Anda bisa melewati ini, ini untuk pengacara. Selanjutnya termasuk dan saya berterima kasih kepada penulis - masing-masing disertai dengan komentar tentang apa yang ingin kita dapatkan dari setiap file - semua orang akan melakukan itu, pekerjaan peneliti Indiana Jones akan lebih mudah. Menentukan datang berikutnya, saya bukan fanatik kebersihan kode dan tidak akan berdebat - biarkan mereka menjadi dan segera melihat definisi jumlah bit dalam unit penyimpanan, yang merupakan lama tanpa tanda tangan. Melanjutkan sepanjang jalan Definisikan yang licin, saya mencari definisi unit penyimpanan saya sendiri, yang sama dengan uint8_fast, meskipun saya mengerti bahwa ini jelas tidak cukup. Ngomong-ngomong, saya segera membawa satu lagi terima kasih kepada penulis untuk gaya - di akhir modul mereka membersihkan diri mereka sendiri, menghancurkan definisi internal - saya bahkan tidak berharap bahwa saya akan melihat hal seperti itu dalam produksi modern.

Dan di sini kesalahpahaman ringan mulai - pada awalnya mereka mendefinisikan struktur tambahan template template _Base_bitset (mengapa persis struktur, dan bukan kelas, karena mereka dapat dipertukarkan), di mana mereka menentukan jenis penyimpanan data dan sekali lagi ditentukan secara langsung - kita mengubahnya menjadi milik kita sendiri. Selanjutnya, dua contoh struktur tambahan ini ditentukan - untuk satu unit penyimpanan (well, ini dapat dimengerti, untuk meningkatkan efisiensi kode) dan untuk contoh degenerasi tanpa penyimpanan sama sekali (mungkin untuk penanganan kesalahan), hingga kami menyentuh kode ini.

Selanjutnya, kita menemukan bitet kelas kelas utama (sekarang kelas), berasal dari struktur templat (ya, dalam C dimungkinkan, tidak jelas mengapa melakukan ini, tetapi dimungkinkan), di mana jenis penyimpanan ditentukan kembali dan diubah menjadi milik kita lagi. Tapi di sini saya bertanya-tanya - mengapa jenis itu tidak diwarisi dari struktur induk dan kagum menemukan (ya dengan takjub, kelas templat - ini bukan yang saya lakukan dalam kehidupan sehari-hari) bahwa warisan kelas templat agak berbeda dari yang biasa. Artinya, warisannya persis sama dan semua entitas orang tua tersedia di kelas anak perempuan (saya harap tidak ada yang akan membawa saya untuk kelas chauvinist gender), tetapi mereka harus ditandai dengan nama lengkap. Yah, ini masih bisa dipahami, jika kompiler tidak akan menebak mana dari pilihan instantiated untuk diterapkan, tetapi jika Anda menggunakan jenis yang didefinisikan di kelas, Anda masih perlu menambahkan kata kunci nama kunci. Ini bukan solusi yang jelas, saya sangat senang dengan diagnostik kompiler, yang berarti "mungkin Anda memiliki tipe dari kelas induk" - ya, terima kasih, kapten, itulah yang saya maksud, saya senang kami saling memahami, tetapi itu tidak semakin mudah bagi saya. Namun, diagnosis semacam itu terjadi di versi lama GCC, dalam pesan saat ini menjadi lebih jelas "mungkin Anda lupa kata kunci nama ketik." Memahami pesan diagnostik kompiler untuk kelas templat umumnya merupakan topik untuk lagu yang terpisah, dan lagu ini sama sekali tidak menggembirakan.

Jadi jika kita tidak ingin terus menggunakan konstruk di kelas anak

std::_Base_bitset<Nb>::_WordT 

(dan kami tidak mau, neg?) maka kami memiliki tiga cara

1) sepele

 #define _WordT typename _Base_bitset<_Nb>::_WordT 

2) definisi tipe - jelas pada tingkat global dan saya lebih menyukainya

3) untuk penggemar aneh

 typedef typename _Base_bitset<_Nb>::_WordT _WordT; 

(Perasaan kruk krom yang luar biasa). Secara pribadi, dalam ketiga kasus ini, saya tidak senang dengan indikasi langsung dari kelas induk, karena implementasinya tidak harus memperhitungkan rantai warisan, tetapi mungkin saya tidak mengerti sesuatu.

Ada juga metode 4)

  using _WordT = std::_Base_bitset<Nb>::_WordT; 

tetapi dalam versi saya dari kompiler itu tidak berfungsi, jadi tidak.

Catatan tentang margin (PNP) - dalam buku yang indah "C ++ untuk programmer nyata" (Saya mengunduhnya secara tidak sengaja pada saat itu, memutuskan bahwa itu dikhususkan untuk C ++ dalam sistem waktu nyata) ia mengatakan tentang bahasa "Keanggunannya tidak berarti sama sekali dalam kesederhanaan (kata-kata C ++ dan kesederhanaan memotong telinga dengan kontradiksi yang tampak), tetapi dalam kemampuan potensial. Untuk setiap masalah jelek, ada semacam idiom cerdas, tipuan bahasa yang elegan, berkat masalah yang meleleh tepat di depan mata kita. ” Karena buku ini benar-benar luar biasa, itu berarti bahwa kutipan di atas benar (saya mengerti bahwa ada batasan logis tertentu di sini), tetapi secara pribadi, sayangnya, saya melihat masalahnya, tetapi ada ketegangan dengan idiom yang cerdas.

Meskipun masih ada sedikit kebingungan, masalahnya diselesaikan dan Anda dapat menikmati hasilnya - tetapi tidak ada di sana. Saat mengubah ukuran set tes dari 15 menjadi 5, kesalahan kompilasi terjadi sepenuhnya secara tak terduga. Tidak, semuanya jelas, instantiasi yang tidak dimodifikasi dengan parameter template 1 berfungsi, tetapi dari luar terlihat sangat aneh - kami mengubah konstanta dan program berhenti mengkompilasi.

Anda dapat, tentu saja, memodifikasi implementasi ini dan lainnya, tetapi pendekatan seperti itu tampak seperti pelanggaran berat terhadap prinsip KERING. Ada solusi yang mungkin, dan ada lebih dari satu di antaranya 1) yang jelas - lagi mendefinisikan dan 2) sepele - lagi definisi jenis di tingkat global, tetapi dalam kasus ini akan keluar dari modul, yang, bagaimanapun, dalam implementasi yang sedang dipertimbangkan, menonjol dari struktur dasar , tetapi kualifikasi juga tidak perlu ditulis.

Oleh karena itu, saya cenderung pilihan 3) kelas dasar untuk menentukan jenis dan warisan semua contoh dari itu. Selain itu, entitas dari kelas induk dilindungi lagi, sehingga mereka tidak menonjol. Kemudian saya menemukan properti lucu, mungkin C ++ harus dipuji karena fleksibilitasnya - untuk varian tanpa penyimpanan, jenisnya tidak diperlukan, dan bahasa memungkinkan implementasi untuk digunakan tanpa warisan, meskipun ini tidak terlalu diperlukan dalam kasus khusus ini. Segera saya menemukan satu kekurangan lagi dari perpustakaan - dalam ketiga varian, operasi penghitungan jumlah unit penyimpanan diatur setiap kali

 static size_t _S_whichword 

dan topeng bit di dalamnya _S_maskbit dan semuanya benar-benar identik - kami juga memindahkannya ke kelas dasar. Dalam kasus ini, kode "mati" terdeteksi - metode _S_whichbyte, saya bahkan tidak tahu apa yang harus dilakukan - di satu sisi, aturan nada yang baik mengharuskan penghapusannya, di sisi lain - dalam kasus templat, ini tidak mempengaruhi kode yang dihasilkan. Saya akan menggunakan aturan - "tidak mengerti sesuatu - jangan sentuh" ​​dan tinggalkan metode ini.

Pada prinsipnya, modifikasi dalam hal jenis penyimpanan selesai dan Anda dapat memulai pengujian. Dan segera saya menemukan kurangnya implementasi - untuk arsitektur MSP430 untuk beberapa alasan kata-kata dua-byte dialokasikan bukan byte. Tentu saja, bukan dua kata, seperti sebelumnya, tetapi kami masih berjuang untuk kode minimum (dalam segala hal). Ternyata kompiler yakin bahwa dalam arsitektur ini tipe uint_fast8_t memiliki ukuran 2, meskipun sistem perintah memiliki operasi dengan byte dan kompiler itu sendiri menggunakannya sepenuhnya. Gagasan menggunakan tipe ini dikompromikan dan Anda harus mengatur tipe data uint8_t secara langsung. Nah, jika ada arsitektur di mana bekerja dengan byte tidak berhasil dan ukuran tipe uint_fast8_t berbeda dari 1 (tidak ada yang tersedia di kompiler), itu harus menderita karena kecepatan semua yang lain.

Menguji versi yang dikoreksi menunjukkan operasi yang benar pada berbagai arsitektur dan dengan parameter yang berbeda, tetapi masih ada masalah menghitung masker bit untuk MK tanpa pergeseran yang dikembangkan, dalam kasus kami adalah MSP430 dan AVR. Pada prinsipnya, Anda cukup membuat membaca bitmask dari array sebagai metode untuk semua kasus, terlepas dari arsitektur MK. Solusinya cukup berhasil, dalam arsitektur yang dikembangkan dengan pengindeksan semuanya baik-baik saja, tetapi masih akan ada kehilangan waktu dibandingkan dengan pergeseran cepat, tetapi saya tidak ingin menyodok jari saya dengan kata-kata "kelas akan lebih cepat, katanya, kami mengoptimalkan katanya. "

Oleh karena itu, kita memerlukan implementasi yang berbeda untuk dua arsitektur kita yang lemah, yang berbeda dari semua yang lain dalam ukuran tipe uint_fast16_t - itu adalah 2 banding 4, atau 8 untuk versi 64 bit. Kompilasi bersyarat tidak akan membantu kita, tetapi pengaturan kondisi dengan harapan optimasi bukan cara samurai, ada pola yang tersisa. Saya mencoba untuk membuat metode templat kelas, tetapi spesialisasi parsial tidak tersedia untuk itu - ternyata harus begitu dan bahkan menemukan artikel yang mengatakan mengapa ini adalah solusi yang baik. Jujur, saya masih tidak mengerti mengapa keputusan ini baik, kemungkinan besar, ini karena pertimbangan agama. Namun demikian, kami tidak ditanya, dan apa yang masih harus dilakukan - Anda dapat membuat fungsi templat yang ramah (ternyata tidak mungkin, dan untuk alasan yang sama - spesialisasi parsial dilarang), ada solusi lain, kelihatannya lucu - spesialisasi sebagian dari metode di luar kelas. Anda bahkan dapat membuat fungsi templat terpisah dan mengaksesnya dari metode kelas, saya tidak tahu metode mana yang lebih benar, saya memilih yang ini. Ya, dia tidak memiliki akses ke entitas internal kelas, tetapi dalam kasus khusus ini tidak perlu, apa yang harus dilakukan jika perlu, saya belum tahu, "kami akan menyelesaikan masalah saat mereka muncul".

Sekarang kami memiliki semua yang kami butuhkan, kami mengumpulkan fragmen yang diuji bersama-sama dan mendapatkan kode yang memecahkan masalah optimasi awal. Pada saat yang sama, kami membuat kode lebih kompak (dan karena itu lebih bisa dimengerti, meskipun yang terakhir masih bisa diperdebatkan), menghapus banyak pengulangan dan menghilangkan entitas yang berlebihan yang mencuat. Satu-satunya hal yang tidak diperbaiki adalah campuran struktur dan kelas, tetapi di sini saya tidak mengerti alasan untuk implementasi seperti itu, oleh karena itu, kalau-kalau, saya tidak akan menyentuh bagian ini.

Posting kedua akan dikhususkan untuk implementasi versi kedua dari himpunan, dan tanpa itu sesuatu telah banyak berhasil.

Source: https://habr.com/ru/post/id450176/


All Articles