ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Substrate 指标开发最佳实践:从 Instrument 选型、命名到注册的完整流程

Substrate 指标开发最佳实践:从 Instrument 选型、命名到注册的完整流程 人工智能AI AgentAgent 沙箱云原生容器运行时零信任【免费下载链接】substrateAgent Substrate: the core system项目地址https://gitcode.com/GitHub_Trending/substrate7/substrate点击查看免费下载本文是 Agent Substrate 核心仓库substrate中关于如何在子系统内新增一个 OpenTelemetry 指标的工程指南对应 docs/dev/best-practices/metrics.md。它回答四个问题什么时候该加指标以及什么时候不该加、该选哪种 Instrument、指标与标签如何命名、如何用 Go 编写、测试并把它注册进指标注册表。读完本文你将掌握 Substrate 体系中从一次缓存命中率到一条ate.*指标的全套规范并能在atelet、ateapi等二进制中按同一标准落地新指标。在动手之前建议先阅读 Actor Observabilitydocs/observability.md它说明了系统目前已经有哪些遥测、注册表如何使用。本文只讲如何往里加东西。什么时候该加指标什么时候不该加判断的第一原则是这个值是否会被聚合。应该加指标当有人会对该值做 rate速率、ratio比率、percentile百分位、按结果计数、或全集群汇总时。缓存命中率、按阶段拆分的延迟分布、队列深度——这些是指标。不应该加指标当问题只针对某一个 actor时。由于 actor 身份被禁止出现在指标标签中见下文标签与基数指标无法回答actor X 花了多久。此时应该用ateattr.ActorLogAttrs写一条结构化日志记录atelet中的Restore timing breakdown就是典范。指标与日志并非二选一atelet对一次 restore 同时发出两者——直方图用于集群视图日志记录用于单个 actor 视图。不要为一次性/启动期事实加指标如配置值、版本号把它们放在启动日志行或资源对象上即可。新指标 PR 的检查清单每个指标 PR 都包含以下部分评审者会逐项核对指标在 Go 中定义于注入的metric.Meter之上nil 安全并带metric.WithUnit与metric.WithDescription每个标签键都是internal/ateattr中的常量每个标签值要么来自其中定义的有界集合要么命名一个 operator 创建的对象模板、池符合标签规则单元测试通过ManualReader采集指标断言名称、单位、Instrument 类型以及每个序列的标签集合指标与任何新属性都登记在docs/metrics/registry/metrics.yaml且hack/verify/metrics.sh通过若指标包含注册表无法承载的规则、或填补了某个盲区则更新docs/metrics/substrate.yamldocs/observability.md的指标表为它增加一行。选择 Instrument你想知道什么InstrumentGo 构造器仓库内示例某件事发生了多少次及其速率CounterInt64Counterate.imagecache.requestsinternal/imagecache/metrics.go每次发生的时长或大小分布HistogramFloat64Histogram秒、Int64Histogram字节ate.actor.restore.durationcmd/atelet/metrics.go、atelet.snapshot.sizecmd/atelet/main.go当前存在多少东西通过加减维护UpDownCounterInt64ObservableUpDownCounter可在采集时枚举、Int64UpDownCounter自己维护增量罕见ate.workerpool.workerscmd/ateapi/internal/controlapi/metrics.go一个需要读取并记录的表盘读数GaugeInt64ObservableGauge/Float64ObservableGaugeate.actor.stats.memory.working_setcmd/atelet/statspoller.go几条经验法则有多少优先用 observable 形式observable 回调在采集时读取真值缓存、map因此漏掉的递减不会让值漂移从数据源消失的组也会从导出中消失。当变化发生在请求路径上、且最后递减后序列保留末值可以接受时如 router 的 parked-request 计数用同步 UpDownCounter当你要复制一份已经拥有的数据结构时才用 observable 形式。UpDownCounter 还是 Gauge你在计数还是在读表盘UpDownCounter 是你自己维护的计数某件事开始就加一结束就减一。ate.workerpool.workers是 UpDownCounter因为 ateapi 负责分配和释放每个 worker它是那个维护计数的人。Gauge 是你读取的表盘memory working set 是 atelet 轮询 sandbox 得到的值gVisor 上是 cgroupmicro-VM 上是 guest agent按模板记录。问自己这个数是我加减出来的还是我看到的答案就是 Instrument 类型。dashboard 之后如何聚合与此无关——类型是对管道关于数据点性质的承诺rollup 和 actor relay 会在不检查的情况下按此承诺行事。不要在一个 success counter 旁边再加一个 failure counter一个 Instrument 承载成败失败挂在error.type或ate.failure.reason上键的缺席即成功。见下文报告失败。一个直方图、多个阶段而不是每个阶段一个直方图当阶段共享维度时。ate.actor.restore.duration用ate.snapshot.phase作为标签让所有阶段落在同一张图上。命名Instrument 名称使用点号分隔的小写substrate 全局概念用ate.subsystem.noun如ate.actor.crashes、ate.imagecache.requests指标描述组件自身机制时用component.subsystem.noun如atenet.router.parking.active、atelet.snapshot.size。拿不准就用ate.*以组件开头会把指标绑定到那个二进制同一测量若在第二个组件中出现就需要第二个名字。命名被测的事物而不是聚合duration、size、requests、workers。单位放在 Instrument 的 unit 字段每个 exporter 按自己的方式渲染kind 上的 Prometheus exporter 会追加_seconds、_bytes、_total而 Cloud Monitoring 保留 OpenTelemetry 名称、只追加_bucket、_count、_sum随附 dashboard 查询atelet.snapshot.size_bucket。不要在名称里放单位或聚合词否则某个后端会显示两次。存在上游语义约定指标时直接复用rpc.server.call.duration原样来自otelgrpc。上游有形态但没有对应概念时在ate.*名下镜像该形态ate.workerpool.desired_workers与ate.workerpool.ready_workers遵循k8s.deployment.desired_pods和k8s.deployment.available_pods——是两个 Instrument 而不是一个按状态打标签的 Instrument因为 desired 加 ready 不是求和。存在上游属性模式时遵循它*.operation.name、*.duration、error.type。原样复用上游属性而不是把它别名化进ate.*error.type与file.name都是原样使用的。属于子系统的标签键以子系统为根ate.imagecache.outcome而不是放在actor下——当每个 actor 都共享该标签描述的事物时。单位与桶Units and buckets使用 UCUM 单位与仓库其余部分一致量单位Go 值时间stime.Since(start).Seconds()为float64大小Byint64字节事物的计数花括号{thing}{request}、{worker}、{crash}int64比率1UCUM目前树内尚无 Instrument 使用float64即使对快路径也记录秒而不是毫秒分辨率由桶承载而跨 Instrument 混用单位会破坏每个做除法运算的 dashboard。每个直方图必须显式设置桶边界。SDK 默认值0, 5, 10, 25 … 10000是为毫秒设计的对以秒记录的值是错的0 到 5 秒之间的一切都会落进同一个桶。要选择覆盖所见两端的边界并在注释中写明这两端是什么。量可比时复用已有集合量树内使用的边界生命周期操作0.005 … 30cmd/ateapi/internal/controlapi/metrics.go调度步骤0.0005 … 5同一文件快照阶段0.005 … 60cmd/atelet/metrics.go 中的snapshotPhaseBuckets请求等待0.001 … 60cmd/atenet/internal/router/ingress/metrics.go字节大小1e6 … 1e10cmd/atelet/main.go小计数worker 数0, 1, 2, 3, 5, 10, 20, 50, 100, 250cmd/ateapi/internal/scheduling/metrics.go以ate.actor.restore.duration为例实际实现cmd/atelet/metrics.go中的桶边界是// snapshotPhaseBuckets have to cover both ends of a phase breakdown: a warm OCI // unpack or a local rename lands in single-digit milliseconds, while a cold node // fetching a multi-GiB snapshot runs for tens of seconds. var snapshotPhaseBuckets []float64{0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 20, 30, 60}注册表条目会在annotations.substrate.buckets下重复这些边界让读者不用读代码就能找到。标签与基数Labels and cardinality规则集中在 docs/metrics/substrate.yaml 的cardinality_rules下。每个新指标都必须满足以下规则禁止 actor 身份ate.actor.name、ate.actor.uid、ate.atespace、ate.actor.version、ate.actor.container.name永远不能是指标标签。它们属于 span 和日志记录。这是让序列数量与历史上存在过多少 actor无关的规则。有界或目录作用域每个ate.*标签要么是带固定值列表的枚举要么命名 operator 创建的对象模板、池。指标标签上的任何值都不得来自请求负载、错误消息、主机名或路径。在生产端归一化当值来自网络或未经校验的文件时在记录前把它映射到有界集合上ateattr.NormalizeSandboxClass、ateattr.NormalizeOperationName、ateattr.SnapshotScopeValue。无法识别的值上报unknown绝不报告原始字符串。这些归一化函数都实现在 internal/ateattr/ateattr.go 中例如NormalizeSandboxClass会把快照清单里未经校验的 class 折叠到gvisor、microvm或unknown。不知道的标签就省略而不是空着发一个在选中 worker 之前就失败的 resume 没有池池键应缺席而不是。ateattr.WorkerPoolAttributes在这种情况下返回 nil。成对键一起出现ate.workerpool.namespace与ate.workerpool.nameate.failure.reason与ate.failure.domain。使用返回成对的ateattr辅助函数ateattr.FailureAttributes就是一个它同时返回 reason 与由 reason 严格推导出的 domain。每个标签键都是 internal/ateattr/ateattr.go 中的常量每个有界值集合都是它旁边的常量组。指标绝不在本地声明attribute.Key(...)唯一例外的 Instrumentrouter 的 parkingoutcome标签早于该规则被记录在substrate.yaml的lint_exceptions下。所以顺序是先把键和值加进ateattr注册表条目与代码便按构造达成一致。加标签前先问 dashboard 会按什么分组。一个没人会去分组或过滤的标签只会白白翻倍序列数。三四个标签是常态标签最多的 restore 直方图有八个且其中几个是有条件的。SDK 还有硬限制每个 Instrument 2000 个不同的属性集合按 reader 持有OTEL_GO_X_CARDINALITY_LIMIT可修改未设置即 2000。超过后每个新组合都被静默折叠进一个只带otel.metric.overflowtrue的序列。把你标签的值数量相乘让乘积远低于该值。该限制作用于单进程内因此 atelet 上的 per-template 标签受限于单节点托管的模板数而不是整个集群。报告失败Reporting failures一个 Instrument 同时承载成功与失败。成功即失败键的缺席。用哪个键取决于错误在何处被分类error.type用于 RPC 边界携带 gRPC 状态码status.Code(err).String()如ate.actor.lifecycle.operation.duration所做。也用于有界协议状态集如ate.imagecache.requests用一个 HTTP 状态码 allow-list 加_OTHER兜底。ate.failure.reason与ate.failure.domain成对当失败属于 substrate 自身的 reason 分类internal/ateerrors时。使用ateattr.FailureAttributes(reason)绝不手工设置这两个键。这是 atelet 与 ateom handler 内的正确选择——那里的 gRPC 状态只在 handler 返回后才被赋值读出来会是Unknown。把调用方放弃与失败分开context.Canceled与context.DeadlineExceeded是 outcome 标签上自己的结果cancelled、timeout不是error。而且必须记录它们一次被取消的 pull 仍然运行过。把请求上下文原样传给Add和Record无论是否已取消——SDK 从不检查ctx.Err()所以什么都不会被丢弃它从上下文里唯一读取的是 span以便 exemplar 指向被采样的 trace。internal/imagecache/metrics.go是这套做法的完整实现failureOutcome先读 cancellation被放弃的请求也携带 transport error否则会被误读为 registry 故障errorType只对reportedStatuses401/403/404/429/500/502/503/504内的状态返回具体码其余一律返回_OTHER从而把标签钉在有界集合内。编写 Go 代码结构形态Shape把包内所有 Instrument 放进一个metrics.go包含Instrument 名称常量、一个 Instrument 结构体、一个接收metric.Meter的构造函数、以及未导出的 nil 安全record*方法。internal/imagecache、cmd/atelet、cmd/ateapi/internal/controlapi 都遵循此形态。核心样板如下const requestsMetric ate.snapshotcache.requests // Instruments holds the snapshot caches instruments. A nil *Instruments is a // valid no-op, so call sites need no guard. type Instruments struct { requests metric.Int64Counter } func NewInstruments(meter metric.Meter) (*Instruments, error) { requests, err : meter.Int64Counter( requestsMetric, metric.WithUnit({request}), metric.WithDescription(Number of snapshot lookups in the node-local snapshot cache, by outcome.), ) if err ! nil { return nil, fmt.Errorf(create %s counter: %w, requestsMetric, err) } return Instruments{requests: requests}, nil } // recordRequest counts one lookup. kind is known before the lookup starts, so // the label is present on every outcome, which is what lets the registry mark // it required. failureOutcome and errorType are the same two helpers as in // internal/imagecache/metrics.go. func (i *Instruments) recordRequest(ctx context.Context, kind, outcome string, err error) { if i nil || i.requests nil { return } if err ! nil { outcome failureOutcome(err) } attrs : []attribute.KeyValue{ ateattr.SnapshotCacheOutcomeKey.String(outcome), ateattr.SnapshotKindKey.String(kind), } if outcome ateattr.SnapshotCacheOutcomeError { attrs append(attrs, ateattr.ErrorTypeKey.String(errorType(err))) } i.requests.Add(ctx, 1, metric.WithAttributes(attrs...)) }nil 安全至关重要测试、benchmark 和无指标部署都会在无 Instrument 的情况下构造子系统调用点必须保持无条件。注册表中标记为required的每个标签都必须在 record 方法的每条路径上设置。这里kind是参数而非事后推导所以 miss、hit、failure 都携带它。如果某标签只在部分路径可知在注册表中把它标为conditionally_required并在其他路径省略它而不要空着发。获取 Meter把 meter 作为依赖注入不要在库包深处调用otel.Meter。二进制的主函数传入otel.Meter(component)组件名作为 scopeatelet、ateapi、atecontroller带 options 结构体的包增加一个WithMeter(metric.Meter) Option如internal/imagecache在main中只构造一次的包接收在那里构建的*Instruments如cmd/atelet。Meter provider 每个二进制只设置一次由serverboot的三个函数之一完成internal/serverboot/serverboot.goserverboot.InitMetricsPrometheus reader 加 OTLP pushserverboot.InitMetricsPushOnly仅 OTLP pushatecontrollerserverboot.InitMetricsPushOnlyVia通过 atelet relay 走 OTLP pushateom。新组件调用其中一个并 deferShutdownProvider已有组件内的新包无需在此添加任何东西。可观测 InstrumentObservable instruments对可在采集时枚举的值注册回调而不是维护增量。这是完整示例中的 size Instrumentconst sizeMetric ate.snapshotcache.size // The cache owns its index and the bytes per kind partition it, so this is // an UpDownCounter and not a gauge; observable, because the index is the // truth to read at collection time. size, err : meter.Int64ObservableUpDownCounter(sizeMetric, metric.WithUnit(By), metric.WithDescription(Bytes of snapshots held in the node-local cache, by snapshot kind.)) ... _, err meter.RegisterCallback(func(_ context.Context, o metric.Observer) error { for kind, n : range cache.bytesByKind() { o.ObserveInt64(size, n, metric.WithAttributes(ateattr.SnapshotKindKey.String(kind))) } return nil }, size)来自 ateapiRegisterWorkerCount的两个习惯cmd/ateapi/internal/controlapi/metrics.go数据源不可用时返回 nil、本周期不观测任何值而不是观测 0为警报依赖的序列显式播种一个 0让没有空闲 worker是0而不是缺失序列。回调要尽量廉价、尽量无锁——它每次采集都会运行kind 上每 10 秒一次、生产环境每 60 秒一次每次 Prometheus 抓取也会运行。statspollercmd/atelet/statspoller.go用原子指针发布预计算的快照latest atomic.Pointer[map[templateKey]*templateAggregate]回调只读它。测试通过ManualReader测试绝不通过全局 provider那会让测试产生顺序依赖并阻塞t.Parallel。模式见 cmd/atelet/metrics_test.go 与 internal/imagecache/metrics_test.gofunc newTestInstruments(t *testing.T) (*Instruments, *sdkmetric.ManualReader) { t.Helper() reader : sdkmetric.NewManualReader() mp : sdkmetric.NewMeterProvider(sdkmetric.WithReader(reader)) inst, err : NewInstruments(mp.Meter(atelet)) if err ! nil { t.Fatalf(NewInstruments: %v, err) } return inst, reader }然后reader.Collect(ctx, rm)并遍历rm.ScopeMetrics[*].Metrics按名称找到 Instrument。断言名称与单位这里的笔误会静默破坏每个 dashboard数据类型counter 是metricdata.Sum[int64]且IsMonotonic为 true时长直方图是metricdata.Histogram[float64]gauge 是Gauge每个序列的标签集合包括某标签应在场缺席时确实缺席成功时的error.type、分配前的池对值归一化未知的线上值落为unknown被取消的调用落为cancelled而非error。要测试维持基数有界的行为而不只是快乐路径。image cache 测试喂入一个未列出状态的 registry 错误并断言_OTHER。指标在 kind 上存在后e2e 指标套件internal/e2e/suites/metrics可以通过抓取 collector 的 Prometheus 端点e2e.ScrapeCollectorMetrics断言它到达了 collector。当指标是 operator 会设警报的对象时把它加入套件检查的列表。注册Registering itdocs/metrics/registry/metrics.yaml该注册表是一个OpenTelemetry Weaver 语义约定注册表是 substrate 发出内容的唯一权威清单hack/verify/metrics.sh在 CI 中运行weaver registry check -r docs/metrics/registry验证它本地 weaver 仅当恰好是 v0.25.1 时可用其他版本会报错否则走 docker 中的固定镜像。两类条目属性attribute放在其子系统的attribute_group中每个允许值作为一个成员。把新组放在registry.ate.imagecache旁边- id: registry.ate.snapshotcache type: attribute_group brief: The labels of the snapshot cache on the node. The key starts with the name of the subsystem and not with actor: every actor on the node shares it. attributes: - id: ate.snapshotcache.outcome stability: development brief: The result of one snapshot lookup. Calculate the hit ratio as hit / (hit miss). Do not put the failures or the stopped lookups in the denominator. type: members: - id: hit stability: development value: hit brief: The node holds the snapshot. - id: miss stability: development value: miss brief: The lookup must download the snapshot. - id: error stability: development value: error brief: The lookup failed. Only this outcome has an error.type key. - id: cancelled stability: development value: cancelled brief: The caller stopped the lookup. - id: timeout stability: development value: timeout brief: The time limit of the caller ended.指标metric放在其组件对应的 section- id: metric.ate.snapshotcache.requests type: metric metric_name: ate.snapshotcache.requests instrument: counter unit: {request} stability: development brief: The number of snapshot lookups in the snapshot cache on the node, by outcome. note: A miss causes a download. Thus the hit ratio of a node is an early sign of the resume time. annotations: substrate: emitted_by: [atelet] golden_signals: [latency, errors] code_anchor: cmd/atelet/internal/snapshotcache/metrics.go cuj: Resumes became slower on one node. Does the snapshot cache miss? attributes: - ref: ate.snapshotcache.outcome requirement_level: required - ref: ate.snapshot.kind requirement_level: required - ref: error.type requirement_level: conditionally_required: The outcome is error.annotations.substrate块是 substrate 自有、Weaver 会原样透传的emitted_by列出二进制code_anchor指向创建该 Instrument 的文件cuj是 operator 用它回答的问题golden_signals说明它服务四个黄金信号中的哪些latency、traffic、errors、saturation把适用的都列上这样 dashboard 作者不用读每个 brief 就能找到饱和度 Instrumentbuckets是直方图边界。所有适用字段都要填包括直方图的buckets——注册表读者不应需要看代码。已存在的属性ate.template.name、ate.snapshot.kind、error.type、ate.failure.reason用ref:引用而非重定义。若代码在某些路径省略某标签标为conditionally_required并说明条件而不是required。然后运行 CI 同款检查hack/verify/metrics.sh # local weaver only if it is exactly v0.25.1 (another version is an error), else the pinned image via dockerdocs/metrics/substrate.yaml当新指标填补了某个列出的blind_spot删除该条目、包含 Weaver 无法表达的规则加到cardinality_rules并说明什么能强制它、或有意打破某个约定加lint_exceptions条目并写明原因时更新此文件。该文件当前记录了六条基数规则no-actor-identity、bounded-or-catalog-scoped、error-type-not-parallel-counters、failure-keys-paired、workload-domain-is-a-report、pool-keys-paired、两条 lint 例外以及若干盲区store、worker cache、actor population、image cache cost and eviction、podcertcontroller、golden snapshot pipeline、router shutdown outcome并注明每条规则目前尚无机器强制执行——它由人和 agent 阅读。docs/observability.md在 2. Metrics 下的指标表中加一行名称、发出组件、Instrument 类型、一句话说明测量内容并列出标签。若标签语义需要超过一句话如快照标签那样在表格下方加一小段而不是拉长表格行。DashboardsCloud Monitoring dashboard 位于tools/setup-gcp/dashboards/。新指标不必在同一 PR 内新增 dashboard但 operator 会设警报的指标应在后续 PR 中加面板且 PR 描述应说明该面板回答什么问题。完整示例节点本地快照缓存假设在 atelet 中为快照下载前加一个缓存想回答缓存有没有用、一次 miss 花多少钱、它占多少磁盘。这需要四个 Instrument而不是一个问题Instrument标签有用吗ate.snapshotcache.requestscounter按 outcomeate.snapshotcache.outcome、ate.snapshot.kind、error 时加error.typemiss 花多少钱ate.snapshotcache.fill.duration直方图秒与cmd/atelet/metrics.go中snapshotPhaseBuckets相同的边界那里未导出需复制或提取ate.snapshot.kind、ate.template.atespace、ate.template.name失败时加 failure 对占多少磁盘ate.snapshotcache.sizeobservable UpDownCounter字节缓存拥有总量且按 kind 分区所以不是 gauge外加ate.snapshotcache.evictionscounterate.snapshot.kindevictions 还带ate.snapshotcache.eviction.reason枚举capacity、ttl、explicit刻意不作为标签的东西快照名称或 digest每个 actor 一个无界、对象存储 URL路径、actor。这个 actor 的 resume 命中了哪个快照这类 per-actor 问题用带ateattr.ActorLogAttrs的日志记录写在 restore 路径上——那里已有Restore timing breakdown。实施步骤按顺序在 internal/ateattr/ateattr.go 中SnapshotCacheOutcomeKey、SnapshotCacheEvictionReasonKey及其值常量。复用SnapshotKindKey、TemplateAtespaceKey、TemplateNameKey、ErrorTypeKey、FailureAttributes。cmd/atelet/internal/snapshotcache/metrics.go单二进制使用的包放在cmd/binary/internal/下Instruments结构体、接收metric.Meter的构造函数、nil 安全的record*方法、在既有锁下读取缓存索引的 size 回调。cmd/atelet/main.go用otel.Meter(atelet)构建 Instruments 并传入缓存。cmd/atelet/internal/snapshotcache/metrics_test.goManualReader一个 miss-then-hit 测试、一个断言error.type只在error上出现的失败测试、一个按 reason 的 eviction 测试、一个 insert 和 evict 后的 size 测试。docs/metrics/registry/metrics.yamlregistry.ate.snapshotcache属性组与四条指标条目运行hack/verify/metrics.sh。docs/metrics/substrate.yaml无需新增。size 与 eviction Instrument 随缓存一起发布所以起点没有盲区那里的image cache cost and eviction条目是 image cache 的缺口与本例无关。docs/observability.md表格四行outcome 语义一小段。关键路径速查标签键与有界值集唯一事实来源internal/ateattr/ateattr.go计数器实现样板失败分类、_OTHER、allow-listinternal/imagecache/metrics.go直方图与阶段桶实现ate.actor.restore.durationcmd/atelet/metrics.go可观测 gauge 与原子快照发布ate.actor.stats.memory.working_setcmd/atelet/statspoller.goobservable UpDownCounter 与 0 播种习惯ate.workerpool.workerscmd/ateapi/internal/controlapi/metrics.go指标注册表与检查命令docs/metrics/registry/metrics.yaml、hack/verify/metrics.sh基数规则、lint 例外与盲区清单docs/metrics/substrate.yaml面向 operator 的指标总表docs/observability.md指标的对偶文档tracingdocs/dev/best-practices/tracing.md赞分享人工智能AI AgentAgent 沙箱云原生容器运行时零信任【免费下载链接】substrateAgent Substrate: the core system项目地址https://gitcode.com/GitHub_Trending/substrate7/substrate点击查看免费下载相关推荐wazero最佳实践指南从开发到生产的完整流程wazero最佳实践指南从开发到生产的完整流程 WebAssembly 运行时 wazero 是一个零依赖的 Go 语言 WebAssembly 运行时让开发工具系统底层3行代码加速卷积神经网络wincnn Winograd最小卷积算法生成器完全入门指南3行代码加速卷积神经网络wincnn Winograd最小卷积算法生成器完全入门指南 wincnn 是一个轻量级 Python 开源模块用于计算 卷积神经网StepCI 最佳实践10个技巧让你的API测试更高效StepCI 最佳实践10个技巧让你的API测试更高效 StepCI 是一个强大的开源 API 质量保证框架专门为开发者和测试工程师提供高效的API测试解决创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表