>  기사  >  백엔드 개발  >  Go 프로그램이 너무 큽니다. 지연 초기화를 사용할 수 있나요?

Go 프로그램이 너무 큽니다. 지연 초기화를 사용할 수 있나요?

Golang菜鸟
Golang菜鸟앞으로
2023-08-04 17:23:53811검색


회사의 지속적인 발전 속에서 대부분이 초기에는 대형 유닛이었고 변화도 느렸습니다. 창고는 10년 이상 사용할 예정이며 창고 규모는 기본적으로 지속적으로 증가하는 과정.

영향 중 하나는 패키징된 애플리케이션의 크기가 점점 커지고, 어디에 사용되는지 알 수 없다는 점인데... 오늘 논의할 제안은 "제안: 언어: 지연 초기화 가져오기 가능 부작용 없이 수입 [1]'이 이와 관련이 있습니다.

Proposal

Background

아주 간단한 Go 코드를 관찰하며 공부해 봅시다. 다음 코드:

package main

import _ "crypto/x509"

func main() {}

이 Go 프로그램에는 코드가 3줄만 있고 아무것도 보이지 않습니다. 이것이 실제로 사실입니까?

다음 명령을 실행하여 초기화 프로세스를 확인할 수 있습니다.

$ go build --ldflags=--dumpdep main.go 2>&1 | grep inittask

출력 결과:

runtime.main -> runtime..inittask
runtime.main -> main..inittask
main..inittask -> crypto/x509..inittask
crypto/x509..inittask -> bytes..inittask
crypto/x509..inittask -> crypto/sha256..inittask
crypto/x509..inittask -> encoding/pem..inittask
crypto/x509..inittask -> errors..inittask
crypto/x509..inittask -> sync..inittask
crypto/x509..inittask -> crypto/aes..inittask
crypto/x509..inittask -> crypto/cipher..inittask
crypto/x509..inittask -> crypto/des..inittask
...
context..inittask -> context.init.0
vendor/golang.org/x/net/dns/dnsmessage..inittask -> vendor/golang.org/x/net/dns/dnsmessage.init
vendor/golang.org/x/net/route..inittask -> vendor/golang.org/x/net/route.init
vendor/golang.org/x/net/route..inittask -> vendor/golang.org/x/net/route.init.0
...

이 프로그램은 실제로 많은 소프트웨어 패키지(표준 라이브러리, 타사 패키지 등)를 초기화합니다. 이로 인해 패키지 크기가 표준 1.3MB에서 2.3MB로 변경됩니다.

특정 규모에서 모든 사람은 이러한 영향이 매우 비싸다고 믿습니다. 3줄 Go 프로그램은 실질적인 작업을 수행하지 않는다는 것을 알 수 있기 때문입니다.

시작 성능에 민감한 프로그램은 더욱 불편해집니다. 일반 프로그램도 시간이 지남에 따라 악순환에 빠지고 시작이 평소보다 느려집니다.

솔루션

다른 제안 "제안: 사양: Go 2: 가져온 패키지 초기화에 대한 수동 제어 허용[2]"과 함께 솔루션을 살펴보겠습니다.

핵심 아이디어는 업계에서 지연 로딩이라고도 불리는 지연 초기화(lazy init)를 도입하는 것입니다. 즉, 필요한 경우 실제 import가 이루어지고, 패키지가 도입되지 않은 경우에는 초기화가 완료됩니다.

최적화 방향: 아래에 언급된 go:lazyinit 또는 go:deferred 주석과 같이 패키지 경로를 가져온 후 주로 지연 초기화 선언을 추가합니다. 프로그램을 공식적으로 초기화하기 전에 프로그램이 실제로 사용될 때까지 기다리십시오.

1、go:lazyinit 的例子:

package main

import (
      "crypto/x509" // go:lazyinit
      "fmt"
)

func main() {...}

2、go:deferred 的例子:

package main

import (
    _ "github.com/eddycjy/core" // go:deferred
    _ "github.com/eddycjy/util" // go:deferred
)

func main() {
    if os.Args[1] != "util" {
        // 现在要使用这个包,开始初始化
        core, err := runtime.InitDeferredImport("github.com/some/module/core")
        ...
    }
    ...
}

以此来实现,可以大大提高启动性能。

讨论

实际上在大多数的社区讨论中,对这个提案是又爱又恨。因为它似乎又有合理的诉求,但细思似乎又会发现完全不对劲。

这个提案的背景和解决方案,是治标不治本的。因为根本原因是:许多库滥用了 init 函数,让许多不必要的东西都初始化了。

Go 프로그램이 너무 큽니다. 지연 초기화를 사용할 수 있나요?

Go 核心开发团队认为让库作者去修复这些库,而不是让 Go 来 “解决” 这些问题。如果支持惰性初始化,也会为这些低质量库的作者提供继续这样做的借口。

似曾相识的感觉

在写这篇文章时,我想起了 Go 的依赖管理(Go modules),其有一个设计是基于语义化版本的规范。

如下图

Go 프로그램이 너무 큽니다. 지연 초기화를 사용할 수 있나요?

版本格式为 “主版本号.次版本号.修订号”,版本号的递增规则如下:

  • 主版本号:当你做了不兼容的 API 修改。
  • 次版本号:当你做了向下兼容的功能性新增。
  • 修订号:当你做了向下兼容的问题修正。

Go modules 的原意是软件库都遵守这个规范,因此内部会有最小版本选择的逻辑。

也就是一个模块往往依赖着许多其它许许多多的模块,并且不同的模块在依赖时很有可能会出现依赖同一个模块的不同版本,Go 会把版本清单都整理出来,最终得到一个构建清单。

如下图:

Go 프로그램이 너무 큽니다. 지연 초기화를 사용할 수 있나요?

구축된 최종 종속성 버전이 예상과 일치하지 않아 많은 비즈니스 문제가 발생할 가능성이 높습니다. 가장 고전적인 것은 grpc-go, protoc-go, etcd의 다중 버전 호환성 문제로 많은 사람들이 어려움을 겪고 있습니다.

이 분야에서 Go 팀의 디자인은 비교적 이상적이며 Cao Da도 이를 Go 모듈의 일곱 가지 대죄 중 하나로 분류했습니다. 소프트웨어 패키지의 init 기능은 무작위 초기화에 문제가 많은데, 이 역시 조금은 익숙합니다.

요약

이 문제에 대한 해결책(제안)은 아직 논의 중입니다. 분명히 Go 팀은 소프트웨어 라이브러리 작성자가 자신의 코드를 제한하고 무작위로 초기화하지 않기를 바랍니다.

지연 초기화를 도입하는 것은 어떻습니까? 댓글 영역에서 메시지를 남기고 토론하는 것을 환영합니다.

위 내용은 Go 프로그램이 너무 큽니다. 지연 초기화를 사용할 수 있나요?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 Golang菜鸟에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제