Pertimbangkan fungsi berikut:
void func(bool& flag)
{
if(!flag) flag=true;
}
Menurut saya, jika flag memiliki nilai boolean yang valid, ini akan setara dengan pengaturan tanpa syarat true
, seperti ini:
void func(bool& flag)
{
flag=true;
}
Namun baik gcc maupun clang tidak mengoptimalkannya dengan cara ini - keduanya menghasilkan yang berikut pada -O3
tingkat pengoptimalan:
_Z4funcRb:
.LFB0:
.cfi_startproc
cmp BYTE PTR [rdi], 0
jne .L1
mov BYTE PTR [rdi], 1
.L1:
rep ret
Pertanyaan saya adalah: apakah hanya karena kodenya terlalu kasus khusus untuk dirawat untuk dioptimalkan, atau adakah alasan bagus mengapa pengoptimalan seperti itu tidak diinginkan, mengingat itu flag
bukan referensi volatile
? Tampaknya satu-satunya alasan yang mungkin adalah bahwa flag
entah bagaimana bisa memiliki nilai non- true
atau false
tanpa perilaku yang tidak terdefinisi pada saat membacanya, tetapi saya tidak yakin apakah ini mungkin.
c++
optimization
Ruslan
sumber
sumber
1
yang digunakan. godbolt.org/g/swe0tcJawaban:
Ini dapat berdampak negatif pada kinerja program karena pertimbangan koherensi cache . Menulis ke
flag
setiap kalifunc()
dipanggil akan mengotori baris cache yang memuatnya. Ini akan terjadi terlepas dari fakta bahwa nilai yang ditulis sama persis dengan bit yang ditemukan di alamat tujuan sebelum penulisan.EDIT
hvd telah memberikan alasan bagus lainnya yang mencegah pengoptimalan seperti itu. Ini adalah argumen yang lebih meyakinkan terhadap pengoptimalan yang diusulkan, karena dapat mengakibatkan perilaku tidak terdefinisi, sedangkan jawaban (asli) saya hanya membahas aspek kinerja.
Setelah sedikit refleksi lagi, saya dapat mengusulkan satu contoh lagi mengapa kompiler harus sangat dilarang - kecuali mereka dapat membuktikan bahwa transformasi aman untuk konteks tertentu - dari memperkenalkan penulisan tanpa syarat. Pertimbangkan kode ini:
Dengan penulisan tanpa syarat dalam
func()
hal ini pasti memicu perilaku tidak terdefinisi (menulis ke memori hanya-baca akan menghentikan program, bahkan jika efek penulisan sebaliknya akan menjadi tanpa operasi).sumber
const
untuk meneruskan ke sebuah fungsi yang dapat mengubah data yang merupakan sumber dari perilaku yang tidak ditentukan, bukan penulisan tanpa syarat. Dokter, sakit saat saya melakukan ini ....Selain dari jawaban Leon tentang kinerja:
Misalkan
flag
adalahtrue
. Misalkan dua utas terus memanggilfunc(flag)
. Fungsi seperti yang tertulis, dalam hal ini, tidak menyimpan apa pun keflag
, jadi ini harus aman untuk thread. Dua utas mengakses memori yang sama, tetapi hanya untuk membacanya. Menyetel tanpa syaratflag
ketrue
berarti dua utas berbeda akan menulis ke memori yang sama. Ini tidak aman, ini tidak aman meskipun data yang ditulis identik dengan data yang sudah ada.sumber
[intro.races]/21
.0x01
ke byte yang sudah0x01
menyebabkan perilaku "tidak aman". Pada sistem dengan akses memori kata atau dword itu akan; tetapi pengoptimal harus menyadari hal ini. Pada PC atau OS ponsel modern, tidak ada masalah yang terjadi. Jadi ini bukan alasan yang sah.flag
ada di halaman copy-on-write. Sekarang, di tingkat CPU, perilaku mungkin ditentukan (kesalahan halaman, biarkan OS menanganinya), tetapi di tingkat OS, mungkin masih belum ditentukan, bukan?Saya tidak yakin tentang perilaku C ++ di sini, tetapi di C memori mungkin berubah karena jika memori berisi nilai bukan nol selain 1, itu akan tetap tidak berubah dengan pemeriksaan, tetapi diubah ke 1 dengan pemeriksaan.
Tetapi karena saya tidak terlalu fasih dalam C ++, saya tidak tahu apakah situasi ini mungkin.
sumber
_Bool
?bool
/_Bool
dan meantrue
, maka dalam ABI tertentu itu, Anda mungkin benar._Bool
dan C ++bool
adalah tipe yang sama, atau tipe kompatibel yang mengikuti aturan yang sama. Dengan MSVC, mereka memiliki ukuran dan keselarasan yang sama, tetapi tidak ada pernyataan resmi tentang apakah mereka menggunakan aturan yang sama.<stdbool.h>
menyertakantypedef _Bool bool;
Dan ya, pada x86 (setidaknya di Sistem V ABI),bool
/_Bool
harus 0 atau 1, dengan bit atas byte dihapus. Saya rasa penjelasan ini tidak masuk akal.func
diteruskan dalam RDI, sedangkan Windows akan menggunakan RDX).