当前位置: 首页 > news >正文

TVM调度原语完全指南:从入门到微架构级优化

调度原语

在TVM的抽象体系中,调度(Schedule)是对计算过程的时空重塑。每一个原语都是改变计算次序、数据流向或并行策略的手术刀。其核心作用可归纳为:

优化目标 = max ⁡ ( 计算密度 内存延迟 × 指令开销 ) \text{优化目标} = \max \left( \frac{\text{计算密度}}{\text{内存延迟} \times \text{指令开销}} \right) 优化目标=max(内存延迟×指令开销计算密度)

下面我们将解剖20+个核心原语,揭示它们的运作机制与优化场景。


基础维度操作

1. split:维度的量子裂变

作用:将单个维度拆分为多个子维度,为后续优化创造空间

# 将长度128的维度拆分为(外轴, 内轴)=(16, 8)  
outer, inner = s[op].split(op.axis[0], factor=8)  
# 或者指定外层大小  
outer, inner = s[op].split(op.axis[0], nparts=16)  '''  
数学等价转换:  
原始迭代: for i in 0..127  
拆分后: for i_outer in 0..15  for i_inner in 0..7  i = i_outer * 8 + i_inner  
'''  

硬件视角

  • 当处理256-bit SIMD寄存器时,拆分成8个float32元素的分块可完美利用向量化
  • 在L1缓存为32KB的CPU上,拆分后的子块应满足:
    子块大小 × 数据类型大小 ≤ 32768 B \text{子块大小} \times \text{数据类型大小} \leq 32768B 子块大小×数据类型大小32768B

2. fuse:维度的熔合反应

作用:合并多个连续维度,简化循环结构

fused = s[op].fuse(op.axis[0], op.axis[1])  
'''  
数学等价:  
原始: for i in 0..15  for j in 0..31  
合并后: for fused in 0..511 (16*32=512)  
'''  

优化场景

  • 当相邻维度具有相同优化策略时,减少循环嵌套层数
  • 与parallel原语配合实现粗粒度并行
  • 案例:将H和W维度融合后做分块,更适合GPU线程块划分

3. reorder:维度的空间折叠

作用:重新排列循环轴的顺序

s[op].reorder(op.axis[2], op.axis[0], op.axis[1])  
'''  
原始顺序: axis0 -> axis1 -> axis2  
调整后: axis2 -> axis0 -> axis1  
'''  

硬件敏感优化

  • 将内存连续访问的维度置于内层循环
# 将通道维度移到最内层以利用向量化  
s[conv].reorder(n, h, w, c)  
  • 在GPU上将块索引维度提前以提升局部性
s[matmul].reorder(block_idx, thread_idx, inner)  

并行化武器库

4. parallel:多核并发的起搏器

作用:标记循环轴进行多线程并行

s[op].parallel(op.axis[0])  

实现机制

  • 在LLVM后端会生成OpenMP pragma指令
#pragma omp parallel for  
for (int i = 0; i < N; ++i)  

黄金法则

  • 并行粒度不宜过细(避免线程创建开销)
  • 每个线程的任务量应大于10μs
  • 案例:对batch维度做并行,每个线程处理不同样本

5. vectorize:SIMD的激活密钥

作用:将内层循环转换为向量化指令

s[op].vectorize(inner_axis)  

代码生成示例
原始标量计算:

for (int i = 0; i < 8; ++i)  C[i] = A[i] + B[i];  

向量化后(AVX2):

__m256 va = _mm256_load_ps(A);  
__m256 vb = _mm256_load_ps(B);  
__m256 vc = _mm256_add_ps(va, vb);  
_mm256_store_ps(C, vc);  

性能临界点

  • 向量化收益公式:
    加速比 = min ⁡ ( 元素数 向量宽度 , 内存带宽 ) \text{加速比} = \min\left(\frac{\text{元素数}}{\text{向量宽度}}, \text{内存带宽}\right) 加速比=min(向量宽度元素数,内存带宽)
  • 当循环长度不是向量宽度整数倍时,需尾部处理

6. bind:硬件线程的映射协议

