İçeriğe geç
academia.sh

Ders 15 / 15

Optimizasyon Düzeyleri

Eniyileyicinin okunmayan bir yazmayı sildiği model üzerinden as-if kuralı, volatile'ın verdiği garanti ve kursun on beş dersinin toplu dökümü.

Yayın Güncelleme

Bu derste

  • as-if kuralının bir yazmayı ne zaman silmeye izin verdiğini bir model üzerinden gösterir
  • volatile işaretli bir yazmanın neden silinmediğini açıklar
  • kursun on beş değişikliğini sınıflarına göre bir tabloda toplar
İçindekiler

Bir önceki ders, hata ayıklayıcının gördüğü değerin kaynaktaki yazımla her zaman aynı olmadığını söyleyerek kapandı: bir değişken yazmaçta tutulmuş ya da tamamen silinmiş olabilir. Bu ders o farkın nereden geldiğini ele alıyor. Eniyileyici, gözlenebilir davranışı değiştirmeyen bir yazmayı kaldırabilir — kaldırdığı yazma hiçbir okuyucuya ulaşmaz. Kurs bunu tek bir soruyla ölçüyordu: bu değişikliği kim görüyor? Eniyileme için yanıt, geri kalan on dört değişiklikten farklı bir yoldan gözlenmeyene varıyor: burada ne bir kopyalama var ne de bir aracı. Yazma vardır, ama hiçbir okuma ona ulaşmadan üstüne başka bir yazma gelir.

Okunmadan Ezilen Yazma

Bir akışı üç adımla düşünün: bir yere 1 yazılır, aynı yere 2 yazılır, sonra o konum bir yerde okunur. Birinci yazmanın etkisi, ikinci yazma gelene kadar hiç okunmadıysa, ikinci yazma geldiğinde birinci yazmanın konumdaki izi tamamen kaybolmuştur. Programın davranışını gözleyen hiçbir taraf — ne bir dosyaya yazılan çıktı, ne bir başka sürece giden bir değer, ne de bir kilitle korunan paylaşılan durum — birinci yazmanın hiç var olup olmadığını ayırt edemez. Program birinci yazmayı hiç yapmasa da gözlenen sonuç değişmez.

PA10. Bir eniyileyici modeli, ardışık iki yazma arasında hiçbir okuma olmadığını tespit ettiğinde birincisini kaldırabilir; kaldırmasa da gözlenebilir sonuç aynıdır.

Ölçüm, kursun çekirdeğiyle aynı kayıt biçimini kullanıyor:

// gozlem.h — bir degisikligin kime ulastigini siniflayan kayit
#ifndef GOZLEM_H
#define GOZLEM_H

enum Sinif { KOPYA, ORTAK, AKTARIM, GOZLENMEYEN };

/* Bir degisikligi kaydeder: gordu, degisikligi yapmayan tarafin onu
   okuyup okuyamadigidir (1 ya da 0). */
void gozlem(const char *degisiklik, enum Sinif sinif, int gordu);

/* Sinif basina sayilari ve ulasan / ulasmayan toplamini basar. */
void gozlem_ozeti(void);

#endif
// gozlem.c — sayilar yalniz bu birimde tutulur
#include <stdio.h>
#include "gozlem.h"

static const char *const adlar[] = { "kopya", "ortak", "aktarim", "gozlenmeyen" };
static int sayilar[4];
static int ulasan = 0;
static int ulasmayan = 0;

void gozlem(const char *degisiklik, enum Sinif sinif, int gordu) {
    int beklenen = (sinif == ORTAK || sinif == AKTARIM);
    sayilar[sinif]++;
    if (gordu) {
        ulasan++;
    } else {
        ulasmayan++;
    }
    printf("%-34s %-8s %s%s\n", degisiklik, gordu ? "gordu" : "gormedi",
           adlar[sinif], gordu == beklenen ? "" : "  TUTARSIZ");
}

void gozlem_ozeti(void) {
    printf("siniflar: kopya %d, ortak %d, aktarim %d, gozlenmeyen %d\n",
           sayilar[KOPYA], sayilar[ORTAK], sayilar[AKTARIM], sayilar[GOZLENMEYEN]);
    printf("ulasan %d, ulasmayan %d\n", ulasan, ulasmayan);
}

Aşağıdaki kesit, kursun çekirdeğinden alınıyor ve bu dersin kendi kesitidir — adları ve gövdesi kurs boyunca değişmez:

// eniyileyici_olcumu.c — gorur.c'deki eniyileme kesiti, degistirilmeden
#include <stdio.h>
#include "gozlem.h"

