일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- SQLite
- kernel
- 커널
- DBMS
- newSQL
- Preprocessor
- Golang
- go
- 긴옵션
- OS 커널
- 전처리기
- 포인터변수
- 컴퓨터 강좌
- 한빛미디어
- 함수포인터
- UNIX
- 구조와 원리
- Programming
- 포인터
- DBMS 개발
- FreeBSD
- TiKV
- 약어
- TiDB
- Symbol
- bash
- getopts
- Pointer
- Windows via c/c++
- UNIX Internals
- Today
- Total
sonumb
Go - release/debug 모드 빌드하기 - 2 본문
개요
https://sonumb.tistory.com/124
Go - release/debug 모드 빌드하기 - 1
개요 빌드 시, 릴리즈 모드에 따라 호출되는 함수의 기능을 달리하고 싶을 때가 있다. "모드에 따른 다른 빌드"를 구현하는데, 두 가지 방법이 있다. 방안들 1. 빌드 시, 태그( -tags ) 옵션을 이용하
sonumb.tistory.com
개요 및 전반적인 이야기는 윗 글을 참조하고, 전편에 이어 방안 2에 대한 소스코드를 보여주려한다.
'모드에 따른 빌드하기'의 방안 2에 대해 간단히 설명하자면,
- 빌드시, 빌드모드를 소스코드에 기록하기 (실제로는 하드디스크의 파일의 내용을 바꾼다는 의미가 아니라, 파일 내용을 메모리로 로딩 후, 메모리 내에 변수를 수정한다는 것이다.)
- 코드에 기록된 빌드모드로 동작
이다.
여기서 가장 중요한것은 빌드 모드를 소스코드에 기록하는 것은 "go build -ldflag
" 의 '-X
' 옵션이다. 이에 대한 설명은 아래 링크에서.
link command - cmd/link - pkg.go.dev
Link, typically invoked as “go tool link”, reads the Go archive or object for a package main, along with its dependencies, and combines them into an executable binary. Command Line ¶Usage: go tool link [flags] main.a Flags: -B note Add an ELF_NT_GNU_B
pkg.go.dev
소스파일들
main.go
파일
package main
import (
"fmt"
"time"
)
func foo() {
_, _, fnname, err := GetTraceInfo()
if err == nil {
println(fnname)
}
time.Sleep(time.Second)
fmt.Println("foo() : done")
}
func bar() {
Trace()
time.Sleep(1 * time.Second)
fmt.Println("bar() : done")
}
func main() {
foo()
bar()
}
trace.go
파일
package main
import (
"fmt"
"runtime"
"errors"
"strings"
)
type TraceFunc func()
const (
traceMode_Release = iota + 0
traceMode_Debug
traceMode_Max
)
var (
BuildType = "Release"
traceMode = traceMode_Release
)
func init() {
switch BuildType {
case "Release":
traceMode = traceMode_Release
case "Debug":
traceMode = traceMode_Debug
}
if strings.EqualFold(BuildType, "debug") == true {
traceMode = traceMode_Debug
}
}
func GetTraceInfo() (string, int, string, error) {
pc, file, line, ok := runtime.Caller(1)
if ok == false {
return "", 0, "", errors.New("invalid calling")
}
fn := runtime.FuncForPC(pc)
if fn == nil {
return "", 0, "", errors.New("unknown method")
}
return file, line, fn.Name(), nil
}
var trFunc = [traceMode_Max]TraceFunc {
trace_Release,
trace_Debug,
}
func trace_Release() {
// do nothing
}
func trace_Debug() {
pc, file, line, ok := runtime.Caller(1)
if ok == false {
return
}
fnName := ""
fn := runtime.FuncForPC(pc)
if fn != nil {
fnName = fn.Name()
}
fmt.Printf("%s:%d %s()\n", file, line, fnName)
}
func Trace() {
trFunc[traceMode]()
}
Makefile
파일
all: release
DEBUG := "Debug"
release:
go build -o buildSystem2
debug:
go build -ldflags '-X "main.BuildType=Debug"' -o buildSystem2
clean:
rm -f buildSystem2
실행 및 결과
$ make clean all; ./buildSystem2
rm -f buildSystem2
go build -o buildSystem2
main.foo
foo() : done
bar() : done
$ make clean debug; ./buildSystem2
rm -f buildSystem2
go build -ldflags '-X "main.BuildType=Debug"' -o buildSystem2
main.foo
foo() : done
/Users/sonumb/work/go_exam/buildsystem2/trace.go:72 main.Trace()
bar() : done
$
성능 차이
두 방식 간의 성능 차이는 확실히 존재한다. 개념적으로도 함수를 한번 더 호출하는 두 번째 방식이 성능이 떨어질 것으로 예측할 수 있다.
릴리즈 모드, 즉 비어있는 Trace() 함수를 10억번 호출하는데, 아래와 같은 차이가 있다.
방안1: 비어 있는 trace()를 호출 |
방안2: Trace()함수 내에서 함수포인터를 이용한 호출(if문은 없음) |
$ time buildSystem real 0m0.221s user 0m0.217s sys 0m0.003s |
$ time buildSystem2 real 0m1.077s user 0m1.072s sys 0m0.004s |
수치상으로는 5배이지만, 10억회 호출할 동안 다른 일처리(파일, 소켓 I/O)로 인해 이 정도 성능차이는 아주 적게 반영될 것이다.
가령 "10분 0.2초 VS. 10분 1초" 같은 느낌...
따라서, 두 방식간의 차이는 성능보단 구현 용이성 및 형상 관리 측면에서 접근하는 것이 옳을 것으로 판단된다.