Derlenmiş dosya boyutu nasıl azaltılır?


84

C'yi karşılaştıralım ve gidelim: Hello_world.c:

#include<stdio.h>
int main(){
    printf("Hello world!");
}

Hello_world.go:

package main
import "fmt"
func main(){
    fmt.Printf("Hello world!")
}

Her ikisini de derleyin:

$gcc Hello_world.c -o Hello_c 
$8g Hello_world.go -o Hello_go.8
$8l Hello_go.8 -o Hello_go

ve o ne?

$ls -ls
... 5,4K 2010-10-05 11:09 Hello_c
... 991K 2010-10-05 11:17 Hello_go

1Mb Hakkında Merhaba dünya. Benimle dalga mı geçiyorsun? Neyi yanlış yapıyorum?

(Hello_go -> 893K sadece şerit)


2
X86_64 Mac'te "Hello World" ikili dosyasının x64 Linux makinesinde olduğu gibi 1.3 MB olduğunu varsayıyorum. Aksine, ARM x32 ikili, x86_32 ikili dosyası kadar büyüktür. Boyut, ilgili mimarinin "kelime" uzunluğuna önemli ölçüde bağlıdır. X32 makinelerde, x64'te 32 bit, 64 bit genişliğindedir. Bu nedenle x32 "Merhaba Dünya" ikili dosyası yaklaşık% 30 daha küçüktür.
Alex

17
@Nick: GO'nun bir sistem dili olarak pazarlandığını düşünürsek, bunun adil bir soru olduğunu düşünüyorum. Sistemlerde çalışıyorum ve her zaman 4GB + RAM lüksüne ve büyük bir diske sahip değiliz.
Ed S.

3
893kb'lik bir yürütülebilir dosya "4GB + RAM ve büyük bir diskten" çok uzaktır ve diğerlerinin daha önce de belirttiği gibi, buna statik olarak bağlanmış çalışma zamanı dahildir ve bu kolayca hariç tutulabilir.
Nick Johnson

22
Evet, bu çok uzak bir çığlık ve cevabı biliyorum, ancak bu geçerli bir soru ve "hafıza tüketimini kimin umursadığı" yaklaşımı, önemli olmayan sistemler üzerinde çalışmaktan geliyor. Görünüşe göre cehalet tamam ve sadece soru sormamak daha iyi. Ve tekrar söylüyorum; bazen ~ 1MB çok fazla, belli ki o dünyada çalışmıyorsun. DÜZENLE - Google'da çalışıyorsun! lol. 'kimin umurunda' tutumu.
Ed S.

12
Belli ki Java bölümünde;)
Matt Joiner

Yanıtlar:


29

Dosyanın daha büyük olması bir sorun mu? Go'yu bilmiyorum ama C programı için geçerli olmayan bazı çalışma zamanı kitaplıklarını statik olarak bağladığını varsayıyorum. Ama muhtemelen programınız büyüdükçe endişelenecek bir şey yok.

Açıklandığı gibi burada , statik Git çalışma zamanını bağlayan varsayılan değerdir. Bu sayfa ayrıca dinamik bağlamanın nasıl ayarlanacağını da anlatır.


2
Bir yoktur açık konu Go1.5 için dönüm noktası seti ile,
Joe

79

Unix tabanlı bir sistem kullanıyorsanız (örneğin, Linux veya Mac OSX) yürütülebilir dosyada bulunan hata ayıklama bilgilerini -w bayrağıyla oluşturarak kaldırmayı deneyebilirsiniz:

go build -ldflags "-w" prog.go

Dosya boyutları önemli ölçüde azaltılır.

Daha fazla ayrıntı için GDB'nin sayfasını ziyaret edin: http://golang.org/doc/gdb


3
Aynısı stripkomutla da elde edildi : go build prog.go; strip progsonra sözde şeritli yürütülebilir dosya var: ELF 64-bit LSB yürütülebilir, x86-64, sürüm 1 (SYSV), dinamik olarak bağlantılı (paylaşılan kütüphaneleri kullanır), soyulmuş
Alexander I.Grafov

