#pragma paketi efekti


233

Birisi bana #pragma packönişlemci deyiminin ne yaptığını ve daha da önemlisi neden kullanmak istediğini açıklayabilir mi diye merak ediyordum .

Bazı bilgiler sunan MSDN sayfasını kontrol ettim , ancak deneyimi olan insanlardan daha fazlasını duymayı umuyordum. Ben artık nerede bulamıyorum görünmesine rağmen, daha önce kodda gördüm.


1
Bir yapının belirli bir hizalamasını / paketlenmesini zorlar, ancak tüm #pragmadirektifler gibi bunlar da uygulama tanımlıdır.
dreamlax

A mod s = 0burada A adres ve s veri tipinin boyutudur; bu bir verinin yanlış hizalanıp hizalanmadığını kontrol eder.
legends2k

Yanıtlar:


422

#pragma packderleyiciye yapı elemanlarını belirli bir hizalamayla paketlemesi talimatını verir. Çoğu derleyici, bir yapı bildirdiğinizde, üyeler arasında, bellekteki uygun adreslere (genellikle türün boyutunun bir katı) hizalandıklarından emin olmak için dolgu ekler. Bu, düzgün hizalanmamış değişkenlere erişmeyle ilişkili bazı mimarilerde performans cezasını (veya açık hatayı) önler. Örneğin, 4 baytlık tamsayılar ve aşağıdaki yapı verilir:

struct Test
{
   char AA;
   int BB;
   char CC;
};

Derleyici, yapıyı şu şekilde bellekte bırakmayı seçebilir:

|   1   |   2   |   3   |   4   |  

| AA(1) | pad.................. |
| BB(1) | BB(2) | BB(3) | BB(4) | 
| CC(1) | pad.................. |

ve sizeof(Test)yalnızca 6 bayt veri içermesine rağmen 4 × 3 = 12 olur. #pragma(Bildiğim kadarıyla) için en yaygın kullanım durumu , derleyicinin verilere dolgu eklemediğinden ve her üyenin bir öncekini izlediğinden emin olmanız gereken donanım aygıtlarıyla çalışmaktır. Bununla birlikte #pragma pack(1), yukarıdaki yapı şu şekilde ortaya konacaktır:

|   1   |

| AA(1) |
| BB(1) |
| BB(2) |
| BB(3) |
| BB(4) |
| CC(1) |

Ve sizeof(Test)1 × 6 = 6 olur.

Bununla birlikte #pragma pack(2), yukarıdaki yapı şu şekilde ortaya konacaktır:

|   1   |   2   | 

| AA(1) | pad.. |
| BB(1) | BB(2) |
| BB(3) | BB(4) |
| CC(1) | pad.. |

Ve sizeof(Test)2 × 4 = 8 olur.

Yapıdaki değişkenlerin sırası da önemlidir. Değişkenler aşağıdaki gibi sıralanmıştır:

struct Test
{
   char AA;
   char CC;
   int BB;
};

ve #pragma pack(2)yapı şu şekilde düzenlenir:

|   1   |   2   | 

| AA(1) | CC(1) |
| BB(1) | BB(2) |
| BB(3) | BB(4) |

ve sizeOf(Test)3 × 2 = 6 olur.


76
Ambalajın dezavantajlarını eklemeye değer olabilir. (hizalanmamış nesne erişimi en iyi durumda yavaştır , ancak bazı platformlarda hatalara neden olur.)
jalf

11
Bahsedilen hizalamalar "performans cezası" bazı sistemlerde gerçekten yararlı olabilir gibi görünüyor danluu.com/3c-conflict .

