跳至主要内容

Go语言中的init函数

 

1. init函数的作用

  • 包初始化:init函数用于在包被导入时执行初始化任务(如配置资源、注册组件等)。

  • 可重复定义:同一个包中可以定义多个init函数,它们会按顺序执行。


2. init函数的执行顺序

(1)同一个Go文件中的多个init函数

  • 按代码中的定义顺序执行。

go
复制
// a.go 文件内
func init() { fmt.Println("init 1") }
func init() { fmt.Println("init 2") }
// 输出:init 1 → init 2

(2)同一个包中的不同文件

  • 按文件名排序(字典序)执行各文件中的init。

  • 例如:a.go的init先于b.go的init执行。

(3)不同包的init函数

  • 按依赖关系从最深到最浅执行:

    1. 被导入的包的init优先于当前包的init。

    2. 若包A导入包B,包B导入包C,则执行顺序为:C → B → A。

  • 同一层级按import语句的顺序执行。


3. Go程序的完整初始化顺序

  1. 被导入的包:

    • 递归初始化其依赖的包。

    • 初始化包级变量和常量。

    • 执行包的init函数。

  2. 当前包:

    • 初始化包级变量和常量。

    • 执行当前包的init函数。

  3. main函数:

    • 最后执行main包的main函数。

正确顺序总结:

text
复制
被导入包的变量/常量 → 被导入包的init → 当前包的变量/常量 → 当前包的init → main函数

4. 其他关键规则

(1)init函数的唯一性

  • 即使一个包被多次导入(如多个包依赖它),其init函数仅执行一次(包级单例)。

(2)执行环境

  • 所有init函数在同一个goroutine中按顺序执行,不存在并发问题。

(3)避免循环导入

  • Go禁止循环导入(如包A导入包B,包B又导入包A),需通过设计解耦。


示例代码验证

场景:

  • 包deep(依赖层次最深) → 包middle → 包main。

go
复制
// deep/init.go
package deep
func init() { fmt.Println("deep init") }

// middle/init.go
package middle
import _ "deep"
func init() { fmt.Println("middle init") }

// main.go
package main
import _ "middle"
func init() { fmt.Println("main init") }
func main() { fmt.Println("main") }

输出:

text
复制
deep init → middle init → main init → main

常见误区纠正

  • 错误理解:当前包的init先于其变量/常量初始化。

    • 正确顺序:当前包的变量/常量初始化 → 当前包的init执行。

  • 错误理解:不同包的init按import顺序直接执行。

    • 正确逻辑:依赖的最深包最先初始化(递归处理)。


通过理解init的执行顺序和规则,可以避免初始化时的依赖问题,确保资源按预期正确初始化。

此博客中的热门博文

PyTorch学习路线图

  第一阶段:基础知识(2-3周) 第1周:Python与机器学习基础 Python数据结构与NumPy基础 张量概念与线性代数基础 机器学习基本概念 第2-3周:PyTorch核心 张量操作与计算图 自动微分(autograd)机制 数据加载与预处理(DataLoader, Dataset) 构建神经网络模块(nn.Module) 第二阶段:深度学习基础(4-6周) 第4周:线性模型与优化器 线性回归与逻辑回归实现 优化器(SGD, Adam等) 损失函数与评估指标 第5-6周:基础神经网络 多层感知机(MLP) 卷积神经网络(CNN) 循环神经网络(RNN, LSTM, GRU) 第7-8周:训练技巧 模型保存与加载 学习率调度 正则化技术 迁移学习 第三阶段:进阶应用(6-8周) 第9-10周:计算机视觉 图像分类 目标检测 图像分割 视觉Transformer 第11-12周:自然语言处理 词嵌入 序列到序列模型 Transformer架构 预训练语言模型应用 第13-14周:生成模型 自编码器 变分自编码器(VAE) 生成对抗网络(GAN) 扩散模型 第四阶段:工程实践(4-6周) 第15-16周:模型部署 模型量化与优化 TorchScript与ONNX导出 服务化部署 移动端部署 第17-18周:高级训练技术 分布式训练 混合精度训练 梯度累积与梯度裁剪 模型剪枝与蒸馏 第19-20周:项目实战 完整项目流程 模型性能优化 工业级代码实践

