developer tip

컴파일 된 파일 크기를 줄이는 방법은 무엇입니까?

optionbox 2020. 10. 18. 09:21
반응형

컴파일 된 파일 크기를 줄이는 방법은 무엇입니까?


c를 비교하고 가자 : Hello_world.c :

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

Hello_world.go :

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

둘 다 컴파일 :

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

그리고 이것은 무엇입니까?

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

약 1Mb Hello world. 장난 해? 내가 뭘 잘못 했어?

(Hello_go 스트립-> 893K 전용)


파일이 더 크다는 것이 문제입니까? 나는 Go를 모르지만 C 프로그램의 경우가 아닌 일부 런타임 라이브러리를 정적으로 링크한다고 가정합니다. 그러나 아마도 프로그램이 커지면 걱정할 것이 없습니다.

여기설명 된대로 Go 런타임을 정적으로 연결하는 것이 기본값입니다. 이 페이지에는 동적 연결을 설정하는 방법도 나와 있습니다.


Unix 기반 시스템 (예 : Linux 또는 Mac OSX)을 사용하는 경우 -w 플래그로 빌드하여 실행 파일에 포함 된 디버깅 정보를 제거 할 수 있습니다.

go build -ldflags "-w" prog.go

파일 크기가 크게 줄어 듭니다.

자세한 내용은 GDB 페이지를 방문하십시오 : http://golang.org/doc/gdb


2016 년 답변 :

1. Go 1.7 사용

2. 컴파일 go build -ldflags "-s -w"

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

3. 그런 다음 사용은 upx, goupx더 이상 1.6 이후 필요하지 않습니다.

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

Go 바이너리는 정적으로 연결되어 있기 때문에 크기가 큽니다 (cgo를 사용하는 라이브러리 바인딩 제외). C 프로그램을 정적으로 연결해 보면 비슷한 크기로 커지는 것을 볼 수 있습니다.

이것이 정말로 당신에게 문제라면 (믿기 힘들지만) gccgo로 컴파일하고 동적으로 연결할 수 있습니다.


당신은 가야 goupx 작업을하려면, 그것은 것 "수정"Golang ELF 실행 파일을 upx. 어떤 경우에는 이미 78 % 정도의 파일 크기가 줄어 들었습니다 ~16MB >> ~3MB.

압축률은 일반적으로 25 %이므로 시도해 볼 가치가 있습니다.

$ 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.

추가 : -s플래그 (스트립)는 빈 파일을 훨씬 더 줄일 수 있습니다.goupx -s filename


더 간결한 hello-world 예제 :

package main

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

fmt패키지를 건너 뛰고 바이너리가 눈에 띄게 줄었습니다.

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

C만큼 콤팩트하지는 않지만 M이 아니라 K로 측정됩니다. 좋습니다. 크기를 최적화하는 몇 가지 방법을 보여주는 일반적인 방법은 아닙니다. 스트립 을 사용하고 최소 패키지를 사용하십시오. 어쨌든 Go는 작은 크기의 바이너리를 만드는 언어가 아닙니다.


라는 파일을 만들고 main.go간단한 hello world 프로그램을 사용해 보겠습니다.

package main

import "fmt"

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

go 버전 1.9.1을 사용합니다.

$ go version
 go version go1.9.1 linux/amd64

표준 go build명령으로 컴파일하십시오 .

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

이제 다시 한번 컴파일하자 go build만에 ldflags, 위의 제안

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

파일 크기가 30 % 줄어 듭니다.

이제 사용 할 수 있습니다 gccgo,

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

건물은 gccgo,

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

Binary size is reduced by almost 100%. Let's once again try building our main.go with gccgo but with build flags,

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

Warning: As gccgo binaries were dynamically linked. If you have a binary which is very big in size, your binary when compiled with gccgo will not be decreased by 100%, but it will be reduced in size by considerable amount.

Compared to gc, gccgo is slower to compile code but supports more powerful optimizations, so a CPU-bound program built by gccgo will usually run faster. All the optimizations implemented in GCC over the years are available, including inlining, loop optimizations, vectorization, instruction scheduling, and more. While it does not always produce better code, in some cases programs compiled with gccgo can run 30% faster.

The GCC 7 releases are expected to include a complete implementation of the Go 1.8 user libraries. As with earlier releases, the Go 1.8 runtime is not fully merged, but that should not be visible to Go programs.

Pros:

  1. Reduced size
  2. Optimized.

Cons

  1. Slow
  2. Cannot use the latest version of go.

You can see over here and here.


The binary contains by default the garbage collector, the schedulding system that manage the go routines, and all the libraries you import.

The result is a minimal size of about 1 Mb.


From Go 1.8 you can also use the new plugin system to split up your binary into something that resembles shared libraries. For this release it only works on Linux, but other platforms will probably be supported in the future.

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


2018 answer for the next Go 1.11, as tweeted by Brad Fitzpatrick (mid-June 2018):

As of a few minutes ago, DWARF sections in #golang ELF binaries are now compressed, so at tip binaries are now smaller than Go 1.10, even with all the extra debug stuff at tip.

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

Cf. Golang issue 11799:

Compressing our debug info might offer a significant, cheap file size win.

See more in commit 594eae5

cmd/link: compress DWARF sections in ELF binaries

The trickiest part of this is that the binary layout code (blk, elfshbits, and various other things) assumes a constant offset between symbols' and sections' file locations and their virtual addresses.

Compression, of course, breaks this constant offset.
But we need to assign virtual addresses to everything before compression in order to resolve relocations before compression.

As a result, compression needs to re-compute the "address" of the DWARF sections and symbols based on their compressed size.
Luckily, these are at the end of the file, so this doesn't perturb any other sections or symbols. (And there is, of course, a surprising amount of code that assumes the DWARF segment comes last, so what's one more place?)

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 mentions:

That only helps on machines that use ELF.
Binaries are still too big, and growing.

Brad replied:

At least it's something. Was about to be much worse.
Stopped the bleeding for one release.

Reasons: Debug info, but also register map for the GC to let any instruction be a savepoint.


By default gcc links dynamicaly and go - staticaly. But if you link you C code statically, you might get a binnary with a bigger size.

In my case:

  • go x64 (1.10.3) - generated binary with size 1214208 byte
  • gcc x64 (6.2.0) - generated binary with size 1421312 byte

both binaries are statically linked and without debug_info.

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

참고URL : https://stackoverflow.com/questions/3861634/how-to-reduce-compiled-file-size

반응형