1
Bu, Windows'ta çalışır ve normal olarak oluşturarak ve çıkararak üretilene kıyasla biraz daha küçük bir ikili dosya sağlar.
Isxek

9
@Axel: Go ikili dosyalarının arındırılması açıkça desteklenmez ve bazı durumlarda ciddi hatalara neden olabilir. Buraya bakın .
Flimzy

9
Şu anda dokümantasyon listeleri -wyerine -s.
Aradaki

2
@Dynom: -w boyutu biraz küçültüyor gibi görünüyor ancak küçültme -s ile olduğu kadar iyi değil. -s şu anlama
arkod

42

2016 cevabı:

1. Go 1.7'yi kullanın

2. ile derleyin go build -ldflags "-s -w"

➜ ls -lh hello
-rwxr-xr-x 1 oneofone oneofone 976K May 26 20:49 hello*

3. Daha sonra kullanım upx, goupxartık 1.6 yana gereklidir.

➜ ls -lh hello
-rwxr-xr-x 1 oneofone oneofone 367K May 26 20:49 hello*

3
UPX ile ilgili olağan uyarıların muhtemelen burada geçerli olduğunu buraya eklemek isterim. Daha fazla bilgi için buna bakın: stackoverflow.com/questions/353634/… (çoğu Windows olmayan işletim sistemleri için de geçerlidir).
Jonas

23

Go ikili dosyaları büyüktür çünkü statik olarak bağlanırlar (cgo kullanan kitaplık bağlamaları dışında). Bir C programını statik olarak bağlamayı deneyin ve benzer bir boyutta büyüdüğünü göreceksiniz.

Bu sizin için gerçekten bir problemse (inanmakta zorlanıyorum), gccgo ile derleyebilir ve dinamik olarak bağlantı kurabilirsiniz.


19
Ayrıca henüz evrensel olarak benimsenmemiş bir dil için hoş bir hareket. Hiç kimse sistemlerini go sistem kitaplıkları ile karıştırmakla ilgilenmez.
Matt Joiner

4
Go içerik oluşturucuları dinamik bağlantıyı açıkça tercih ediyor: harmful.cat-v.org/software/dynamic-linking . Google, tüm C ve C ++ dillerini statik olarak bağlar: reddit.com/r/golang/comments/tqudb/…
Graham King

14
Bağlantılı makalenin tam tersini söylediğini düşünüyorum - Go yaratıcıları açıkça statik bağlamayı tercih ediyor.
Petar Donçev

16

Sen almalısınız goupx ile çalışmalarına, bu "düzeltme" Golang ELF çalıştırılabilir upx. Bazı durumlarda zaten yaklaşık% 78 dosya boyutu küçültmem var ~16MB >> ~3MB.

Sıkıştırma oranı genellikle% 25'tir, bu nedenle denemeye değer:

$ go get github.com/pwaller/goupx
$ go build -o filename
$ goupx filename

>>

2014/12/25 10:10:54 File fixed!

        File size         Ratio      Format      Name
   --------------------   ------   -----------   -----------
  16271132 ->   3647116   22.41%  linux/ElfAMD   filename                            

Packed 1 file.

EKSTRA: -sbayrak (şerit), bin dosyasını daha da küçültebilirgoupx -s filename


1
Bu harika, teşekkürler! GOARCH = 386 ayarlıyorum ve bir süredir sadece normal upx kullanıyorum.
codekoala

10
Go 1.6'dan itibaren buna artık gerek yok, normal upx'i kullanabilirsiniz.
OneOfOne

11

adlı bir dosya oluşturun main.go, basit bir merhaba dünya programı ile deneyelim.

package main

import "fmt"

func main(){
    fmt.Println("Hello World!")
}

Go 1.9.1 sürümünü kullanıyorum

$ go version
 go version go1.9.1 linux/amd64

Standart go buildkomutla derleyin .

$ go build main.go
$ ls -lh
-rwxr-xr-x-x 1 nil nil 1.8M Oct 27 07:47 main

Bir kez daha derleyelim go buildama ldflagsyukarıda önerildiği gibi,

