Linux makinemde yerel yürütülebilir dosya oluşturan bir merhaba dünya Go programı hazırladım. Ama basit Hello world Go programının boyutunu görünce şaşırdım, 1.9MB idi!
Go'da bu kadar basit bir programın çalıştırılabilirliği neden bu kadar büyük?
Linux makinemde yerel yürütülebilir dosya oluşturan bir merhaba dünya Go programı hazırladım. Ama basit Hello world Go programının boyutunu görünce şaşırdım, 1.9MB idi!
Go'da bu kadar basit bir programın çalıştırılabilirliği neden bu kadar büyük?
dotnet publish -r win-x64 -p:publishsinglefile=true -p:publishreadytorun=true -p:publishtrimmed=true
yaklaşık 26MB civarında bir ikili dosya oluşturur!
Yanıtlar:
Tam olarak bu soru resmi SSS'de yer almaktadır: Benim önemsiz programım neden bu kadar büyük bir ikili dosyadır?
Cevabın alıntılanması:
Gc araç zinciri (bağlayıcıların
5l
,6l
ve8l
) statik bağlama yapmak. Bu nedenle tüm Go ikili dosyaları, dinamik tür kontrollerini, yansımayı ve hatta panik zamanı yığın izlerini desteklemek için gerekli çalışma zamanı tür bilgileriyle birlikte Go çalışma zamanını içerir.Linux'ta gcc kullanılarak statik olarak derlenen ve bağlanan basit bir C "merhaba, dünya" programı
printf
,. Eşdeğer bir Go programı kullananfmt.Printf
yaklaşık 1,9 MB'dir, ancak bu daha güçlü çalışma zamanı desteği ve tür bilgileri içerir.
Dolayısıyla, Hello World'ünüzün yerel yürütülebilir dosyası 1,9 MB'dir, çünkü çöp toplama, yansıtma ve diğer birçok özelliği (programınızın gerçekten kullanmayabileceği, ancak orada bulunan) sağlayan bir çalışma zamanı içerir. Ve metni fmt
yazdırmak için kullandığınız paketin uygulaması "Hello World"
(artı bağımlılıkları).
Şimdi şunu deneyin: fmt.Println("Hello World! Again")
programınıza başka bir satır ekleyin ve yeniden derleyin. Sonuç 2x 1.9MB değil, yine de sadece 1.9 MB olacak! Evet, çünkü kullanılan tüm kitaplıklar ( fmt
ve bağımlılıkları) ve çalışma zamanı zaten çalıştırılabilir dosyaya eklenmiştir (ve bu nedenle, eklediğiniz 2. metni yazdırmak için yalnızca birkaç bayt daha eklenecektir).
Aşağıdaki programı düşünün:
package main
import "fmt"
func main() {
fmt.Println("Hello World!")
}
Bunu Linux AMD64 makinemde (Go 1.9) şöyle derlersem:
$ go build
$ ls -la helloworld
-rwxr-xr-x 1 janf group 2029206 Sep 11 16:58 helloworld
Yaklaşık 2 Mb boyutunda bir ikili dosya alıyorum.
Bunun nedeni (diğer cevaplarda açıklanmıştır) oldukça büyük olan "fmt" paketini kullanmamızdır, ancak ikili de çıkarılmamıştır ve bu, sembol tablosunun hala orada olduğu anlamına gelir. Bunun yerine derleyiciye ikiliyi çıkarması talimatını verirsek, çok daha küçük olacaktır:
$ go build -ldflags "-s -w"
$ ls -la helloworld
-rwxr-xr-x 1 janf group 1323616 Sep 11 17:01 helloworld
Ancak, programı fmt.Println yerine yazdırma yerleşik işlevini kullanacak şekilde yeniden yazarsak, şöyle:
package main
func main() {
print("Hello World!\n")
}
Ve sonra derleyin:
$ go build -ldflags "-s -w"
$ ls -la helloworld
-rwxr-xr-x 1 janf group 714176 Sep 11 17:06 helloworld
Daha da küçük bir ikili ile sonuçlanırız. Bu, UPX paketleme gibi hilelere başvurmadan elde edebileceğimiz kadar küçüktür, bu nedenle Go çalışma zamanının ek yükü kabaca 700 Kb'dir.
Golang / go projesindeki ikili boyut sorununun 6853 numaralı sayı ile izlendiğini unutmayın .
Örneğin, a26c01a (Go 1.4 için) işle, merhaba dünyayı 70 KB azaltın :
çünkü bu isimleri sembol tablosuna yazmıyoruz.
1.5 için derleyici, derleyici, bağlayıcı ve çalışma zamanının tamamen Go'da olacağı düşünüldüğünde, daha fazla optimizasyon bekleyebilirsiniz.
2016 Go 1.7 Güncellemesi: bu optimize edilmiştir: bkz. " Smaller Go 1.7 ikili dosyaları ".
Ancak bu gün (Nisan 2019) en çok yer alan şey runtime.pclntab
.
Bkz: " Go büyük? Boyut görselleştirme D3 kullanarak yürütülebilir dosyalar yüzden neden Git çalıştırılabilir dosyalardır gelen" Raphael 'Kena' Poss .
Çok iyi belgelenmemiş ancak Go kaynak kodundan gelen bu yorum amacını gösteriyor:
// A LineTable is a data structure mapping program counters to line numbers.
Bu veri yapısının amacı, Go çalışma zamanı sisteminin bir çökme durumunda veya
runtime.GetStack
API aracılığıyla dahili isteklerde açıklayıcı yığın izleri üretmesini sağlamaktır .Bu yüzden faydalı görünüyor. Ama neden bu kadar büyük?
Yukarıda bağlantısı verilen kaynak dosyada gizlenen URL https://golang.org/s/go12symtab Go 1.0 ve 1.2 arasında ne olduğunu açıklayan bir belgeye yönlendirir. Kelimeleri ifade etmek:
1.2'den önce, Go bağlayıcı sıkıştırılmış bir çizgi tablosu yayıyordu ve program, çalıştırma zamanında başlatmanın ardından onu açardı.
Go 1.2'de, çalıştırılabilir dosyadaki satır tablosunu, ek bir açma adımı olmadan çalışma zamanında doğrudan kullanıma uygun nihai biçimine önceden genişletme kararı verildi.
Başka bir deyişle, Go ekibi, başlatma süresinden tasarruf etmek için yürütülebilir dosyaları daha büyük hale getirmeye karar verdi.
Ayrıca, veri yapısına bakıldığında, derlenmiş ikili dosyalardaki toplam boyutunun, her bir işlevin ne kadar büyük olduğuna ek olarak, programdaki işlev sayısında süper doğrusal olduğu görülmektedir.