作用:将循环轴绑定到硬件线程索引

block_x = tvm.thread_axis("blockIdx.x")  
s[op].bind(op.axis[0], block_x)  

GPU编程范式

  • blockIdx.x:GPU线程块索引
  • threadIdx.x:块内线程索引
  • 典型绑定策略:
    bx = tvm.thread_axis("blockIdx.x")  
    tx = tvm.thread_axis("threadIdx.x")  
    s[matmul].bind(s[matmul].op.axis[0], bx)  
    s[matmul].bind(s[matmul].op.axis[1], tx)  
    

CPU-GPU差异

  • CPU:通常绑定到OpenMP线程
  • GPU:需要精确管理线程层次结构

内存优化原语

7. compute_at:计算的时空折叠

作用:将一个阶段的计算插入到另一个阶段的指定位置

s[producer].compute_at(s[consumer], consumer_axis)  

优化效果

  • 提升数据局部性,减少中间结果存储
  • 案例:在卷积计算中,将输入加载插入到输出通道循环内

8. storage_align:内存对齐的标尺

作用:调整张量存储的内存对齐

s[op].storage_align(axis, factor, offset)  

底层原理

  • 确保数据地址满足:
    address % factor = = offset \text{address} \% \text{factor} == \text{offset} address%factor==offset
  • 典型用例:
    # 对齐到64字节边界(适合AVX-512)  
    s[input].storage_align(axis=2, factor=64, offset=0)  
    

性能影响

  • 对齐错误可导致性能下降10倍以上
  • 现代CPU对非对齐访问的惩罚已减小,但SIMD指令仍需对齐

9. cache_read/cache_write:数据的时空驿站

作用:创建数据的临时缓存副本

AA = s.cache_read(A, "shared", [B])  

GPU优化案例

# 将全局内存数据缓存到共享内存  
s[AA].compute_at(s[B], bx)  
s[AA].bind(s[AA].op.axis[0], tx)  

缓存层次选择

缓存类型硬件对应延迟周期
“local”寄存器1
“shared”GPU共享内存10-20
“global”设备内存200-400

循环优化原语

10. unroll:循环展开的时空折叠

作用:将循环体复制多份,消除分支预测开销

s[op].unroll(inner_axis)  

代码生成对比
原始循环:

for (int i = 0; i < 4; ++i) {  C[i] = A[i] + B[i];  
}  

展开后:

C[0] = A[0] + B[0];  
C[1] = A[1] + B[1];  
C[2] = A[2] + B[2];  
C[3] = A[3] + B[3];  

收益递减点

  • 循环体过大会导致指令缓存压力
  • 经验公式:
    最佳展开因子 = L1 ICache Size 循环体代码大小 \text{最佳展开因子} = \sqrt{\frac{\text{L1 ICache Size}}{\text{循环体代码大小}}} 最佳展开因子=循环体代码大小L1 ICache Size

11. pragma:编译器的微观调控

作用:插入特定编译指导语句

s[op].pragma(axis, "unroll_and_jam", 4)  

常见Pragma指令

# 强制向量化  
s[op].pragma(axis, "vectorize", 8)  # 流水线并行  
s[op].pragma(axis, "software_pipeline", 3)  # 内存预取  
s[op].pragma(axis, "prefetch", A)  

架构特定优化

  • Intel CPU:
    s[op].pragma(axis, "ivdep")  # 忽略向量依赖  
    
  • NVIDIA GPU:
    s[op].pragma(axis, "ldg", 1)  # 使用__ldg指令  
    

张量计算原语

12. tensorize:硬件指令的直通车

作用:将计算模式映射到特定硬件指令

# 定义矩阵内积的Tensorize内核  
def dot_product_4x4():  # 此处定义计算规则  pass  s[matmul].tensorize(ci, dot_product_4x4)  

硬件案例

  • Intel VNNI:4x4矩阵乘指令
  • NVIDIA Tensor Core:混合精度矩阵运算
  • ARM SVE:可伸缩向量扩展

性能收益

  • 在兼容硬件上可获得10-100倍加速
  • 需要精确匹配计算模式和数据布局