$ go build -ldflags "-s -w" main.go
$ ls -lh
-rwxr-xr-x-x 1 nil nil 1.2M Oct 27 08:15 main

Dosya boyutu% 30 azaltılır.

Şimdi kullanalım gccgo,

$ go version
 go version go1.8.1 gccgo (GCC) 7.2.0 linux/amd64

Bina ile gitmek gccgo,

$ go build main.go
$ ls -lh
-rwxr-xr-x 1 nil nil 34K Oct 27 12:18 main

İkili boyut neredeyse% 100 oranında azaltılır. Bir kez de bizim bina deneyelim main.goile gccgoancak inşa bayraklarıyla

$ go build -gccgoflags "-s -w" main.go
-rwxr-xr-x 1 nil nil 23K Oct 27 13:02 main

Uyarı: As gccgoikili dinamik bağlandı. Eğer çok büyük bir ikiliye sahipseniz, gccgo ile derlendiğinizde ikiliniz% 100 azalmayacaktır, fakat boyutu önemli ölçüde küçülecektir.

Gc ile karşılaştırıldığında, gccgo kodu derlemek için daha yavaştır ancak daha güçlü optimizasyonları destekler, bu nedenle gccgo tarafından oluşturulan CPU'ya bağlı bir program genellikle daha hızlı çalışır. Yıllar boyunca GCC'de uygulanan tüm optimizasyonlar, satır içi, döngü optimizasyonları, vektörleştirme, talimat planlaması ve daha fazlası dahil olmak üzere mevcuttur. Her zaman daha iyi kod üretmese de, bazı durumlarda gccgo ile derlenen programlar% 30 daha hızlı çalışabilir.

GCC 7 sürümlerinin Go 1.8 kullanıcı kitaplıklarının tam bir uygulamasını içermesi beklenmektedir. Önceki sürümlerde olduğu gibi, Go 1.8 çalışma zamanı tam olarak birleştirilmemiştir, ancak bu Go programları tarafından görülmemelidir.

Artıları:

  1. Küçültülmüş boyut
  2. Optimize edildi.

Eksileri

  1. Yavaş
  2. Son sürümü kullanılamaz go.

Burayı ve burayı görebilirsiniz .


Teşekkür ederim. Bir sorum var, okuduğum gibi, gogccARM işlemciyi desteklemiyor, doğru mu? Ve Golang derleme dosya boyutunu küçülten bir araç önerebilir misiniz?
M.Rostami

1
Gccgo ile nasıl derlersiniz?
Vitaly Zdanevich

10

Daha kompakt bir merhaba dünya örneği:

package main

func main() {
  print("Hello world!")
}

Büyük fmtpaketi atladık ve ikiliyi önemli ölçüde azalttık :

  $ go build hello.go
  $ ls -lh hello
  ... 259K ... hello2
  $ strip hello
  $ ls -lh hello
  ... 162K ... hello2

C kadar kompakt değil, ancak K cinsinden ölçülmesine rağmen M değil :) Tamam, genel bir yol sadece bir boyutu optimize etmenin bazı yollarını göstermez: şerit kullanın ve minimum paketleri kullanmaya çalışın. Her neyse Go, küçük boyutlu ikili dosyalar yapmak için kullanılan bir dil değildir.


11
Bu kod yerleşik print () işlevini kullanır ve bu uygulama önerilmez. Ve soru, sağlanan örnek programın kod boyutunun nasıl küçültüleceği ile ilgili değil, daha genel bir şekilde idi.
wldsvc 01

@wldsvc Size katılıyorum not. Print () işlevinin görüntü çıkışı için neden kötü olduğunu anlamasanız da? Basit betikler için veya hata ayıklama çıktısı için yeterince iyidir.
Alexander I. Grafov

3
Bkz. Doc.golang.org/ref/spec#Bootstrapping Bu işlevlerin eksiksiz olduğu belgelenmiştir ancak dilde kalmaları garanti edilmez . Benim için bu, bir defalık hata ayıklama amaçları dışında herhangi bir komut dosyasında "bunları kullanmayın" anlamına gelir.
wldsvc