LLM缓存详解

 可以把“大模型缓存”理解成: 把已经算过的结果(或中间结果)存下来,下次尽量复用 。但这里面其实分几层,不只是简单的“问题→答案”缓存。 1️⃣ 常见的几种缓存类型 (1)KV Cache(推理内部缓存) Transformer 在生成时,会把前面 token 的 Key/Value 向量 缓存下来。 本质:避免重复计算 attention 作用: 同一请求内部加速 特点: 👉 只对“同一上下文继续生成”有效 👉 不跨用户、不跨请求 这类缓存是你体感“流式输出越来越快”的原因之一。 (2)Prompt Cache(提示词缓存) 缓存的是: 相同(或高度相似)的 prompt → 对应的中间表示 / 输出 典型场景: 系统提示词(system prompt)很长 多轮对话里前文基本不变 👉 这里能省掉 前缀计算成本(prefill) (3)Embedding / 语义缓存(Semantic Cache) 这个才是你问题的关键 👇 不是按“字符串完全一致”,而是: 把问题转成向量 → 找“语义相似”的历史问题 → 直接复用答案 2️⃣ 为什么命中缓存成本低很多? 因为大模型推理成本主要在两块: (1)Prefill(吃 prompt) 复杂度 ~ O(n²) 很贵(尤其长 prompt) (2)Decode(逐 token 生成) 每个 token 都要算一遍模型 而缓存命中后: KV cache:不用重复 attention Prompt cache:不用重新 encode 语义缓存: 直接跳过模型推理 👉 相当于从: 几十~几百毫秒 + GPU算力 变成: 一次向量检索(毫秒级)+ 直接返回 所以成本差一个数量级是正常的。 3️⃣ “每个人问法不同,怎么命中缓存?” 这是核心难点,也是工程重点👇 ❌ 不能靠字符串匹配 比如: “今天天气怎么样” “今天外面热不热” 字符串完全不同 → 必须 miss ✅ 用语义相似度(Embedding) 流程一般是: 把问题转 embedding(向量) 在向量数据库里找 TopK 相似问题 如果相似度 > 阈值(比如 0.9) 直接返回缓存答案 一个简单示意 Q1: 北京天气怎么样 → embedding A Q2: 北京今天热吗 → embedding B cosine(A, B) ≈ 0.95...

Elasticsearch 读写原理指南

### 1. 什么是 segment,里面装了什么? 在 Lucene(也是 Elasticsearch)里,索引被切分成若干 **segment(段)**,每个 segment 是一个完整的、只读的倒排索引单元。一个 segment 包含: * **倒排词典** —— 用 **FST(Finite‑State Transducer)** 以高度压缩的形式保存每个字段出现的所有 term 以及 term→ord 的映射。对应的磁盘文件是 `*.tim`(新版)或 `*.tis/*.tii`(旧版)。 * **倒排列表(postings)** —— 保存每个 term 出现的文档 ID、频次、位置信息等,文件名通常是 `*.doc`、`*.pos`、`*.pay`。 * **存储字段**(_source、store:true 的字段)—— 以二进制块的形式写入 `*.fdt` / `*.fdx`。 * **doc‑values、norms、向量** 等辅助结构,分别保存在 `*.dv`、`*.norm`、`*.tv` 等文件里。 * **deleted‑docs bitmap**(`*.del`),标记哪些文档已被删除或被更新。 所有这些文件在 segment **写入磁盘后即成为只读**,后续的查询只能读取,永远不会在原文件上进行增删改。 --- ### 2. 原始文档和 FST 为什么都在 segment 里? * **原始文档**:Elasticsearch 默认把完整的 JSON(_source)以及任何 `store:true` 的字段写入 segment 的 `*.fdt/*.fdx` 文件。每个 segment 保存自己的那部分文档,旧的 segment 在合并前仍然保留,直到合并后被删除。 * **FST**:每个字段的词典在每个 segment 中单独维护,采用 FST 进行前缀共享和字节压缩。这样即使同一个 term 在多个 segment 中出现,也会在每个 segment 里拥有独立的映射,查询时只需要在对应 segment 的 FST 中定位即可。 --- ### 3. 查询时到底是怎么遍历 segment 的? 1. **请求入口**      客户端的搜索请求先到达 **协调节点**,协调节点把请求 ...