高级组合原语

13. rfactor:归约计算的时空分裂

作用:将归约操作分解为多阶段计算

# 原始归约  
C = tvm.compute((n,), lambda i: tvm.sum(A[i,j], axis=j))  # 创建rfactor阶段  
_, ki = s[C].split(s[C].op.reduce_axis[0], factor=4)  
Crf = s.rfactor(C, ki)  

数学等价性
原始:
C [ i ] = ∑ j = 0 15 A [ i , j ] C[i] = \sum_{j=0}^{15} A[i,j] C[i]=j=015A[i,j]
分解后:
C r f [ i , k ] = ∑ j = 0 3 A [ i , 4 k + j ] C [ i ] = ∑ k = 0 3 C r f [ i , k ] Crf[i,k] = \sum_{j=0}^{3} A[i,4k+j] \\ C[i] = \sum_{k=0}^{3} Crf[i,k] Crf[i,k]=j=03A[i,4k+j]C[i]=k=03Crf[i,k]

优化场景

  • 提升归约操作的并行度
  • 减少原子操作冲突(GPU)

14. compute_inline:计算的时空湮灭

作用:将中间计算结果直接内联到消费者

s[B].compute_inline()  

代码变换
内联前:

B = A + 1  
C = B * 2  

内联后:

C = (A + 1) * 2  

权衡分析

  • 优点:减少内存占用,提升局部性
  • 缺点:可能增加重复计算量

架构特定原语

15. stencil:数据流动的模板

作用:定义滑动窗口式计算模式

with tvm.stencil.grid([H, W]) as [i, j]:  B[i,j] = A[i-1,j] + A[i+1,j] + A[i,j-1] + A[i,j+1]  

硬件映射

  • FPGA:生成流水线化数据流
  • GPU:映射到共享内存的滑窗缓存
  • CPU:自动生成SIMD优化代码

16. sparse:稀疏数据的压缩艺术

作用:处理稀疏张量计算

# 定义CSR格式稀疏矩阵  
indptr = tvm.placeholder((n+1,), dtype="int32")  
indices = tvm.placeholder((nnz,), dtype="int32")  
data = tvm.placeholder((nnz,), dtype="float32")  # 稀疏矩阵乘调度  
s = tvm.create_schedule([indptr, indices, data, dense])  
s.sparse_indices(indptr, indices)  

优化技巧

  • 使用行分块减少随机访问
  • 利用向量化处理非零元素
  • 案例:在Transformer模型中优化稀疏注意力计算

调试与剖析原语

17. debug:计算图的显微镜

作用:输出中间计算步骤详情

s[op].debug()  

输出示例

Compute stage:  for (i, 0, 16) {  for (j, 0, 32) {  C[i, j] = (A[i, j] + B[i, j])  }  }  

调试技巧

  • 结合TVM的Lower函数查看IR变更
  • 使用LLDB/GDB附加到编译过程

18. profile:性能的时空计量仪

作用:插入性能剖析代码

s[op].profile()  

输出信息

  • 循环迭代次数
  • 缓存命中率
  • 指令吞吐量
  • 案例:发现某个循环存在90%的缓存未命中

未来原语展望

19. auto_tensorize:AI优化AI

作用:自动匹配硬件指令模式

s.auto_tensorize(target="avx512")  

实现原理

  • 使用机器学习模型识别可优化的计算模式
  • 自动生成tensorize内核

20. quantum:量子计算接口

作用:映射到量子计算指令

s[op].quantum(gate="H", qubits=[0,1])  

前沿领域

  • 量子神经网络优化
  • 混合经典-量子调度

原语组合艺术

优化案例:三维卷积调度策略

