Restrict anahtar kelimesi C ++ içinde ne anlama geliyor?


182

Her zaman emin değilim, restrict anahtar kelime C ++ ne anlama geliyor?

Bu işleve verilen iki veya daha fazla işaretçinin üst üste gelmediği anlamına mı geliyor? Başka ne anlama geliyor?


23
restrictbir c99 anahtar kelimesidir. Evet, Rpbert S. Barnes, çoğu derleyicinin desteklediğini biliyorum __restrict__. Çift alt çizgisi olan herhangi bir şeyin, tanım gereği, uygulamaya özel ve dolayısıyla C ++ DEĞİL olduğunu , ancak bunun derleyiciye özgü bir sürümü olduğunu göreceksiniz .
KitsuneYMG

5
Ne? Sadece uygulamaya özgü olduğu için C ++ yapmaz; C ++ belirli öğelerin açıkça uygulanmasına izin verir ve buna izin vermez veya C ++ oluşturmaz.
Alice

4
@Alice KitsuneYMG, ISO C ++ 'ın bir parçası olmadığı ve bunun yerine C ++ uzantısı olarak kabul edildiği anlamına gelir. Derleyici içerik oluşturucuların, ISO C ++ ile bir arada bulunan ve C ++ 'a genellikle daha az veya taşınabilir olmayan gayri resmi bir ilacın bir parçası olarak hareket eden kendi uzantılarını oluşturmalarına ve dağıtmalarına izin verilir. Örnekler MS'nin eski Yönetilen C ++ ve daha yeni C ++ / CLI'si olabilir. Diğer örnekler, ortak derleme #warningveya işlev imza makroları ( __PRETTY_FUNCTION__GCC'de, __FUNCSIG__MSVC'de, vb.) Gibi bazı derleyiciler tarafından sağlanan önişlemci yönergeleri ve makrolarıdır .
Justin Time - Monica'yı

4
@Alice Bildiğim kadarıyla C ++ 11, C99'un tamamı için tam destek gerektirmiyor, C ++ 14 veya C ++ 17 hakkında bildiklerimi de desteklemiyor. restrictbir C ++ anahtar kelimesi olarak kabul edilmez (bkz. en.cppreference.com/w/cpp/keyword ) ve aslında restrictC ++ 11 standardında belirtilen tek şey (bkz. open-std.org/jtc1/sc22/wg21 /docs/papers/2012/n3337.pdf , küçük editoryal değişikliklerle FDIS'in bir kopyası, §17.2 [kütüphane.c], PDF sayfa 413) şunu belirtiyor:
Justin Time - Monica

4
@Alice Nasıl? Ben söylüyor kısmını ifade restrictedilecek çıkarılmıştır bu fonksiyonlar C ++ standart kütüphanesinde dahil edildiğinde C standart kütüphanesi işlevi imzalar ve semantik (dışında kalan, dışlanan). Başka bir deyişle, bir C standart kitaplık işlevinin imzasının restrictC'de restrictbulunması durumunda , anahtar kelimenin C ++ eşdeğerinin imzasından kaldırılması gerektiğini söyleyen gerçeği belirtmiştim .
Justin Time - Monica

Yanıtlar:


143

Bellek Optimizasyonu adlı makalesinde, Christer Ericson restricthenüz C ++ standardının bir parçası olmasa da, birçok derleyici tarafından desteklendiğini ve kullanılabilir olduğunda kullanılmasını önerdiğini söylüyor:

anahtar kelimeyi kısıtla

! 1999 ANSI / ISO C standardında yeni

! Henüz C ++ standardında değil, birçok C ++ derleyicisi tarafından destekleniyor

! Sadece bir ipucu, hiçbir şey yapamayabilir ve yine de uyumlu olabilir

Kısıtlamaya uygun bir işaretçi (veya başvuru) ...

! ... temel olarak derleyiciye imlecin kapsamı için imlecin hedefine sadece o imleçten (ve ondan kopyalanan imleçlerden) erişileceğine dair bir vaattir.

Onu destekleyen C ++ derleyicilerinde muhtemelen C'deki gibi davranmalıdır.

Ayrıntılar için şu SO yayınına bakın: C99 'restrict' anahtar kelimesinin gerçekçi kullanımı?

Ericsson'un gazetesinde dolaşmak için yarım saat ayırın, ilginç ve zaman ayırmaya değer.

Düzenle

IBM'in AIX C / C ++ derleyicisinin __restrict__anahtar kelimeyi desteklediğini de buldum .

Aşağıdaki program g ++ üzerinde temiz bir şekilde derlendiğinden g ++ bunu destekliyor gibi görünüyor:

#include <stdio.h>

int foo(int * __restrict__ a, int * __restrict__ b) {
    return *a + *b;
}

