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


82

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 전용)


2
x86_64 Mac에서 "Hello World"바이너리는 내가 생각하는 x64 Linux 시스템에서와 같이 1.3MB입니다. 반면 ARM x32 바이너리는 x86_32 바이너리만큼 큽니다. 크기는 각 아키텍처의 "단어"길이에 따라 크게 달라집니다. x32 컴퓨터에서는 x64에서 32 비트이고 너비는 64 비트입니다. 따라서 x32 "Hello World"바이너리는 약 30 % 더 작습니다.
알렉스

17
@Nick : GO가 시스템 언어로 판매된다는 점을 고려하면 공정한 질문이라고 생각합니다. 저는 시스템에서 일하는데 4GB 이상의 RAM과 거대한 디스크의 사치가 항상있는 것은 아닙니다.
Ed S.

3
893kb 실행 파일은 "4GB 이상의 RAM 및 대용량 디스크"와는 거리가 멀고 다른 사람들이 이미 지적했듯이 여기에는 쉽게 제외 할 수있는 정적으로 연결된 go 런타임이 포함됩니다.
Nick Johnson

22
네, 그것은 먼 외침이고 대답을 알고 있습니다. 그러나 그것은 타당한 질문이고 "누가 메모리 소비에 관심을 갖는지"태도는 그것이 중요하지 않은 시스템에서 작업하는 경향이 있습니다. 당신은 무지하다고 생각하는 것 같습니다. 괜찮습니다. 질문하지 않는 것이 좋습니다. 그리고 다시 말하겠습니다. 때로는 ~ 1MB가 많고, 분명히 그 세상에서 일하지 않습니다. 편집-Google에서 일합니다! 롤. '누가 염려 태도하지만.
에드 S.

12
분명히 자바 부서에서;)
Matt Joiner

답변:


29

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

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


2
개방 문제는 Go1.5의 이정표 세트,

80

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

go build -ldflags "-w" prog.go

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

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


3
같은과 함께 달성 strip: 명령을 go build prog.go; strip prog우리가 소위있어 스트라이프 실행 : ELF 64 비트 LSB 실행, - 64, 버전 1 (SYSV), 동적 (사용이 libs와 공유) 연결, 제거
알렉산더 I.Grafov

1
이것은 Windows에서 작동하며 정상적으로 빌드하고 제거하여 생성 된 파일에 비해 약간 더 작은 이진 파일을 제공합니다.
Isxek 2014 년

9
@Axel : go 바이너리 제거는 명시 적으로 지원되지 않으며 경우에 따라 심각한 버그가 발생할 수 있습니다. 여기를 참조 하십시오 .
Flimzy

9
현재 문서 목록 -w대신 -s. 확실하지 :-) 어떤 차이가 있지만, 당신은 당신의 대답을 업데이트 할 수 있습니다
Dynom

1
@Dynom : -w는 크기를 줄이는 것처럼 보이지만 축소는 -s만큼 좋지 않습니다. : -s는 -w 암시하는 것 같다 golang.org/cmd/link
arkod

42

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*

3
UPX에 대한 일반적인 경고가 여기에 적용될 수 있음을 여기에 추가하고 싶습니다. 자세한 내용은 다음을 참조하십시오. stackoverflow.com/questions/353634/… (대부분 Windows가 아닌 운영 체제에도 적용됩니다).
Jonas

23

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

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


19
또한 아직 보편적으로 채택되지 않은 언어에 대한 좋은 움직임입니다. 아무도 go system libs로 시스템을 복잡하게 만드는 데 관심이 없습니다.
Matt Joiner

4
이동 제작자는 명시 적으로 동적 링크를 선호 : harmful.cat-v.org/software/dynamic-linking . Google은 모든 C 및 C ++를 정적으로 연결합니다. reddit.com/r/golang/comments/tqudb/…
Graham King

13
링크 된 기사는 그 반대라고 생각합니다. Go 제작자는 명시 적으로 정적 링크를 선호합니다.
Petar Donchev 2015

17

당신은 가야 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.

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


1
훌륭합니다, 감사합니다! 나는 GOARCH = 386을 설정하고 잠시 동안 정상적인 upx를 사용하고 있습니다.
codekoala 2015 년

9
Go 1.6에서는 더 이상 필요하지 않으며 일반 upx를 사용할 수 있습니다.
OneOfOne

12

라는 파일을 만들고 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

바이너리 크기는 거의 100 % 감소합니다. 이제 다시 한 번 우리의 구축 해보자 main.go으로 gccgo하지만, 빌드 플래그

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

경고 :gccgo바이너리가 동적으로 연결되어 있었다. 크기가 매우 큰 바이너리가있는 경우 gccgo로 컴파일 할 때 바이너리가 100 % 줄어들지는 않지만 크기가 상당히 줄어 듭니다.

gc에 비해 gccgo는 코드 컴파일 속도가 느리지 만 더 강력한 최적화를 지원하므로 gccgo로 빌드 된 CPU 바인딩 프로그램은 일반적으로 더 빠르게 실행됩니다. 인라인, 루프 최적화, 벡터화, 명령어 스케줄링 등을 포함하여 수년 동안 GCC에서 구현 된 모든 최적화를 사용할 수 있습니다. 항상 더 나은 코드를 생성하지는 않지만 gccgo로 컴파일 된 프로그램이 30 % 더 빠르게 실행될 수 있습니다.