# 定义计算  
data = tvm.placeholder((N, C, D, H, W), "float32")  
kernel = tvm.placeholder((K, C, KD, KH, KW), "float32")  
conv3d = topi.nn.conv3d_ndhwc(data, kernel)  # 创建调度  
s = tvm.create_schedule(conv3d.op)  # 分块策略  
n, d, h, w, k = conv3d.op.axis  
dn, di = s[conv3d].split(d, factor=2)  
hn, hi = s[conv3d].split(h, factor=4)  
wn, wi = s[conv3d].split(w, factor=4)  
s[conv3d].reorder(n, dn, hn, wn, di, hi, wi, k)  # 并行化  
s[conv3d].parallel(n)  # 向量化  
s[conv3d].vectorize(wi)  # 缓存优化  
AA = s.cache_read(data, "local", [conv3d])  
WW = s.cache_read(kernel, "local", [conv3d])  
s[AA].compute_at(s[conv3d], wn)  
s[WW].compute_at(s[conv3d], wn)  # 指令级优化  
s[conv3d].unroll(hi)  
s[conv3d].pragma(dn, "prefetch", AA)  

结语:调度原语的哲学

在TVM的世界里,每一个调度原语都是时空的雕塑工具。优秀的性能工程师需要兼具:

  • 微观直觉:理解每个原语在硬件底层的映射
  • 宏观视野:把握多个原语之间的相互作用
  • 艺术感知:在约束条件下找到优雅的优化路径

正如计算机图形学中的渲染方程,调度优化也是一个积分过程:

最优性能 = ∫ 硬件空间 ∏ 原语 f ( x ) d x \text{最优性能} = \int_{\text{硬件空间}} \prod_{\text{原语}} f(x) \, dx 最优性能=硬件空间原语f(x)dx

愿每一位读者都能在TVM的调度世界中,找到属于自己的优化之美。

相关文章:

TVM调度原语完全指南:从入门到微架构级优化

调度原语 在TVM的抽象体系中&#xff0c;调度&#xff08;Schedule&#xff09;是对计算过程的时空重塑。每一个原语都是改变计算次序、数据流向或并行策略的手术刀。其核心作用可归纳为&#xff1a; 优化目标 max ⁡ ( 计算密度 内存延迟 指令开销 ) \text{优化目标} \max…...

c语言进阶(简单的函数 数组 指针 预处理 文件 结构体)

c语言补充 格式 void函数头 {} 中的是函数体 sum函数名 &#xff08;&#xff09; 参数表 #include <stdio.h>void sum(int begin, int end) {int i;int sum 0;for (i begin ; i < end ; i) {sum i;}printf("%d到%d的和是%d\n", begin, end, sum); …...

终极版已激活!绿话纯净,打开即用!!!

今天我想和大家聊聊一个非常实用的工具——视频转换大师最终版。 视频转换大师终极版&#xff0c;堪称一款全能型的视频制作神器&#xff0c;集视频转换与编辑功能于一体。它搭载的视频增强器技术&#xff0c;能够最大限度地保留原始视频质量&#xff0c;甚至还能实现质量的进…...

Vue.js 什么是 Composition API?

Vue.js 什么是 Composition API&#xff1f; 今天我们来聊聊 Vue 3 引入的一个重要特性&#xff1a;组合式 API&#xff08;Composition API&#xff09;。如果你曾在开发复杂的 Vue 组件时感到代码难以维护&#xff0c;那么组合式 API 可能正是你需要的工具。 什么是组合式 …...

MySQL高可用

一、mysql路由 1.利用路由器的连接路由特性&#xff0c;用户可以编写应用程序来连接到路由器&#xff0c;并令路由器使用响应的路由策略来处理连接来使其连接到正确的mysql数据库服务器 2.mysql route的部署方式 需要在所有数据库主机之外再打开一台主机mysql-router 配置mysql…...

30.Word:设计并制作新年贺卡以及标签【30】

目录 NO1.2 NO3邮件合并-信函 NO4邮件合并-标签​ NO1.2 另存为/F12&#xff1a;考生文件夹&#xff1a;Word.docx布局→页面设置对话框→页边距&#xff1a;上下左右→纸张&#xff1a;宽度/高度&#xff08;先调页边距&#x1f197;&#xff09;设计→页面颜色→填充效果→…...

Flink2支持提交StreamGraph到Flink集群