struct Adim {
    char islem;
    int deger;
};
static void eniyileme(void) {
    struct Adim akis[] = { { 'y', 1 }, { 'y', 2 }, { 'o', 0 } };
    int okuyan = 0;
    int son_yazma = -1;
    for (int i = 0; i < 3; i++) {
        if (akis[i].islem == 'y') {
            son_yazma = i;
        } else if (son_yazma == 0) {
            okuyan++;
        }
    }
    gozlem("okunmadan ezilen yazma", GOZLENMEYEN, okuyan > 0);
}

int main(void) {
    eniyileme();
    gozlem_ozeti();
    return 0;
}
okunmadan ezilen yazma             gormedi  gozlenmeyen
siniflar: kopya 0, ortak 0, aktarim 0, gozlenmeyen 1
ulasan 0, ulasmayan 1

akis dizisi üç adımı taşıyor: y bir yazma, o bir okuma. Döngü, her okuma adımında en son hangi yazmanın etkili olduğunu (son_yazma) yokluyor. Okuma geldiğinde son_yazma hâlâ sıfırıncı yazmayı gösteriyorsa (son_yazma == 0), o okuma birinci yazmanın değerini görmüş sayılır ve okuyan artıyor. Bu modelde akis sırasıyla y=1, y=2, o içeriyor: ikinci adım birinciyi son_yazmayı 1’e taşıyarak ezmiş oluyor; üçüncü adımdaki okuma geldiğinde son_yazma artık 0 değil, 1. Birinci yazmayı gören hiçbir okuma yok — okuyan sıfırda kalıyor. gozlem çağrısı bunu GOZLENMEYEN sınıfıyla ve gordu alanına okuyan > 0 (yani 0, yanlış) vererek kaydediyor.

Bu, bir eniyileyicinin okunmayan bir yazmayı neden silebileceğinin modelidir: birinci yazma hiç yapılmasa da son_yazma dizisi ve dolayısıyla okuyan sayısı aynı kalır. Program iki yazma yerine tek yazma içerseydi — doğrudan 2 yazsaydı — gözlenebilir sonuç (üçüncü adımın ne okuduğu) değişmezdi. Model bu yüzden okunmadan ezilen yazmayı gözlenmeyen sınıfına koyuyor: değişiklik yapılmış, ama hiçbir okuyucuya ulaşmamış.

As-if Kuralı

Bir eniyileyicinin bir yazmayı silme hakkı keyfi değildir; tek bir koşula bağlıdır: as-if kuralı (“sanki kuralı”). Bu kural, bir programın çalışma zamanı davranışı gözlenebilir davranış kümesi üzerinden tanımlanır ve eniyileyici bu kümeyi koruduğu sürece programın adım adım nasıl çalıştığını istediği gibi değiştirebilir — sanki kaynaktaki sırayla çalışıyormuş gibi görünmesi yeterlidir, gerçekten öyle çalışması gerekmez. Gözlenebilir davranış üç şeyle sınırlıdır: girdi/çıktı işlemlerinin sırası ve içeriği, volatile işaretli nesnelere yapılan erişimler ve programın sonlanma durumu. Yukarıdaki modelde birinci yazma bunların hiçbirine girmiyor: bir dosyaya yazılmıyor, volatile değil, programın sonlanmasını etkilemiyor. Bu yüzden silinmesi as-if kuralını ihlal etmez.

PA11. Bir yazma ancak gözlenebilir davranışın hiçbir parçasına girmiyorsa okunmadan ezilebilir; girdiği an bu hak düşer.

As-if kuralı çekirdeğin diğer on dört ölçümüyle çelişmiyor, onları tamamlıyor. fork sonrası çocuğun değiştirdiği değişken (kopya sınıfı) as-if kuralına konu değildir — o, iki ayrı belleğin varlığıyla ilgilidir, tek bir sürecin içindeki yazma sırasıyla değil. İşleyicinin kaldırdığı bayrak (aktarım sınıfı) ise tam tersi yönden as-if kuralına giriyor: volatile sig_atomic_t işaretli olduğu için eniyileyici o yazmaya asla dokunamaz — bir sonraki bölüm bunu ayrıntılandırıyor.

Volatile’ın Verdiği Garanti

volatile anahtar sözcüğü bir değişkene uygulandığında, derleyiciye o değişkene yapılan her okuma ve yazmanın gözlenebilir davranışın bir parçası sayılmasını söyler. Bunun sonucu doğrudandır: volatile işaretli bir yazma, ardından hiçbir okuma gelmese bile silinemez; volatile işaretli bir okuma, sonucu hiçbir yerde kullanılmasa bile atlanamaz. Sinyaller dersindeki bayrak değişkeni tam olarak bu yüzden volatile sig_atomic_t olarak tanımlanmıştı: bir işleyicinin kaldırdığı bayrağı ana akış okumadan önce eniyileyici o yazmayı “kimse okumuyor” diye silebilseydi, aktarım hiç gerçekleşmezdi.

