ホームページ > バックエンド開発 > Golang > Go プログラムが大きすぎます。遅延初期化を使用できますか?

Go プログラムが大きすぎます。遅延初期化を使用できますか?

リリース: 2023-08-04 17:23:53
転載
838 人が閲覧しました


#会社の継続的な発展において、そのほとんどは当初は大規模な単一の事業体であり、変革は遅々として進みませんでした。規模は基本的に増加するプロセスです。

影響の一つは、パッケージ化されたアプリケーションのサイズがどんどん大きくなり、どこで使われるか分からないことです... 本日議論する提案「提案: 言語」 : 副作用なしでインポートできる遅延初期化インポート[1]》は、これに関連しています。

提案

背景

非常に単純な 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
...
ログイン後にコピー

このプログラムは実際に多くのソフトウェア パッケージ (標準ライブラリ、3 番目) を初期化します。パーティーパッケージなど)。これにより、パッケージ サイズが標準の 1.3 MB から 2.3 MB に変更されます。

一定の規模では、その影響は非常に高くつくと考えられます。なぜなら、3 行 Go プログラムが実質的なことを何もしていないことがわかるからです。

起動パフォーマンスに敏感なプログラムはさらに不快になり、通常のプログラムも時間の経過とともに悪循環に陥り、起動が通常より遅くなります。

提案

解決策の面では、これを別の提案「提案: 仕様: Go 2: 手動制御を許可する」と組み合わせました。インポートされたパッケージの初期化について #[2]>>一緒に見てみましょう。

中心となるアイデアは、業界では遅延読み込みとも呼ばれる遅延初期化 (遅延 init) を導入することです。つまり、実際のインポートは必要に応じて行われ、パッケージが導入されていない時点で初期化が完了します。

最適化の方向: 主に、パッケージ パスのインポート後に遅延初期化ステートメントを追加します (後述の 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 のマルチバージョン互換性の問題であり、多くの人を悩ませています。

この分野の囲碁チームの設計は比較的理想的であり、曹達もこれを囲碁モジュールの七つの大罪の 1 つとして分類しました。ソフトウェア パッケージの init 関数には、ランダムな初期化に関する多くの問題がありますが、これも少し馴染みのあるものです。

概要

この問題に対する解決策 (提案) はまだ議論中です。明らかに Go チームは、ソフトウェア ライブラリの作成者が独自のコードを制約できることを望んでいます。いじらないで初期化してください。

遅延初期化を導入してみてはいかがでしょうか。どう思いますか?メッセージを残してコメント欄で議論することを歓迎します。

以上がGo プログラムが大きすぎます。遅延初期化を使用できますか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:Golang菜鸟
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート