Ders 12 / 12
Paket Yönetimi ve Derleme Araçları
Bağımlılık tablosu, sürüm sabitleme ve kilit dosyasıyla yeniden üretilebilir kurulum; yalnız başlıktan oluşan şablon kütüphanesinin her birimi yeniden derletmesi.
Yayın Güncelleme
Bu derste
- Bir başlık değiştiğinde hangi çeviri birimlerinin yeniden derlendiğini bağımlılık tablosundan okur.
- Sürüm aralığı ile kilit dosyasının aynı kurulumu nasıl yeniden üretilebilir kıldığını açıklar.
- Yalnız başlıktan oluşan bir şablon kütüphanesinin derleme bedelini derlenmiş bir kütüphaneyle karşılaştırır.
İçindekiler
Önceki ders, bir başlığın görünürlüğünü azaltmanın bedelini nesne başına ayırmaya taşıdığını gösterdi, ama kazanılan derleme süresini tahminle anlattı. Bu ders o tahmini bir sayıya çeviriyor: değişen bir başlık kaç birimi yeniden derletiyor? Aynı soru bir projenin dışarıdan getirdiği kütüphaneler için de sorulur; çünkü bir kütüphane de sonunda birkaç başlıktır. Süre basılmaz. Ölçülen sayı, bir değişikliğin yeniden derlettiği çeviri birimi sayısıdır. M10/K03’ten bir ayrım: sayı ölçünün kendisi değil, kanıt aracıdır. Burada da sayı sınıflandırmanın kanıtıdır; ölçü sorusunun yanıtı bir sınıf adıdır ve bu dersin yanıtı derlemedir.
Bağımlılık Tablosu
Bir derleme sistemi işini tek bir tabloya dayandırır: her birim hangi başlıkları içeriyor? Bir dosya değişince, o dosyayı içeren birimler eskimiş sayılır; ötekilere dokunulmaz. Sistemin adı ve yapılandırma dili bu dersin konusu değil, tablonun kendisi konusu. Tabloyu sade C++ ile kurabiliriz: birim adı ve içerdiği başlıkların listesi.
// bagimlilik.hpp — birim ve icerdigi basliklar tablosu
#pragma once
#include <string>
#include <vector>
struct Birim {
std::string ad;
std::vector<std::string> basliklar;
};
inline int yeniden(const std::vector<Birim>& birimler, const std::string& degisen) {
int sayi = 0;
for (const auto& b : birimler) {
if (b.ad == degisen) {
++sayi;
continue;
}
for (const auto& h : b.basliklar) {
if (h == degisen) {
++sayi;
break;
}
}
}
return sayi;
}
yeniden her birime bakar. Birimin kendi kaynağı değiştiyse onu sayar; değilse başlık
listesinde değişen dosyayı arar ve ilk eşleşmede durur. Aynı başlık bir birimde iki kez
geçse bile birim bir kez sayılır, çünkü bir birim bir kez derlenir. Dolaylı içermeler de
tabloya yazıldığı sürece bu sayıma girer; bir başlığın getirdiği başlık, listeye yazılmış
başlıktan farksızdır.
Sürüm Sabitleme ve Kilit Dosyası
Dışarıdan gelen bir kütüphaneyi bir paket yöneticisi getirir. Projenin ona bağımlılığı bir sürüm aralığıyla yazılır: “1.0.0 dahil, 2.0.0 hariç”. Aralık bir söz verir, tek bir sürümü göstermez. Kayıt yeni bir sürüm yayımladığında aynı aralık başka bir sürüme çözülür. Sürüm sabitleme bu kaymayı keser: çözülen sürüm bir kilit dosyasına yazılır ve sonraki kurulumlar aralığı yeniden çözmek yerine kilitteki sürümü kullanır.
Bir ayrım gerekiyor. Kilit dosyası, bu kursun eşzamanlılık bölümündeki dışlama kilidiyle yalnızca adı paylaşır. Dışlama kilidi çalışan iş parçacıklarını sıraya sokar; kilit dosyası ise diskte duran bir kayıttır ve hiçbir şeyi engellemez, yalnızca hangi sürümün kurulduğunu hatırlar.
// surum.hpp — surum araligi ve kilit dosyasi modeli
#pragma once
#include <map>
#include <string>
#include <vector>
// Surum 130, 1.3.0 olarak kodlanir; kayit yayimlanmis surumlerin listesidir.
inline int coz(const std::vector<int>& kayit, int alt, int ust) {
int secilen = -1;
for (int s : kayit) {
if (s >= alt && s < ust && s > secilen) secilen = s;
}
return secilen;
}
using KilitDosyasi = std::map<std::string, int>;
inline int kur(const KilitDosyasi& kilit, const std::string& paket,
const std::vector<int>& kayit, int alt, int ust) {
auto it = kilit.find(paket);
if (it != kilit.end()) return it->second;
return coz(kayit, alt, ust);
}
coz aralıktaki en yüksek sürümü seçer. kur önce kilide bakar; paket kilitteyse
aralığı hiç çözmez. Kilit boşsa kurulum aralığa güvenir ve sonucu kaydın o anki haline
bağlar. Yeniden üretilebilir kurulum bu farka dayanır: kilitteki sürüm kayıtta durduğu
sürece, aynı kilit dosyası kayıt değişse de aynı sürümü verir.
Yalnız Başlıktan Oluşan Kütüphane
Bir şablon kütüphanesi çoğu zaman yalnız başlıktan oluşan kütüphanedir (header-only library). Sebebi kursun ilk dersinde görüldü: derleyici şablonu her kullanım yerinde tipi yerine koyarak üretir, bu yüzden gövde kullanan her birimde görünür olmak zorundadır. Her tip için ayrı gövde üretilir ve bu üretim, o başlığı içeren her birimin derleme süresine yazılır. Gövde başlıkta durduğu için kütüphanenin içindeki bir değişiklik, başlığı içeren birimlerin hepsini eskitir.
Derlenmiş bir kütüphane başka bir düzen kurar. Başlık yalnızca arayüzü bildirir; gövde
tek bir .cpp dosyasında durur. Gövdedeki değişiklik tek birimi yeniden derletir.
Ancak bu düzen, önceki dersin açık somutlaştırmasındaki gibi yalnızca önceden seçilmiş
tipler için çalışır. Her tipe açık bir kütüphane, şablon olmaktan çıkar.
// olcum.cpp — degisen baslik kac birimi yeniden derletiyor
#include <iostream>
#include "bagimlilik.hpp"
#include "surum.hpp"
int main() {
std::vector<Birim> birimler = {
{"giris.cpp", {"ortak.hpp"}},
{"hesap.cpp", {"ortak.hpp"}},
{"rapor.cpp", {"ortak.hpp", "ozel.hpp"}},
};
std::cout << "degisen baslik derleme ortak=" << yeniden(birimler, "ortak.hpp")
<< " ozel=" << yeniden(birimler, "ozel.hpp") << "\n";
std::vector<int> kayit = {100, 110, 130};
KilitDosyasi kilit;
int ilk = kur(kilit, "sayac", kayit, 100, 200);
kilit["sayac"] = ilk;
kayit.push_back(140);
kayit.push_back(210);
int kilitsiz = kur(KilitDosyasi{}, "sayac", kayit, 100, 200);
int kilitli = kur(kilit, "sayac", kayit, 100, 200);
std::cout << "surum sabitleme ilk=" << ilk << " kilitsiz=" << kilitsiz
<< " kilitli=" << kilitli << "\n";
std::vector<Birim> yalniz = {
{"giris.cpp", {"ortak.hpp", "kutuphane.hpp"}},
{"hesap.cpp", {"ortak.hpp", "kutuphane.hpp"}},
{"rapor.cpp", {"ortak.hpp", "ozel.hpp", "kutuphane.hpp"}},
{"cizim.cpp", {"kutuphane.hpp"}},
};
std::vector<Birim> derlenmis = {
{"giris.cpp", {"ortak.hpp", "arayuz.hpp"}},
{"hesap.cpp", {"ortak.hpp", "arayuz.hpp"}},
{"rapor.cpp", {"ortak.hpp", "ozel.hpp", "arayuz.hpp"}},
{"cizim.cpp", {"arayuz.hpp"}},
{"kutuphane.cpp", {"arayuz.hpp"}},
};
std::cout << "yalniz baslik govde=" << yeniden(yalniz, "kutuphane.hpp")
<< " derlenmis govde=" << yeniden(derlenmis, "kutuphane.cpp")
<< " arayuz=" << yeniden(derlenmis, "arayuz.hpp") << "\n";
return 0;
}
degisen baslik derleme ortak=3 ozel=1 surum sabitleme ilk=130 kilitsiz=140 kilitli=130 yalniz baslik govde=4 derlenmis govde=1 arayuz=5
EB30. İlk satır bağımlılık tablosunun sonucudur: ortak.hpp değişince üç birimin üçü
de yeniden derlenir (ortak=3), ozel.hpp değişince yalnızca rapor.cpp (ozel=1).
Aynı kod tabanında iki başlığın bedeli üçe bir farkla ayrışır; fark başlığın büyüklüğünden
değil, kaç birimin onu içerdiğinden gelir.
EB31. İkinci satırda ilk=130: kayıt henüz 100, 110 ve 130 sürümlerini taşırken
aralık 130’a çözüldü ve sonuç kilide yazıldı. Kayda 140 ve aralığın dışında kalan
210 eklendi. Kilitsiz kurulum 140’ı seçti (kilitsiz=140), çünkü 210 aralıkta
değil; kilitli kurulum hâlâ 130 verdi (kilitli=130).
EB32. Yukarıdaki iki kurulum aynı projenin iki koşumudur ve arada tek fark kaydın
değişmesidir. Kilitli olan değişmedi, kilitsiz olan kaydı. Bu çıktı sürüm numarasının
kendisini değil, aralığın kaymasını gösterir; 130 ve 140 yalnızca modelin
kodlamasıdır.
EB33. Üçüncü satırda yalnız başlıktan oluşan kütüphanenin gövdesi değişince dört
birimin dördü yeniden derlenir (govde=4). Derlenmiş kütüphanede aynı gövde değişikliği
tek birimi, kutuphane.cpp’yi eskitir (govde=1).
EB34. arayuz=5: derlenmiş kütüphanenin yalnızca bildirimleri taşıyan başlığı
değişirse, onu içeren dört birimin yanında kutuphane.cpp de yeniden derlenir. Derlenmiş
düzen gövde değişikliğini tek birime indirir, arayüz değişikliğini indirmez.
EB35. Dört ve beş birimlik tablolar kurulumdan çıkar, ölçülen bir derleme değildir. Sayı, tabloya yazdığımız içermelere bağlıdır; gerçek bir projede dolaylı içermeler eksik yazılırsa sayı olduğundan küçük çıkar. Model bu yüzden bir sayım örneğidir, derleme süresi hakkında bir şey söylemez.
Sınırlayıcı Ölçüm
Bu dersin ölçtüğü düzenek, değişen bir başlığın yeniden derlettiği birim sayısıdır. Sınır,
yalnız başlıktan oluşan şablon kütüphanesindedir. Kütüphane bir şablon taşıdığında gövdesi
başlıkta durmak zorundadır ve onu içeren her birim gövdeyi kendi içinde yeniden
üretir. Kütüphanenin gövdesinde yapılan en küçük değişiklik de bu yüzden dört birimin
dördünü eskitir: govde=4. Derlenmiş kütüphanede aynı değişiklik govde=1 verir.
Yani şablonun bedeli, kursun başında görülen gövdenin her birimde görünmesi, derlemeye geri döner. Şablon kullanıcıya her tip için çalışma özgürlüğü verir; karşılığında gövde başlıkta kalır ve her değişiklik içeren birimlerin sayısıyla çarpılır. Her kütüphaneyi derlenmiş hâle getirmek genel bir çözüm değildir, çünkü derlenmiş düzen şablonun tipten bağımsızlığını yalnızca açık somutlaştırılan tiplerle sınırlar. Seçim, bir ödünleşimdir: gövde sık değişiyor ve başlık çok birime görünüyorsa derlenmiş düzen kazandırır; gövde nadiren değişiyorsa yalnız başlıktan oluşan düzenin sadeliği yeter.
Sayının yeri de önemlidir. govde=4 bir ölçünün yanıtı değildir; yanıt, düzeneğin
derleme sınıfına girdiğidir. Sayı bu sınıflandırmanın kanıtıdır: bedel çalışan
programda değil, programı kurmadan önce ödeniyor. Bu dersin düzenekleri koşum, bellek ya
da bekleme sınıfında satır doğurmaz; hepsi derleme sınıfındadır.
Özet
- Bu dersin ölçtüğü düzenek bedeli derleme sınıfına taşır: değişen bir başlık, onu içeren her birimi yeniden derletir.
- Bağımlılık tablosu
ortak=3 ozel=1verdi: aynı projede iki başlığın bedeli, kaç birim tarafından içerildiğine göre ayrışır. - Sürüm aralığı bir söz verir, kilit dosyası bir sürüm hatırlar:
kilitsiz=140ikenkilitli=130, yani yeniden üretilebilir kurulum kilide bağlıdır. Kilit dosyası dışlama kilidinden ayrıdır. - Yalnız başlıktan oluşan kütüphane gövde değişince
govde=4birimi eskitir; derlenmiş kütüphanegovde=1eskitir, ama arayüz değişincearayuz=5. - Şablonun bedeli, gövdenin her birimde görünmesi, derlemeye geri döner; derlenmiş düzen bunu ancak önceden seçilmiş tiplerle azaltabilir.
Kurs Kapanışı
Bu kurs boyunca on iki düzeneği aynı soruyla ölçtük: bedel nereye ödeniyor? Aşağıdaki tablo her dersin ölçtüğü düzeneği, bedelin gittiği yeri ve o dersin sınırlayıcı ölçümünü özetliyor.
| Ders | Ölçülen düzenek | Bedel nereye | Sınırlayıcı ölçüm |
|---|---|---|---|
| Fonksiyon ve Sınıf Şablonları | Şablon somutlaştırma | derleme | const int argümanı yeni gövde doğurmaz |
| Şablon Özelleştirme | bool özelleştirmesi |
bellek | Okuma başına iki adım: bedel koşuma döner |
| Değişken Sayıda Şablon Parametresi | Özyinelemeli paket açma | derleme | Yeni tip listesi yeni gövde doğurur |
| Tip Özellikleri | if constexpr ile seçim |
derleme | volatile argümanla hesap koşuma geçer: adim=10 |
| Aşırı Yükleme Çözümlemesi ve SFINAE | void_t ile algılama |
derleme | Gövdedeki hata elenmez, derlemeyi durdurur |
| Statik Çok Biçimlilik | CRTP ile gönderim | derleme | Karışık koleksiyon std::variant ister |
| İş Parçacıkları ve Kilitler | Kilitli sayaç | bekleme | Yerel toplam kilit alımını 4’e indirir |
| Bellek Modeli | release/acquire yayını |
bekleme | Her eleman atomik olursa eşgüdüm 1000 olur |
| Önbellek Dostu Veri Düzeni | Yalnız x alanı |
bellek | Üç alan birlikte okununca tablo tersine döner: aos=0/11 |
| Profilleme ve Karşılaştırmalı Ölçüm | Araçlı profil | koşum | Tembel tabloda ilk çağrı 110, ikinci 10 adım |
| Derleme Süresi Yönetimi | Gizleme deyimi | koşum | Taşıma 0, kopya 1 ayırma |
| Paket Yönetimi ve Derleme Araçları | Değişen başlık | derleme | Yalnız başlıktan oluşan kütüphane her birimi eskitir |
6 derleme · 2 koşum · 2 bellek · 2 bekleme; derlemede ödenen 6, koşumda ödenen 6. Bu dağılım kursun başında kurulan çekirdeğin dağılımıyla birebir aynıdır: altı düzenek bedeli derlemeye yazar, altısı programı çalıştırırken tahsil eder. Gövdenin her birimde görünmesi, kursun ilk dersinde şablonun bedeliydi; bu derste aynı bedel değişen başlık başına birim sayısı olarak geri geldi.
Bu ders M11’in son dersidir; M11 burada kapanır.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.