6
@Pacerier Pek değil. Bu yazı oldukça aşırı bir hizalamadan bahsediyor (4KB sınırlarına uyum sağlayarak). CPU, çeşitli veri türleri için belirli minimum hizalamaları bekler, ancak bunlar en kötü durumda 8 baytlık hizalama gerektirir (16 veya 32 bayt hizalama gerektirebilecek vektör türlerini saymaz). Bu sınırlara hizalanmamak genellikle size dikkat çekici bir performans vuruşu verir (çünkü bir yük bir yerine iki işlem olarak yapılması gerekebilir), ancak tür ya iyi hizalanmış ya da değil. Bundan daha katı hizalama size hiçbir şey satın almaz (ve önbellek kullanımını
bozar

6
Başka bir deyişle, bir çift 8 bayt sınırında olmayı bekler. 7 baytlık bir sınıra koymak performansa zarar verir. Ancak 16, 32, 64 veya 4096 baytlık bir sınır koymak, 8 baytlık sınırın size zaten verdiğinden daha fazla bir şey satın almaz. CPU'dan aynı performansı alırken, bu yayında belirtilen nedenlerden ötürü çok daha kötü önbellek kullanımı elde edersiniz.
jalf

4
Yani ders "paketleme yararlıdır" değildir (paketleme türlerin doğal uyumunu ihlal eder, böylece performansı düşürür), ama basitçe "gerekenden öte aşırı hizalanmayın"
jalf

27

#pragmaderleyiciye taşınabilir olmayan (yalnızca bu derleyicide olduğu gibi) iletiler göndermek için kullanılır. Belirli uyarıları ve paketleme yapılarını devre dışı bırakmak gibi şeyler yaygın nedenlerdir. Belirli uyarıların devre dışı bırakılması, özellikle uyarı bayrağı açıkken uyarılarla derlerseniz yararlıdır.

#pragma packPaketlenmekte olan yapının, üyelerinin hizalı olmaması gerektiğini belirtmek için özellikle kullanılır. Bir donanıma bellek eşlemeli arabiriminiz olduğunda ve farklı yapı üyelerinin işaret ettiği yeri tam olarak kontrol edebilmeniz gerektiğinde kullanışlıdır. Çoğu makine hizalanmış verilerle uğraşırken çok daha hızlı olduğu için, özellikle iyi bir hız optimizasyonu değildir.


17
Daha sonra geri almak için şunu yapın: #pragma pack (push, 1) ve #pragma pack (pop)
malhal

16

Derleyiciye bir yapıdaki nesneleri hizalama sınırını söyler. Örneğin, şöyle bir şey varsa:

struct foo { 
    char a;
    int b;
};

Tipik bir 32 bit makine ile, normalde arasında 3 bayt dolgu olmasını "istersiniz" ave bböylece berişim hızını en üst düzeye çıkarmak için 4 baytlık bir sınırda inersiniz (ve bu genellikle varsayılan olarak olacaktır).

Bununla birlikte, derleyicinin yapınızı tam olarak bu dış tanıma göre düzenlemesini sağlamak için harici olarak tanımlanmış bir yapı ile eşleşmeniz gerekiyorsa. Bu durumda, bir derleyici verebilir #pragma pack(1)bunu anlatmak için değil üyeleri arasında herhangi dolgu eklemek için - yapının tanımı üyeleri arasındaki dolgu içeriyorsa, siz (açıkça takın örneğin tipik adlandırılmış üyelerle unusedNveya ignoreNveya bu konuda bir şey sipariş).


"normalde" a ve b arasında 3 bayt dolgu olmasını istersiniz, böylece b erişim hızını en üst düzeye çıkarmak için 4 baytlık bir sınıra iner "- 3 bayt dolgu olması erişim hızını nasıl en üst düzeye çıkarır?
Ashwin

8
@Ashwin: b4 baytlık bir sınıra yerleştirmek , işlemcinin 4 baytlık tek bir yük vererek yükleyebileceği anlamına gelir. İşlemciye biraz bağlı olsa da, garip bir sınırdaysa, yükleme işleminin işlemcinin iki ayrı yükleme talimatı vermesini gerektirme şansı vardır, daha sonra bu parçaları bir araya getirmek için bir kaydırıcı kullanın. Tipik ceza, söz konusu öğenin 3 kat daha yavaş yüklenmesidir.
Jerry Coffin

... hizalanmış ve hizalanmamış int okumak için montaj koduna bakarsanız, hizalanmış okuma genellikle tek bir anımsatıcıdır. Hizalanmamış okuma, int'i bir araya getirdiği, bayt baytını topladığı ve kaydın doğru yerlerine yerleştirdiği için 10 montaj hattı olabilir.
SF.

2
@SF .: Olabilir - ancak olmasa bile yanlış yönlendirmeyin - x86 CPU'da (açık bir örnek için) işlemler donanımda gerçekleştirilir, ancak yine de kabaca aynı işlem kümesine sahip olursunuz. ve yavaşlama.
Jerry Coffin

8

Veri öğeleri (örneğin, sınıfların ve yapıların üyeleri), erişim sürelerini iyileştirmek için tipik olarak mevcut nesil işlemciler için WORD veya DWORD sınırlarında hizalanır. DWORD'u 4 ile bölünemeyen bir adreste almak için 32 bit işlemcide en az bir ekstra CPU döngüsü gerekir. Bu nedenle, örneğin üç karakter üyeniz varsa char a, b, c;, aslında 6 veya 12 bayt depolama alanı alma eğilimindedirler.

#pragmaerişim hızı pahasına veya farklı derleyici hedefleri arasında depolanan verilerin tutarlılığı için daha verimli alan kullanımı elde etmek için bunu geçersiz kılmanıza olanak tanır. Ben 16 bit 32 bit kod geçiş ile çok eğlendim; 64 bit kodu taşımak bazı kod için aynı tür baş ağrılarına neden olacağını bekliyoruz.


Aslında, char a,b,c;genellikle 3 veya 4 bayt depolama alanı alır (en azından x86'da) - bunun nedeni hizalama gereksinimlerinin 1 bayt olmasıdır. Eğer olmasaydı, nasıl başa çıkardın char str[] = "foo";? A'ya erişim charher zaman basit bir getirme-kaydırma maskesidir, oysa a'ya erişim inthizalanmış olup olmamasına bağlı olarak getirme-birleştirme veya birleştirme olabilir. int(x86'da) 32 bit (4 bayt) bir hizalamaya sahiptir, aksi takdirde intdiğerinde bir buçuk yarım alırsınız (diyelim) DWORDve bu iki arama gerektirir.
Tim Čas

3

Derleyici, belirli bir platformda maksimum performans elde etmek için yapılardaki üyeleri hizalayabilir. #pragma packyönergesi bu hizalamayı kontrol etmenizi sağlar. Genellikle optimum performans için varsayılan olarak bırakmalısınız. Uzak makineye bir yapı geçirmeniz gerekiyorsa, genellikle #pragma pack 1istenmeyen hizalamaları hariç tutmak için kullanırsınız .


2

Bir derleyici , belirli bir mimarideki performans nedenlerinden dolayı yapı üyelerini belirli bayt sınırlarına yerleştirebilir. Bu, üyeler arasında kullanılmayan dolgu bırakabilir. Yapı paketleme, üyeleri bitişik olmaya zorlar.

Bu, örneğin, verilere ihtiyaç duyduğunuz verilerin bir sekans içindeki belirli konumlarda olmasını sağlayan belirli bir dosyaya veya iletişim biçimine uyması için bir yapıya ihtiyaç duyuyorsanız önemli olabilir. Bununla birlikte, bu tür kullanım endianess sorunlarıyla ilgilenmez, bu yüzden kullanılmasına rağmen taşınabilir olmayabilir.

Ayrıca, kayıt erişiminin doğrudan adreslerden ziyade bir yapı üzerinden olması için, örneğin bir UART veya USB kontrol cihazı gibi bazı I / O cihazının dahili kayıt yapısının tam olarak üst üste binmesi de mümkündür.


1

Bunu yalnızca, yalnızca kayıt siparişi ve hizalaması için katı gereksinimleri olan bazı donanımlara (örneğin, bellek eşlemeli bir cihaz) kod yazıyorsanız kullanmak istersiniz.

Ancak bu, bu amaca ulaşmak için oldukça künt bir araç gibi görünüyor. Daha iyi bir yaklaşım, montajcıda bir mini sürücüyü kodlamak ve bu pragma ile uğraşmak yerine bir C çağrı arayüzü vermek olacaktır.


Aslında sık sık erişilmeyen büyük tablolarda yer kazanmak için oldukça çok kullanın. Orada, sadece yer kazanmak ve katı bir hizalama için değil. (Sadece sana oy verdim, btw. Birisi sana olumsuz oy vermişti.)
Todd Lehman

1

Sadece eski kod ile arayüz olsa da, daha önce kodda kullandım. Bu, daha önceki bir Karbon sürümünden (orijinal M68k System 6.5 sürümü ile geriye dönük uyumlu ... tercih edersiniz) tercih dosyalarını yüklemek için gereken bir Mac OS X Kakao uygulamasıydı. Orijinal sürümdeki tercih dosyaları, #pragma pack(1)fazladan yer kaplamaktan ve gereksiz yere tasarruf etmekten kaçınmak için kullanılan bir yapılandırma yapısının ikili dökümüdür (başka bir deyişle yapıda olabilecek dolgu baytları).

Kodun orijinal yazarları, #pragma pack(1)süreçler arası iletişimde mesaj olarak kullanılan yapıları saklamak için de kullanmışlardır . Bence neden bazen kod başlangıçtan itibaren (ewww) bir dizi bayt sayarak mesaj yapısının belirli bir bölümüne baktı bilinmeyen veya değişen dolgu boyutları olasılığını önlemek oldu.


1

İnsanların bir yapının çok iş parçacıklı bir bağlamda yanlış paylaşımı önlemek için tüm bir önbellek hattını aldığından emin olmak için kullandığını gördüm. Varsayılan olarak gevşek bir şekilde paketlenecek çok sayıda nesneye sahip olacaksanız, bellek tasarrufu yapabilir ve bunları daha sıkı paketlemek için önbellek performansını artırabilir, ancak hizalanmamış bellek erişimi genellikle işleri yavaşlatır, böylece bir dezavantaj olabilir.


0

#Pragma paketinin sunduğu veri tutarlılığını elde etmenin başka yolları olduğunu unutmayın (örneğin, bazı insanlar ağ üzerinden gönderilmesi gereken yapılar için #pragma paketi (1) kullanır). Örneğin, aşağıdaki koda ve sonraki çıktısına bakın:

#include <stdio.h>

struct a {
    char one;
    char two[2];
    char eight[8];
    char four[4];
};

struct b { 
    char one;
    short two;
    long int eight;
    int four;
};

int main(int argc, char** argv) {
    struct a twoa[2] = {}; 
    struct b twob[2] = {}; 
    printf("sizeof(struct a): %i, sizeof(struct b): %i\n", sizeof(struct a), sizeof(struct b));
    printf("sizeof(twoa): %i, sizeof(twob): %i\n", sizeof(twoa), sizeof(twob));
}

Çıktı aşağıdaki gibidir: sizeof (yapı a): 15, sizeof (yapı b): 24 sizeof (twoa): 30, sizeof (twob): 48

Struct a büyüklüğü (bkz bayt sayısıdır, ancak yapı b eklendi dolgu ne tam olarak nasıl Bildirimi Bu dolgu ayrıntıları için). #Pragma paketinin aksine bunu yaparak, "kablo biçimini" uygun türlere dönüştürme kontrolüne sahip olabilirsiniz. Örneğin, "kısa int" et cetera'ya "iki karakter [2]" ekleyin.


Hayır, yanlış. Eğer b.two'nun belleğindeki pozisyona bakarsanız, b.one'dan sonra bir bayt değil (derleyici b.two'yu kelime erişimine hizalanacak şekilde hizalayabilir (ve sıklıkla). İki kişi için, bir taneden sonra tam olarak bir bayt. A.two'ya kısa bir int olarak erişmeniz gerekiyorsa, 2 seçeneğiniz olmalıdır, bir birleşim kullanın (ancak bu genellikle endianness sorununuz varsa başarısız olur) veya kodla paketi aç / dönüştür (uygun ntohX işlevini kullanarak)
xryl669

1
sizeofBir döner size_tki kullanılarak basılmış olmalıdır%zu . Yanlış format belirtecini kullanmak tanımlanmamış davranışı başlatır
phuclv

0

Neden kullanmak istersiniz?

Yapının hafızasını azaltmak

Neden kullanılmasın ki?

  1. Bu durum performans cezasına neden olabilir, çünkü bazı sistem hizalanmış veriler üzerinde daha iyi çalışır
  2. Bazı makine hizalanmamış verileri okuyamaz
  3. Kod taşınabilir değil
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.