int main(void) {
    int a = 1, b = 1, c;

    c = foo(&a, &b);

    printf("c == %d\n", c);

    return 0;
}

Ayrıca kullanımı hakkında güzel bir makale buldum restrict:

Anahtar Kelimeyi Kısıtlamayı Belirleme

Edit2

Özellikle C ++ programlarında restrict kullanımını tartışan bir makaleyle karşılaştım:

Load-hit-store'lar ve __restrict anahtar kelimesi

Ayrıca, Microsoft Visual C ++ anahtar sözcüğü de destekler__restrict .


2
Bellek Optimizasyonu kağıt bağlantısı öldü, işte GDC sunumundaki ses bağlantısı. Hafıza
Grimeh

1
@EnnMichael: Açıkçası taşınabilir bir C ++ projesinde kullanacaksanız, buna #ifndef __GNUC__ #define __restrict__ /* no-op */benzer bir şey yapmanız gerekir . Ve bunu tanımlayan __restricthalinde _MSC_VERtanımlanır.
Peter Cordes

96

Diğerlerinin söylediği gibi, C ++ 14'ten başka bir şey ifade __restrict__etmiyorsa, C99 ile aynı şeyi yapan GCC uzantısını düşünelim restrict.

C99

restrictiki göstergenin çakışan bellek bölgelerine işaret edemediğini söylüyor. En yaygın kullanım işlevi işlev bağımsız değişkenleridir.

Bu, işlevin nasıl çağrılacağını kısıtlar, ancak daha fazla derleme optimizasyonu sağlar.

Arayan restrictsözleşmeyi takip etmezse , tanımsız davranış.

C99 N1256 taslak 6.7.3 / 7 "Tip eleme" diyor:

Kısıtlayıcı niteleyicinin amaçlanan kullanımı (yazmaç saklama sınıfı gibi) optimizasyonu teşvik etmek ve uygun bir program oluşturan tüm önişleme çeviri birimlerinden niteleyicinin tüm örneklerinin silinmesi anlamını değiştirmez (yani gözlemlenebilir davranış).

ve 6.7.3.1 "Kısıtlamanın resmi tanımı" kanlı detayları verir.

Olası bir optimizasyon

Vikipedi örnek olduğunu çok aydınlatıcı.

Bir montaj talimatını nasıl kaydedebileceğini açıkça göstermektedir .

Kısıtlama olmadan:

void f(int *a, int *b, int *x) {
  *a += *x;
  *b += *x;
}

Sözde montaj:

load R1  *x    ; Load the value of x pointer
load R2  *a    ; Load the value of a pointer
add R2 += R1    ; Perform Addition
set R2  *a     ; Update the value of a pointer
; Similarly for b, note that x is loaded twice,
; because x may point to a (a aliased by x) thus 
; the value of x will change when the value of a
; changes.
load R1  *x
load R2  *b
add R2 += R1
set R2  *b

Kısıtlama ile:

void fr(int *restrict a, int *restrict b, int *restrict x);

Sözde montaj:

load R1  *x
load R2  *a
add R2 += R1
set R2  *a
; Note that x is not reloaded,
; because the compiler knows it is unchanged
; "load R1 ← *x" is no longer needed.
load R2  *b
add R2 += R1
set R2  *b

GCC gerçekten yapıyor mu?

g++ 4.8 Linux x86-64:

g++ -g -std=gnu++98 -O0 -c main.cpp
objdump -S main.o

İle -O0, onlar aynı.

İle -O3:

void f(int *a, int *b, int *x) {
    *a += *x;
   0:   8b 02                   mov    (%rdx),%eax
   2:   01 07                   add    %eax,(%rdi)
    *b += *x;
   4:   8b 02                   mov    (%rdx),%eax
   6:   01 06                   add    %eax,(%rsi)  

void fr(int *__restrict__ a, int *__restrict__ b, int *__restrict__ x) {
    *a += *x;
  10:   8b 02                   mov    (%rdx),%eax
  12:   01 07                   add    %eax,(%rdi)
    *b += *x;
  14:   01 06                   add    %eax,(%rsi) 

Deneyimsiz olanlar için çağıran sözleşme şöyledir:

  • rdi = ilk parametre
  • rsi = ikinci parametre
  • rdx = üçüncü parametre

GCC çıktısı wiki makalesinden bile daha netti: 4 talimat vs 3 talimat.

Diziler

Şimdiye kadar tek bir talimat tasarrufumuz var, ancak eğer işaretçi döngü yapılacak dizileri temsil ediyorsa, ortak bir kullanım durumu, o zaman supercat ve michael tarafından belirtildiği gibi bir grup talimat kaydedilebilir .

Örneğin şunu düşünün:

void f(char *restrict p1, char *restrict p2, size_t size) {
     for (size_t i = 0; i < size; i++) {
         p1[i] = 4;
         p2[i] = 9;
     }
 }

Çünkü restrictakıllı bir derleyici (veya insan) bunu optimize edebilir:

memset(p1, 4, size);
memset(p2, 9, size);

İyi bir libc uygulamasında (glibc gibi) montaj optimize edilmiş olabileceğinden potansiyel olarak çok daha etkilidir Performans açısından std :: memcpy () veya std :: copy () kullanmak daha mı iyi? , muhtemelen SIMD talimatları ile .

Kısıtlama olmadan, bu optimizasyon yapılamadı, örneğin:

char p1[4];
char *p2 = &p1[1];
f(p1, p2, 3);

Sonra forsürüm yapar:

p1 == {4, 4, 4, 9}

ederken memsetversiyonu yapar:

p1 == {4, 9, 9, 9}

GCC gerçekten yapıyor mu?

GCC 5.2.1. Linux x86-64 Ubuntu 15.10:

gcc -g -std=c99 -O0 -c main.c
objdump -dr main.o

İle -O0, ikisi de aynı.

İle -O3:

  • kısıtlama ile:

    3f0:   48 85 d2                test   %rdx,%rdx
    3f3:   74 33                   je     428 <fr+0x38>
    3f5:   55                      push   %rbp
    3f6:   53                      push   %rbx
    3f7:   48 89 f5                mov    %rsi,%rbp
    3fa:   be 04 00 00 00          mov    $0x4,%esi
    3ff:   48 89 d3                mov    %rdx,%rbx
    402:   48 83 ec 08             sub    $0x8,%rsp
    406:   e8 00 00 00 00          callq  40b <fr+0x1b>
                            407: R_X86_64_PC32      memset-0x4
    40b:   48 83 c4 08             add    $0x8,%rsp
    40f:   48 89 da                mov    %rbx,%rdx
    412:   48 89 ef                mov    %rbp,%rdi
    415:   5b                      pop    %rbx
    416:   5d                      pop    %rbp
    417:   be 09 00 00 00          mov    $0x9,%esi
    41c:   e9 00 00 00 00          jmpq   421 <fr+0x31>
                            41d: R_X86_64_PC32      memset-0x4
    421:   0f 1f 80 00 00 00 00    nopl   0x0(%rax)
    428:   f3 c3                   repz retq

    memsetBeklendiği gibi iki çağrı.

  • kısıtlama olmadan: hiçbir stdlib çağrıları, sadece burada çoğaltmak niyetinde değil 16 yineleme geniş bir döngü unrolling :-)

Ben onları karşılaştırmak için sabır yoktu, ama kısıtlama sürümü daha hızlı olacağına inanıyorum.

Katı takma adlandırma kuralı

restrictAnahtar kelime yalnızca uyumlu türleri işaretçileri (örneğin iki etkileyen int*sıkı örtüşme kuralları uyumsuz türleri aliasing varsayılan olarak tanımlanmamış davranış olduğu belirtildiği için) ve derleyiciler varsayabiliriz yüzden uzakta gerçekleşmesi ve optimize etmez.

Bkz . Katı takma adlandırma kuralı nedir?

Referanslar için çalışıyor mu?

GCC belgelerine göre şunu yapar: https://gcc.gnu.org/onlinedocs/gcc-5.1.0/gcc/Restricted-Pointers.html sözdizimi ile:

int &__restrict__ rref

thisÜye işlevlerinin bir sürümü bile vardır :

void T::fn () __restrict__

güzel asnwer. Sıkı takma ad devre dışı bırakılırsa -fno-strict-aliasing, restrictaynı tür veya farklı türdeki işaretçiler arasında hiçbir fark yaratmamalıdır, hayır? ("restrict anahtar kelimesi yalnızca uyumlu türlerin işaretçilerini etkiler"
ifadesini kullanıyorum

@ tobi303 Bilmiyorum!
Kesin olarak öğrenirseniz

@jww evet, bunu ifade etmenin daha iyi bir yolu. Güncellenmiş.
Ciro Santilli 法轮功 冠状 病 六四 事件 法轮功

restrictC ++ 'da bir şey ifade ediyor. restrictBir C ++ programındaki parametrelerle bir C kütüphanesi işlevini çağırırsanız , bunun sonuçlarına uymanız gerekir. Temel olarak, restrictbir C kütüphanesi API'sinde kullanılırsa, Lisp'ten dinamik FFI dahil olmak üzere herhangi bir dilden çağıran herkes için bir şey ifade eder.
Kaz

22

Hiçbir şey değil. C99 standardına eklendi.


8
Bu tamamen doğru değil. Görünüşe göre bazı C ++ derleyicileri tarafından destekleniyor ve bazı insanlar kullanılabilir olduğunda kullanılmasını şiddetle tavsiye ediyor, aşağıdaki cevabıma bakın.
Robert S. Barnes

18
@Robert S Barnes: C ++ standardı restrictbir anahtar kelime olarak tanımıyor . Dolayısıyla cevabım doğru. Açıkladığınız şey uygulamaya özel davranış ve gerçekten güvenmemeniz gereken bir şeydir .
dirkgently

27
@dirkgently: Saygılarımızla, neden olmasın? Birçok proje, yalnızca belirli veya çok az derleyici tarafından desteklenen standart dışı dil uzantılarına bağlıdır. Linux Çekirdeği ve gcc akla geliyor. Belirli bir derleyiciye, hatta belirli bir derleyicinin projenin tüm kullanım ömrü boyunca belirli bir revizyonuna bağlı kalmak nadir değildir. Her programın kesinlikle uyumlu olması gerekmez.
Robert S. Barnes

7
@Rpbert S. Barnes: Uygulamaya özgü davranışlara neden bağlı olmamanız gerektiğini daha fazla vurgulamıyorum. Linux ve gcc'ye gelince - düşünün ve neden savunmanızda iyi bir örnek olmadıklarını göreceksiniz. Henüz yaşamı boyunca tek bir derleyici sürümünde orta derecede başarılı bir yazılım parçası göremiyorum .
dirkgently

16
@Rpbert S. Barnes: Soru c ++ dedi. MSVC değil, gcc değil, AIX değil. Acidzombie24 derleyiciye özel uzantılar isteseydi, bunu söylemiş / etiketlemeliydi.
KitsuneYMG

12

Bu , bu anahtar kelimeyi eklemek için orijinal tekliftir. Bununla birlikte, açıkça belirtildiği gibi, bu bir C99 özelliğidir; C ++ ile ilgisi yoktur.


5
Birçok C ++ derleyicisi __restrict__, anlayabildiğim kadarıyla aynı olan anahtar kelimeyi destekliyor .
Robert S. Barnes

C ++ ile ilgili her şeye sahiptir, çünkü C ++ programları C kütüphanelerini çağırır ve C kütüphaneleri kullanır restrict. C ++ programının davranışı, ima ettiği kısıtlamaları ihlal ederse tanımsız hale gelir restrict.
Kaz

@kaz Tamamen yanlış. C ++ ile bir ilgisi yoktur, çünkü C ++ 'ın bir anahtar kelimesi veya özelliği değildir ve C ++' da C başlık dosyaları kullanıyorsanız, restrictanahtar kelimeyi kaldırmanız gerekir . Tabii ki, onları işaretlenmiş bir C işlevine (C ++ veya C'den yapabilirsiniz) bildiren bir C işaretine geçirirseniz tanımsızdır, ancak bu sizin üzerinizdedir.
Jim Balter

@JimBalter Gördüğüm kadarıyla, C ++ programlarının C kitaplıkları çağırması ve C kitaplıklarının kullanması restrict. C ++ programının davranışı, restrict tarafından ima edilen kısıtlamaları ihlal ederse tanımsız hale gelir. Ama bunun aslında C ++ ile bir ilgisi yok, çünkü “sizin üzerinizde”.
Kaz

5

C ++ 'da böyle bir anahtar kelime yoktur. C ++ anahtar sözcüklerinin listesi C ++ dil standardının 2.11 / 1 bölümünde bulunabilir. restrictC dilinin C99 sürümünde bir anahtar kelimedir ve C ++ dilinde değildir.


5
Birçok C ++ derleyicisi __restrict__, anlayabildiğim kadarıyla aynı olan anahtar kelimeyi destekliyor .
Robert S. Barnes

18
@Robert: Ama C ++ 'da böyle bir anahtar kelime yok . Bireysel derleyicilerin yaptıkları kendi işidir, ancak C ++ dilinin bir parçası değildir.
jalf

4

Bazı C kitaplıklarındaki başlık dosyaları anahtar kelimeyi kullandığından, C ++ dili bu konuda bir şeyler yapmak zorunda kalacaktır .. en azından anahtar kelimeyi yok sayarak, anahtar kelimeyi bastırmak için # anahtar kelimeyi boş bir makroya tanımlamak zorunda değiliz .


3
Bunun yerine bir extern Cbildirim kullanarak veya bunun yerine __rerstrict__anahtar kelimeyi işleyen AIX C / C ++ derleyicisi ile olduğu gibi sessizce bırakılması ile ele tahmin ediyorum . Bu anahtar kelime gcc altında da desteklenir, böylece kod g ++ altında aynı şekilde derlenir.
Robert S. Barnes
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.