最近研究Flink源码的时候&#xff0c;发现Flink已经支持提交StreamGraph到集群了&#xff0c;替换掉了原来的提交JobGraph。 新增ExecutionPlan接口&#xff0c;将JobGraph和StreamGraph作为实现。 Flink集群Dispatcher也进行了修改&#xff0c;从JobGraph改成了接口Executio…...

大模型本地化部署(Ollama + Open-WebUI)

文章目录 环境准备下载Ollama模型下载下载Open-WebUI 本地化部署的Web图形化界面本地模型联网查询安装 Docker安装 SearXNG本地模型联网查询 环境准备 下载Ollama 下载地址&#xff1a;Ollama网址 安装完成后&#xff0c;命令行里执行命令 ollama -v查看是否安装成功。安装成…...

C++哈希(链地址法)(二)详解

文章目录 1.开放地址法1.1key不能取模的问题1.1.1将字符串转为整型1.1.2将日期类转为整型 2.哈希函数2.1乘法散列法&#xff08;了解&#xff09;2.2全域散列法&#xff08;了解&#xff09; 3.处理哈希冲突3.1线性探测&#xff08;挨着找&#xff09;3.2二次探测&#xff08;跳…...

IME关于输入法横屏全屏显示问题-Android14

IME关于输入法横屏全屏显示问题-Android14 1、输入法全屏模式updateFullscreenMode1.1 全屏模式判断1.2 全屏模式布局设置 2、应用侧关闭输入法全屏模式2.1 调用输入法的应用设置flag2.2 继承InputMethodService.java的输入法应用覆盖onEvaluateFullscreenMode方法 InputMethod…...

网络工程师 (11)软件生命周期与开发模型

一、软件生命周期 前言 软件生命周期&#xff0c;也称为软件开发周期或软件开发生命周期&#xff0c;是指从软件项目的启动到软件不再被使用为止的整个期间。这个过程可以细分为多个阶段&#xff0c;每个阶段都有其特定的目标、任务和产出物。 1. 问题定义与需求分析 问题定义…...

【人工智能】基于Python的机器翻译系统,从RNN到Transformer的演进与实现

《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 机器翻译(Machine Translation, MT)作为自然语言处理领域的重要应用之一,近年来受到了广泛的关注。在本篇文章中,我们将详细探讨如何使…...

网络工程师 (12)软件开发与测试

一、软件设计 &#xff08;一&#xff09;定义与目的 软件设计是从软件需求出发&#xff0c;设计软件的整体结构、功能模块、实现算法及编写代码的过程&#xff0c;旨在确定系统如何完成预定任务。其目标是确保目标系统能够抽象、普遍地完成预定任务&#xff0c;并为后续的软件…...

3.Spring-事务

一、隔离级别&#xff1a; 脏读&#xff1a; 一个事务访问到另外一个事务未提交的数据。 不可重复读&#xff1a; 事务内多次查询相同条件返回的结果不同。 幻读&#xff1a; 一个事务在前后两次查询同一个范围的时候&#xff0c;后一次查询看到了前一次查询没有看到的行。 二…...

Python字典详解:从入门到实践

Python字典详解&#xff1a;从入门到实践 字典&#xff08;Dictionary&#xff09;是Python中最重要且最常用的数据结构之一。本文将深入讲解字典的特性、操作方法和实际应用案例。 1. 字典简介 字典是可变的、无序的键值对集合&#xff0c;使用{}创建。每个元素由key: valu…...

91,【7】 攻防世界 web fileclude

进入靶场 <?php // 包含 flag.php 文件 include("flag.php");// 以高亮语法显示当前文件&#xff08;即包含这段代码的 PHP 文件&#xff09;的内容 // 方便查看当前代码结构和逻辑&#xff0c;常用于调试或给解题者提示代码信息 highlight_file(__FILE__);// 检…...

41【文件名的编码规则】

我们在学习的过程中&#xff0c;写出数据或读取数据时需要考虑编码类型 火山采用&#xff1a;UTF-16 易语言采用&#xff1a;GBK php采用&#xff1a;UTF-8 那么我们写出的文件名应该是何种编码的&#xff1f;比如火山程序向本地写出一个“测试.txt”&#xff0c;理论上这个“测…...