Modelin diliyle söylenirse: akis dizisindeki konum volatile işaretli olsaydı, birinci yazma okunmadan ezilse de silinemezdi, çünkü yazmanın kendisi gözlenebilir davranışın parçası sayılırdı. Bu, kaydın sınıfını değiştirmez — ikinci yazma geldiğinde birinci yazmanın değerini yine hiçbir okuma görmemiştir ve kayıt yine gözlenmeyendir. Değişen tek şey eniyileyicinin hakkıdır: yazma programda kalır, yalnız okuyucuya ulaşmaz. Ders bunu ayrı bir kodla ölçmüyor; volatile’ın etkisi eniyileyicinin ürettiği kodda görünür ve kaynak metindeki bir model o kodu basamaz. Bu dersin main işlevi eniyileme’yi ve ardından gozlem_ozeti’yi çağırıyor; çıktı, gormedi ile işaretlenmiş tek kaydın gözlenmeyen sınıfına düştüğünü gösteriyor.

volatile’ın verdiği garanti dardır ve genişletilmemelidir. Tek başına iş parçacıkları arasında sıralama sağlamaz, atomiklik sağlamaz, bellek bariyeri kurmaz — POSIX İş Parçacıkları dersindeki kilit ve birleştirme (join) hâlâ gereklidir. volatile’ın verdiği tek şey, o nesneye yapılan erişimlerin eniyileyici tarafından silinmeyeceği ve sırasının korunacağı garantisidir; bu, bir sinyal işleyicisi ile ana akış arasındaki tek paylaşım biçiminin neden volatile sig_atomic_t olduğunu açıklar.

Sınırlayıcı Ölçüm

Model, bir yazmanın silinip silinmeyeceğine tek bir kurala — gözlenebilir davranışın korunup korunmadığına — bakarak karar veriyor. Gerçek bir eniyileyici bu kararı çok daha geniş bir bilgiyle verir: bir değişkenin adresi alınmış mı, başka bir çeviri biriminden görünür mü, bir işaretçi üzerinden takma adlanmış olabilir mi. Bu ders eniyileme düzeyini bir bayrakla yazmıyor, derleyici çıktısını, üretilen kodu ya da bir ölçümün süresini basmıyor — çünkü bunların hiçbiri kaynak metindeki bir modelle gösterilemez; gerçek bir aracın gerçek kararıdır. Model yalnız kuralın kendisini gösteriyor: hangi yazmaların silinebileceğini, hangilerinin silinemeyeceğini. Bir yazmanın adresi başka bir işlevce tutuluyorsa, o işlev geri çağrıldığında konumu okuyabilir; bu durumda yazma artık gözlenebilir davranışın bir parçasıdır ve modeldeki gibi sessizce silinemez. Modelin bunu göstermemesi bir eksiklik değil, sınırdır: kesit yalnız tek bir akış içindeki ardışık yazmaları ele alıyor, çapraz işlev görünürlüğünü değil.

Özet

  • okunmadan ezilen yazma (model): bir konuma art arda yapılan iki yazmadan birincisi hiç okunmadan ikincisi geliyorsa, birincinin izi kalmıyor.
  • As-if kuralı, eniyileyicinin bir yazmayı ancak gözlenebilir davranışı değiştirmeyecekse silebileceğini söylüyor; gözlenebilir davranış girdi/çıktı, volatile erişimleri ve sonlanma durumuyla sınırlı.
  • volatile, bir nesneye yapılan her erişimi gözlenebilir davranışın parçası sayar; bu yüzden volatile işaretli bir yazma hiç okunmasa bile silinmez.
  • Sinyaller dersindeki volatile sig_atomic_t bu garantiyi kullanıyordu: bir işleyicinin kaldırdığı bayrak, ana akış onu henüz okumamış olsa da eniyileyici tarafından silinemez.
  • Bu dersin ölçtüğü sınıf gözlenmeyen: yazma yapılıyor ama hiçbir okuyucuya ulaşmıyor — ne yazanın kendi tarafında kalıyor (kopya), ne bir aracıdan geçip bir kez görünüyor (aktarım); ezilip kayboluyor.
  • Model gerçek bir eniyileyicinin kararını değil, kararın dayandığı kuralı gösteriyor; adres alma ve çapraz işlev görünürlüğü modelin kapsamı dışında.

Kurs Kapanışı

Kurs on beş ders boyunca tek bir soruyu sordu: bu değişikliği kim görüyor? Bu ders o soruyu son kez yanıtlıyor ve yanıt geri kalan on dördünden farklı bir yoldan gözlenmeyene varıyor — yazma var, okuyucu yok, çünkü bir sonraki yazma araya girmeden okuma hiç gelmedi. Aşağıdaki tablo on beş değişikliğin tümünü topluyor:

Ders Ölçülen değişiklik Kim görüyor Sınırlayıcı ölçüm
Dosya Tanıtıcıları dup ile çoğaltılan tanıtıcının paylaştığı konum ortak Tanıtıcı numarası neyin paylaşıldığını söylemez; paylaşım yalnız konum davranışıyla ölçülür
Süreç Yönetimi fork sonrası çocuğun değiştirdiği değişken kopya Ölçüm zamanlamaya bağlı değildir; bir yarışı değil, iki kopyanın bağımsız olduğunu ölçer
Sinyaller İşleyicinin kaldırdığı bayrak aktarım Engellenmişken tekrar gelen aynı sinyal açılışta yalnız bir kez teslim edilir
Süreçler Arası İletişim Boruya yazılan değer aktarım Boru bir akıştır, ileti sınırı taşımaz; sınır ayrı bir uzunluk önekiyle kurulur
POSIX İş Parçacıkları İş parçacığının kilit altında yazdığı sayaç ortak Yalnız son değer doğrulanır; ara durumlar ve yazma sırası ölçülmez
Bağlı Yapılar Takma adla değişen düğüm ortak Bir işaretçinin paylaşımı kaynakta görünmez; iki başın aynı düğümü gösterip göstermediği eşitlikle ölçülür
Halka Arabellek ve Kuyruklar Halkaya konan öge aktarım Okuma işlevinin imzası değerin yapıda kalıp kalmadığını söylemez; bu, art arda iki okumayla ölçülür
Karma Tabloları Eklendikten sonra değişen anahtar kopya Ekleme işlevinin imzası anahtarın kopyalanıp kopyalanmadığını söylemez; anahtar işaretçiyle tutulsaydı sınıf ortak olurdu
Nesne Benzeri Tasarım Bir kullanıcının yaptığı yatırma ortak Arayüzün biçimi (opak tip, işlev işaretçileri, düz işlevler) paylaşımı belirlemez; yalnız işaretçi eşitliği belirler
Kaynak Temizleme Deyimleri Boşaltılmadan çıkılan tampon gözlenmeyen Gözlenmeyen, hiçbir yere ulaşmamak demektir; bu, değer basılmadan == NULL denetimleriyle doğrulanır
Derleme Sistemleri Bağımlılığı yazılmamış başlık kopya Model yalnız yazılmış bağımlılığı görür; gerçekte hangi başlığın okunduğunu kendi başına bilmez
Kütüphane Üretimi Statik bağlı kütüphane güncellemesi kopya Model yalnız hangi değere ulaşıldığını ölçer; arayüz değişirse işaretçiye dayalı güvence kaybolur ve bu durum koşturulmaz
Test Çerçeveleri Yalıtılmış testin değişikliği kopya Süreç yalıtımı bellek değişikliğini keser, dosya sistemi değişikliğini kesmez
Hata Ayıklayıcı Kullanımı İki durak arasındaki ara değer gözlenmeyen Kesme ve gözetleme noktası çalışan bir süreci gözler, çekirdek dökümü durmuş olanı okur; döküm üretilmez
Optimizasyon Düzeyleri Okunmadan ezilen yazma gözlenmeyen Model yalnız tek akış içindeki ardışık yazmaları ele alır; adres alma kapsam dışı

5 kopya · 4 ortak · 3 aktarım · 3 gözlenmeyen.

Bu dağılım, çekirdeğin on beş gözleminin dağılımıyla aynıdır — beklenmedik bir uyuşma değil, çünkü çekirdek her ders için tam olarak bir gözlem taşıyor ve her ders kendi kesitini o gözlemden alıyor. Yedi değişiklik öteki tarafa ulaştı, sekizi ulaşmadı; ulaşanlar bir aracıdan (aktarım) ya da paylaşılan bir nesneden (ortak) geçti, ulaşmayanlar ya yazanın kendi tarafında kaldı (kopya) ya da hiçbir yerde durmadı (gözlenmeyen). Bu ayrım kursun başından beri aynı kaldı: bir değişikliğin yapılmış olması, onun görülmüş olmasını garanti etmez.

Kurs boyunca aynı soru farklı düzeylerde tekrar sorulmuş oldu — iki sürecin belleği arasında, bir işleyici ile ana akış arasında, iki iş parçacığının paylaştığı bellekte, bir derleme biriminin başka birine bıraktığı izde ve son olarak bir eniyileyicinin sildiği tek bir yazmada. Her düzeyde yanıt aynı dört sınıftan birine düştü; sınıfların sayısı değişmedi, yalnız hangi düzeneğin hangi sınıfa girdiği değişti.

Bir sonraki kurs (C++ Temelleri ve Nesne Tasarımı) sınıflar ve kaynak yönetimi konusunu ele alıyor; kendi ölçü eksenini kendi ilk dersinde kuracak.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ ↵ Esc gezin · aç · kapat