@wldsvc print () 2020'de hala mevcut ancak aynı sorumluluk reddi
beyanıyla


2

Bir sonraki Go 1.11 için 2018 cevap, tweeted olarak tarafından Brad Fitzpatrick (2018 Haziran ortasında):

Birkaç dakika önce, #golang ELF ikili dosyalarındaki DWARF bölümleri artık sıkıştırılmıştır, bu nedenle uç ikili dosyalar artık uçtaki tüm ekstra hata ayıklama öğelerine rağmen Go 1.10'dan daha küçüktür.

https://pbs.twimg.com/media/DfwkBaqUEAAb8-Q.jpg:large

Cf. Golang 11799 sorunu :

Hata ayıklama bilgilerimizi sıkıştırmak önemli ve ucuz bir dosya boyutu kazanımı sağlayabilir.

Commit 594eae5'te daha fazlasını görün

cmd / link: ELF ikili dosyalarında DWARF bölümlerini sıkıştır

Bunun en zor kısmı, ikili düzen kodunun (blk, elfshbits ve diğer çeşitli şeyler), sembollerin ve bölümlerin dosya konumları ile sanal adresleri arasında sabit bir uzaklık varsaymasıdır.

Elbette sıkıştırma, bu sabit ofseti bozar.
Ancak sıkıştırmadan önce yer değiştirmeleri çözmek için sıkıştırmadan önce her şeye sanal adresler atamamız gerekir.

Sonuç olarak, sıkıştırmanın DWARF bölümlerinin ve sembollerinin "adresini" sıkıştırılmış boyutlarına göre yeniden hesaplaması gerekir.
Neyse ki, bunlar dosyanın sonundadır, bu nedenle bu diğer bölümleri veya sembolleri rahatsız etmez. (Ve elbette, DWARF segmentinin en son geldiğini varsayan şaşırtıcı miktarda kod var, öyleyse bir yer daha nedir?)

name        old exe-bytes   new exe-bytes   delta
HelloSize      1.60MB ± 0%     1.05MB ± 0%  -34.39%  (p=0.000 n=30+30)
CmdGoSize      16.5MB ± 0%     11.3MB ± 0%  -31.76%  (p=0.000 n=30+30)
[Geo mean]     5.14MB          3.44MB       -33.08%

Rob Pike şöyle diyor :

Bu yalnızca ELF kullanan makinelerde yardımcı olur.
İkili dosyalar hala çok büyük ve büyüyor.

Brad yanıtladı:

En azından bir şey. Çok daha kötüsü olmak üzereydi.
Kanamayı bir kez durdurdu.

Nedenler : Hata ayıklama bilgisi, ancak herhangi bir talimatın bir kayıt noktası olmasına izin vermek için GC için haritayı da kaydedin.


2

Varsayılan olarak gcc dinamik olarak bağlanır ve statik olarak gider.

Ancak C kodunu statik olarak bağlarsanız, daha büyük boyutlu bir ikili dosya elde edebilirsiniz.

Benim durumumda:

  • go x64 (1.10.3) - 1214208 bayt boyutunda ikili oluşturuldu
  • gcc x64 (6.2.0) - 1421312 bayt boyutunda ikili oluşturuldu

her iki ikili de statik olarak bağlıdır ve debug_info içermez.

go build -ldflags="-s -w" -o test-go test.go
gcc -static -s -o test-c test.c

1

İkili dosya varsayılan olarak çöp toplayıcıyı, git rutinlerini yöneten zamanlama sistemini ve içe aktardığınız tüm kitaplıkları içerir.

Sonuç, yaklaşık 1 Mb'lik minimum boyuttur.


1

Go 1.8'den itibaren yeni eklenti sistemini ikilinizi paylaşılan kitaplıklara benzeyen bir şeye ayırmak için de kullanabilirsiniz. Bu sürüm için yalnızca Linux üzerinde çalışır, ancak diğer platformlar muhtemelen gelecekte desteklenecektir.

https://tip.golang.org/pkg/plugin/

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.