蓝桥杯备赛经验帖

蓝桥杯备赛经验帖 作者&#xff1a;blue 时间&#xff1a;2025.2.1 文章目录 蓝桥杯备赛经验帖1.为什么有这篇文章2.赛制3.比赛流程4.如何准备5.其他建议6.一些感悟 1.为什么有这篇文章 ​ 笔者近期发现&#xff0c;观看我写的两道第十五届蓝桥杯题解的人数逐渐增多&#xf…...

一文大白话讲清楚webpack基本使用——17——Tree Shaking

文章目录 一文大白话讲清楚webpack基本使用——17——Tree Shaking1. 建议按文章顺序从头看&#xff0c;一看到底&#xff0c;豁然开朗2. 啥叫Tree Shaking3. 什么是死代码&#xff0c;怎么来的3. Tree Shaking的流程3.1 标记3.2 利用Terser摇起来 4. 具体使用方式4.1 适用前提…...

【C++ 区间位运算】3209. 子数组按位与值为 K 的数目|2050

本文涉及知识点 位运算、状态压缩、枚举子集汇总 LeetCode3209. 子数组按位与值为 K 的数目 给你一个整数数组 nums 和一个整数 k &#xff0c;请你返回 nums 中有多少个子数组 满足&#xff1a;子数组中所有元素按位 AND 的结果为 k 。 示例 1&#xff1a; 输入&#xff1a…...

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…...

【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器

一.自适应梯度算法Adagrad概述 Adagrad&#xff08;Adaptive Gradient Algorithm&#xff09;是一种自适应学习率的优化算法&#xff0c;由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率&#xff0c;适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)

概述 在 Swift 开发语言中&#xff0c;各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过&#xff0c;在涉及到多个子类派生于基类进行多态模拟的场景下&#xff0c;…...

解锁数据库简洁之道:FastAPI与SQLModel实战指南

在构建现代Web应用程序时&#xff0c;与数据库的交互无疑是核心环节。虽然传统的数据库操作方式&#xff08;如直接编写SQL语句与psycopg2交互&#xff09;赋予了我们精细的控制权&#xff0c;但在面对日益复杂的业务逻辑和快速迭代的需求时&#xff0c;这种方式的开发效率和可…...

《通信之道——从微积分到 5G》读书总结

第1章 绪 论 1.1 这是一本什么样的书 通信技术&#xff0c;说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号&#xff08;调制&#xff09; 把信息从信号中抽取出来&am…...

linux 下常用变更-8

1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行&#xff0c;YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID&#xff1a; YW3…...

【MATLAB代码】基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),附源代码|订阅专栏后可直接查看

文章所述的代码实现了基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),针对传感器观测数据中存在的脉冲型异常噪声问题,通过非线性加权机制提升滤波器的抗干扰能力。代码通过对比传统KF与MCC-KF在含异常值场景下的表现,验证了后者在状态估计鲁棒性方面的显著优…...

jmeter聚合报告中参数详解

sample、average、min、max、90%line、95%line,99%line、Error错误率、吞吐量Thoughput、KB/sec每秒传输的数据量 sample&#xff08;样本数&#xff09; 表示测试中发送的请求数量&#xff0c;即测试执行了多少次请求。 单位&#xff0c;以个或者次数表示。 示例&#xff1a;…...

tomcat指定使用的jdk版本

说明 有时候需要对tomcat配置指定的jdk版本号&#xff0c;此时&#xff0c;我们可以通过以下方式进行配置 设置方式 找到tomcat的bin目录中的setclasspath.bat。如果是linux系统则是setclasspath.sh set JAVA_HOMEC:\Program Files\Java\jdk8 set JRE_HOMEC:\Program Files…...

Xcode 16 集成 cocoapods 报错

基于 Xcode 16 新建工程项目&#xff0c;集成 cocoapods 执行 pod init 报错 ### Error RuntimeError - PBXGroup attempted to initialize an object with unknown ISA PBXFileSystemSynchronizedRootGroup from attributes: {"isa">"PBXFileSystemSynchro…...