C ++ derleyicileri neden bu koşullu boole atamasını koşulsuz bir atama olarak optimize etmiyor?


117

Aşağıdaki işlevi düşünün:

void func(bool& flag)
{
    if(!flag) flag=true;
}

Bana öyle geliyor ki, bayrak geçerli bir boole değerine sahipse, bu, şu şekilde koşulsuz olarak ayarlanmasına eşdeğer olacaktır true:

void func(bool& flag)
{
    flag=true;
}

Yine de ne gcc ne de clang bunu bu şekilde optimize etmez - her ikisi de aşağıdakileri -O3optimizasyon düzeyinde üretir :

_Z4funcRb:
.LFB0:
    .cfi_startproc
    cmp BYTE PTR [rdi], 0
    jne .L1
    mov BYTE PTR [rdi], 1
.L1:
    rep ret

Benim sorum şudur: kod optimize etmek ister ya da bu tür optimizasyon istenmeyen nedenlerine herhangi iyi nedenler vardır çok özel bir durumdur sadece budur, yani verilen flagbir referans değil volatile? Görünen o ki tek neden , okuma noktasında tanımlanmamış bir davranış olmaksızın flagbir true-veya- falsedeğere sahip olabilmesidir , ancak bunun mümkün olup olmadığından emin değilim.


8
Bunun bir "optimizasyon" olduğuna dair herhangi bir kanıtınız var mı?
David Schwartz

1
@ 200_success Başlık olarak çalışmayan biçimlendirmeye sahip bir kod satırı koymanın iyi bir şey olduğunu düşünmüyorum. Daha spesifik bir başlık istiyorsanız, tamam ama İngilizce bir cümle seçin ve içindeki kodlardan kaçınmaya çalışın (örneğin, derleyiciler, eşdeğer olduklarını kanıtlayabildiklerinde neden koşulsuz yazımlara koşullu yazmaları optimize etmiyor? Veya benzer). Ayrıca ters işaretler oluşturulmadığından, kod kullansanız bile başlıkta kullanmayın.
Bakuriu

2
@Ruslan, bu optimizasyonu işlevin kendisi için yapıyor gibi görünmese de, kodu satır içi yapabildiğinde, bunu satır içi sürüm için yapıyor gibi görünüyor. Çoğunlukla, yalnızca kullanılmakta olan bir derleme zaman sabitiyle sonuçlanır 1. godbolt.org/g/swe0tc
Evan Teran

Yanıtlar:


102

Bu, önbellek tutarlılığı ile ilgili hususlar nedeniyle programın performansını olumsuz etkileyebilir . Yazma flagher zaman func()içeren önbellek satırı kirli olur denir. Bu, yazılan değerin yazmadan önce hedef adresinde bulunan bitlerle tam olarak eşleşmesine bakılmaksızın gerçekleşir.


DÜZENLE

hvd , böyle bir optimizasyonu engelleyen başka bir iyi neden daha sağlamıştır . Bu, önerilen optimizasyona karşı daha zorlayıcı bir argümandır, çünkü tanımlanmamış davranışa neden olabilir, oysa benim (orijinal) cevabım yalnızca performans yönlerine değinmiştir.

Biraz daha derinlemesine düşündükten sonra, koşulsuz yazmayı tanıtmaktan, belirli bir bağlam için dönüşümün güvenli olduğunu kanıtlayamazlarsa, derleyicilerin neden şiddetle yasaklanması gerektiğine dair bir örnek daha önerebilirim. Bu kodu düşünün:

const bool foo = true;

int main()
{
    func(const_cast<bool&>(foo));
}

Koşulsuz bir yazma ile func()bu kesinlikle tanımsız davranışı tetikler (salt okunur belleğe yazmak, yazma etkisi başka türlü işlemsiz olsa bile programı sonlandırır).


7
Bir daldan kurtulduğunuz için performansı da olumlu yönde etkileyebilir. Bu nedenle, bu özel durumun, çok özel bir sistem akılda tutulmadan tartışılmasının anlamlı olduğunu düşünmüyorum.
Lundin

3
@Yakk davranış tanımlaması hedef platformdan etkilenmez. Programı sonlandıracağını söylemek yanlıştır, ancak UB'nin kendisinin burun iblisleri de dahil olmak üzere geniş kapsamlı sonuçları olabilir.
John Dvorak

16
@Yakk Bu, "salt okunur bellek" ile ne anlama geldiğine bağlı. Hayır, bir ROM çipinde değil, ancak genellikle yazma erişiminin etkinleştirilmediği bir sayfaya yüklenmiş bir bölümde bulunur ve yazmaya çalıştığınızda örneğin bir SIGSEGV sinyali veya STATUS_ACCESS_VIOLATION istisnası alırsınız.
Random832

