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ı,
volatileerişimleri ve sonlanma durumuyla sınırlı. volatile, bir nesneye yapılan her erişimi gözlenebilir davranışın parçası sayar; bu yüzdenvolatileişaretli bir yazma hiç okunmasa bile silinmez.- Sinyaller dersindeki
volatile sig_atomic_tbu 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.