GCC 7 릴리스에는 Go 1.8 사용자 라이브러리의 완전한 구현이 포함될 것으로 예상됩니다. 이전 릴리스와 마찬가지로 Go 1.8 런타임은 완전히 병합되지 않았지만 Go 프로그램에 표시되지 않아야합니다.

장점 :

  1. 축소 된 크기
  2. 최적화되었습니다.

단점

  1. 느린
  2. 최신 버전의 go.

여기여기에서 볼 수 있습니다 .


감사합니다. 읽은대로 gogccARM 프로세서를 지원하지 않는 질문 이 있습니다. 맞습니까? 그리고 Golang 빌드 파일 크기를 줄이는 도구를 제안 해 주시겠습니까?
M. Rostami

gccgo로 어떻게 컴파일합니까?
Vitaly Zdanevich

10

더 간결한 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는 작은 크기의 바이너리를 만드는 언어가 아닙니다.


11
이 코드는 내장 print () 함수를 사용하므로 권장하지 않습니다. 그리고 문제는 제공된 샘플 프로그램의 코드 크기를 줄이는 방법이 아니라보다 일반적인 방법이었습니다.
wldsvc

@wldsvc 동의합니다. 왜 print ()가 디스플레이 출력에 나쁜 방법인지 이해하지 못합니까? 간단한 스크립트 또는 디버그 출력의 경우 충분합니다.
Alexander I.Grafov 2014 년

3
doc.golang.org/ref/spec#Bootstrapping을 참조하십시오 . 이러한 함수는 완전성을 위해 문서화되어 있지만 해당 언어로 유지된다는 보장은 없습니다 . 나에게 그것은 한 번의 디버깅 목적을 제외하고는 어떤 스크립트에서도 "사용하지 말 것"을 의미합니다.
wldsvc

3

Brad Fitzpatrick (2018 년 6 월 중순)이 트윗 한 다음 Go 1.11에 대한 2018 년 답변 :

몇 분 전부터 #golang ELF 바이너리의 DWARF 섹션이 이제 압축되었으므로 팁에서 모든 추가 디버그 항목이 포함되어 있어도 이제 팁에서 바이너리가 Go 1.10보다 작습니다.

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

Cf. Golang 문제 11799 :

디버그 정보를 압축하면 상당히 저렴한 파일 크기를 얻을 수 있습니다.

커밋 594eae5 에서 더보기

cmd / link : ELF 바이너리에서 DWARF 섹션 압축

이것의 가장 까다로운 부분은 바이너리 레이아웃 코드 (blk, elfshbits 및 기타 다양한 것)가 심볼과 섹션의 파일 위치와 가상 주소 사이에 일정한 오프셋을 가정한다는 것입니다.

물론 압축은이 일정한 오프셋을 깨뜨립니다.
그러나 압축 전에 재배치를 해결하려면 압축 전에 모든 것에 가상 주소를 할당해야합니다.

결과적으로 압축은 압축 된 크기를 기반으로 DWARF 섹션 및 기호의 "주소"를 다시 계산해야합니다.
운 좋게도 이것들은 파일의 끝에 있으므로 다른 섹션이나 기호를 방해하지 않습니다. (물론 DWARF 세그먼트가 마지막에 올 것이라고 가정하는 놀라운 양의 코드가 있습니다. 그럼 한 곳이 더 있을까요?)

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 다음과 같이 언급합니다 .

이는 ELF를 사용하는 컴퓨터에서만 도움이됩니다.
바이너리는 여전히 너무 크고 커지고 있습니다.

Brad는 다음과 같이 대답했습니다.

적어도 뭔가입니다. 훨씬 더 나빠질 것입니다.
한 번의 릴리스를 위해 출혈을 멈췄습니다.

이유 : 디버그 정보뿐만 아니라 모든 명령어가 저장 점이되도록 GC에 대한 맵을 등록합니다.


1

바이너리에는 기본적으로 가비지 수집기, go 루틴을 관리하는 예약 시스템 및 가져 오는 모든 라이브러리가 포함됩니다.

그 결과 최소 크기는 약 1MB입니다.


1

Go 1.8부터는 새로운 플러그인 시스템을 사용하여 바이너리를 공유 라이브러리와 유사한 것으로 분할 할 수도 있습니다. 이 릴리스의 경우 Linux에서만 작동하지만 향후 다른 플랫폼도 지원 될 것입니다.

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



1

기본적으로 gcc는 동적으로 연결되고 정적으로 이동합니다.

그러나 C 코드를 정적으로 연결하면 더 큰 크기의 바이너리를 얻을 수 있습니다.

나의 경우에는:

  • go x64 (1.10.3)-1214208 바이트 크기의 바이너리 생성
  • gcc x64 (6.2.0)-1421312 바이트 크기로 생성 된 바이너리

두 바이너리는 모두 정적으로 링크되고 debug_info가 없습니다.

go build -ldflags="-s -w" -o test-go test.go
gcc -static -s -o test-c test.c
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.