5
"bu kesinlikle tanımlanmamış davranışı tetikler". Hayır. Tanımlanmamış davranış, soyut makinenin bir özelliğidir. UB'nin mevcut olup olmadığını belirleyen, kodun söylediği şeydir. Derleyiciler buna neden olamaz (gerçi eğer bir derleyici hata verirse, programların yanlış davranmasına neden olabilir).
Eric M Schmidt

7
Döküm koyma edilir constbir fonksiyonu geçmesine verileri değiştirebilir tanımlanmamış davranış kaynağı değil, koşulsuz yazmak. Doktor, bunu yaptığımda canım acıyor ....
Spencer

48

Leon'un performans konusundaki cevabının yanı sıra:

Varsayalım flagöyle true. İki iş parçacığının sürekli aradığını varsayalım func(flag). Yazıldığı şekliyle işlev, bu durumda hiçbir şey kaydetmez flag, bu nedenle bu iş parçacığı açısından güvenli olmalıdır. İki iş parçacığı aynı belleğe erişir, ancak yalnızca onu okumak için. Koşulsuz flagolarak true, iki farklı iş parçacığının aynı belleğe yazılacağı anlamına gelir. Bu güvenli değildir, yazılan veriler zaten mevcut olan verilerle aynı olsa bile bu güvensizdir.


9
Bunun başvurunun bir sonucu olduğunu düşünüyorum[intro.races]/21 .
Griwes

10
Çok ilginç. Bu yüzden şunu okudum: Derleyicinin, soyut makinenin sahip olmadığı bir yazma işlemini "optimize etmesine" asla izin verilmez.
Martin Ba

3
@MartinBa Çoğunlukla öyle. Ancak derleyici bunun önemli olmadığını kanıtlayabilirse, örneğin başka hiçbir iş parçacığının söz konusu değişkene erişemeyeceğini kanıtlayabilirse, o zaman sorun olmayabilir.

13
Bu, yalnızca derleyicinin hedeflediği sistem onu ​​güvensiz kılıyorsa güvensizdir . 0x01Bir bayta yazmanın zaten 0x01"güvensiz" davranışa neden olduğu bir sistem geliştirmedim . Word veya dword bellek erişimine sahip bir sistemde; ancak optimize edenin bunun farkında olması gerekir. Modern bir PC veya telefon işletim sisteminde sorun olmaz. Yani bu geçerli bir sebep değil.
Yakk - Adam Nevraumont

4
@Yakk Aslında, daha da fazla düşünürsek, bunun sıradan işlemciler için bile doğru olduğunu düşünüyorum. Bence CPU doğrudan belleğe yazabildiğinde haklısınız, ancak varsayalım flagbir yazma üzerine kopyalama sayfasında. Şimdi, CPU seviyesinde, davranış tanımlanabilir (sayfa hatası, işletim sistemi bunu halletmesine izin verin), ancak işletim sistemi seviyesinde, hala tanımlanmamış olabilir, değil mi?

13

Buradaki C ++ davranışından emin değilim, ancak C'de bellek değişebilir çünkü bellek 1'den farklı sıfır olmayan bir değer içeriyorsa, kontrolle değişmeden kalır, ancak kontrolle 1 olarak değişir.

Ama C ++ konusunda pek iyi olmadığım için bu durumun mümkün olup olmadığını bile bilmiyorum.


Bu hala doğru olur _Boolmu?
Ruslan

5
C'de, bellek, ABI'nin türü için geçerli olduğunu söylemediği bir değer içeriyorsa, bu bir tuzak temsilidir ve bir tuzak gösterimini okumak tanımsız bir davranıştır. C ++ 'da, bu yalnızca başlatılmamış bir nesneyi okurken olabilir ve UB olan başlatılmamış bir nesneyi okurken olabilir. Ancak, sıfır olmayan herhangi bir değerin tür bool/ _Boolve araçlar için geçerli olduğunu söyleyen bir ABI bulabilirseniz true, o zaman o belirli ABI'da muhtemelen haklısınızdır.

1
@Ruslan Itanium ABI kullanan derleyicilerde ve ARM işlemcilerde, C _Boolve C ++ boolaynı tür veya aynı kuralları izleyen uyumlu türlerdir. MSVC ile aynı boyut ve hizalamaya sahipler, ancak aynı kuralları kullanıp kullanmadıklarına dair resmi bir açıklama yok.
Justin Zaman - Eski Monica

1
@JustinTime: C'ler <stdbool.h>bir typedef _Bool bool; And yes içerir, x86'da (en azından System V ABI'de) bool/ _Boolbaytın üst bitleri temizlenmiş olarak 0 veya 1 olması gerekir. Bu açıklamanın makul olduğunu düşünmüyorum.
Peter Cordes

1
@JustinTime: Bu doğru, System V ABI'nin tüm x86 çeşitlerinde kesinlikle aynı semantiğe sahip olduğunu belirtmeliydim, bu soru da buydu. ( funcWindows RDX kullanırken ilk argümanın RDI'da geçtiğini söyleyebilirim ).
Peter Cordes
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.