TVM 目标描述与代码生成模块 `tvm.target` 完全指南:Target 字符串、TargetKind 与设备检测

发布时间:2026/9/24 9:00:01
TVM 目标描述与代码生成模块 `tvm.target` 完全指南:Target 字符串、TargetKind 与设备检测 编译器深度学习模型优化【免费下载链接】tvmOpen deep learning compiler stack for cpu, gpu and specialized accelerators项目地址https://gitcode.com/gh_mirrors/tvm7/tvm点击查看免费下载导读tvm.target是 TVM 中描述为谁生成代码的核心 Python 模块编译一个模型到 CPU、GPU 或专用加速器之前必须先用它构造一个目标Target对象把目标架构、交叉编译三元组、CPU 特性、外部库等编译期信息封装起来。本篇文章以 docs/reference/api/python/target.rst 所挂载的tvm.target模块为主体完整讲解 Target 字符串语法与全部选项、Target/TargetKind的构造与能力、常用设备辅助构造函数、Target 上下文管理、标签注册、设备自动检测与 GenericFunc 目标分派并结合 python/tvm/target 下的源码与 tests/python/target 中的测试用例给出可复现的实战示例。读完本文你将能够为任意目标设备写出正确、可维护的 TVM Target 配置并理解其底层如何映射到代码生成后端。tvm.target模块是什么tvm.target是 TVM Python 前端中的Target description and codgen module负责目标的描述与代码生成入口。它并非一个简单的字符串常量而是一组与 C 运行时通过 FFI 绑定的对象与工具函数核心包括Target目标设备信息对象可由目标字符串、配置字典或标签创建TargetKind编译目标的种类如llvm、cuda、opencl每种 Kind 维护一份可用选项表VirtualDevice编译期描述数据存放在哪、代码如何编译的表示GenericFunc可按当前目标进行特化分派的通用函数机制一系列便捷构造函数cuda()、rocm()、mali()、arm_cpu()、hexagon()、riscv_cpu()、stm32()、vta()等目标标签tag注册与列表、从物理设备自动检测目标的工具函数。模块的导出与组织方式见 python/tvm/target/init.py它从target.py导出Target、create、TargetKind及各类设备构造函数从virtual_device.py导出VirtualDevice从compilation_config.py导出make_compilation_config从tag.py导出list_tags从generic_func.py导出GenericFunc与三个装饰器并整体引入datatype与codegen子模块。Target 字符串语法与完整选项列表模块的模块级 docstring见 python/tvm/target/init.py给出了 TVM 目标字符串的权威格式target_kind [-optionvalue]...即以目标种类开头后面跟零个或多个以空格分隔的-选项值键值对。TVM 用同一个字符串同时表达后端种类与该后端的详细配置。选项一览表选项含义与取值-devicedevice name设备名称例如mali、arm_cpu、intel_graphics、adreno。-mtripletarget triple目标三元组用于交叉编译例如arm64-linux-android、aarch64-linux-gnu。-mcpucpuname指定当前架构下具体芯片默认根据目标三元组推断并自动检测当前架构例如cortex-a72、cortex-m4。-mattra1,a2,-a3,...覆盖或控制目标具体特性如是否启用 SIMD 运算默认特性集由当前 CPU 决定例如neon、avx512f、-hvx。-mabiabi按指定 ABI 生成代码例如lp64dRISC-V 64 位双精度浮点 ABI、ilp32。-system-lib构建 TVM 系统库模块。系统库是一个全局模块包含程序启动时自我注册的函数可通过tvm.runtime.system_lib获取适用于dlopen等动态加载 API 被禁止的环境只要结果代码被程序链接系统库即可用。需要注意-mattr中的表示启用该特性-表示禁用多个特性用逗号分隔。-mcpu、-mtriple、-mabi等选项最终会传递给 LLVM 后端参与代码生成决策。字典形式配置除了字符串Target构造函数还接受配置字典或 JSON 字符串。字典形式下各字段与字符串选项一一对应详见 python/tvm/target/target.py 的 docstring字典字段类型说明kindstr必填使用的代码生成路径例如llvm或cuda。keysList[str]可选可被分派到的一组策略键。例如使用kindopencl时可设keys[mali, opencl, gpu]。devicestr可选对应实际运行设备的单个键会被追加到keys之后。libsList[str]可选使用的外部库集合例如[cblas, mkl]。system-libbool可选为 True 时构建包含自我注册函数的模块用于禁止dlopen等动态加载的环境。mcpustr可选实际运行的 CPU仅作注解。modelstr可选说明工作负载来源模型的注解。runtimestr可选说明工作负载使用哪个运行时的注解。mtriplestr可选描述目标的 LLVM 三元组例如arm64-linux-android。mattrList[str]可选编译所用的 LLVM 特性例如[avx512f, mmx]。mfloat-abistr可选LLVM 设置取hard或soft表示使用硬件还是软件浮点运算。mabistr可选LLVM 设置按指定 ABI 生成代码例如lp64d。hostUnion[str, Dict]可选目标宿主机host的描述可以递归地采用与 target 相同的形式。此外Target构造函数还支持host位置参数第二个参数其取值形式与 target 相同用于同时指定目标设备 运行该设备驱动的宿主机。字符串与字典的等价示例import tvm # 字符串形式 t1 tvm.target.Target(cuda -archsm_80 -libscublas,cudnn) # 字典形式等价 t2 tvm.target.Target({ kind: cuda, arch: sm_80, libs: [cublas, cudnn], }) # 同时指定 host t3 tvm.target.Target(cuda, hostllvm -mtriplex86_64-linux-gnu)tests/python/target/test_target_target.py中的test_target_string_parse与test_target_string_with_spaces分别验证了cuda -modelunknown -libscublas,cudnn这类字符串解析以及含空格的 target 往返序列化str(target)后再次构造的正确性。TargetKind目标种类与其选项注册表TargetKind是编译目标的种类由tvm._ffi.register_object注册为 TVM Object见 python/tvm/target/target.py。它有两个静态/实例方法TargetKind.options返回当前 kind 可用选项名到类型的字典TargetKind.options_from_name(kind_name)按名称字符串获取某 kind 的选项字典。C 侧在 src/target/target_kind.cc 通过TVM_REGISTER_TARGET_KIND注册了全部内置种类每个种类关联一个 DLPack 设备类型常量种类设备类型说明llvmkDLCPULLVM 通用 CPU 后端最常用ckDLCPU纯 C 代码生成microTVM 等场景cudakDLCUDANVIDIA CUDA 后端nvptxkDLCUDAPTX 汇编后端rocmkDLROCMAMD ROCm 后端openclkDLOpenCLOpenCL 后端metalkDLMetalApple Metal 后端vulkankDLVulkanVulkan 后端webgpukDLWebGPUWebGPU 后端sdaccelkDLOpenCLXilinx SDAccelaocl/aocl_sw_emukDLAOCLIntel FPGA OpenCLhexagonkDLHexagonQualcomm Hexagon DSPstackvmkDLCPU虚拟机栈后端ext_devkDLExtDev外部设备如 VTAhybridkDLCPU混合执行后端compositekDLCPU复合目标testkDLCPU测试用目标Target.list_kinds()会返回当前 TVM 构建中所有可用种类名称的列表tests/python/target/test_target_target.py中的test_target_dispatch等用例会遍历它逐一构造 Target 来验证所有种类都能被正确解析。Target 对象构造、属性与规范化工具三种构造方式按 python/tvm/target/target.pyTarget(target, hostNone)可以从三类输入构造原始目标字符串如cuda、llvm -mtripleaarch64-linux-gnu配置字典如上文的字典字段表目标标签预注册或自定义的标签名见下文目标标签小节。构造时会先经过convert把 str/dict 转成 TVM 内部Map/String表示再由_ffi_api.Target构造底层对象传入非法类型会抛出ValueError(target has to be a string or dictionary.)。若host非空则用Target(target)与Target(host)两个子对象一起构造。常用属性Target提供了一系列属性用于查询目标特性见 python/tvm/target/target.pyarchCUDA 架构字符串如sm_80取自attrs[arch]max_num_threads/max_block_size_x/max_block_size_y/thread_warp_size/max_shared_memory_per_block线程与共享内存限制max_function_args最大函数参数个数vtcm_capacityHexagon VTCM 容量字节0 表示无限制device_name设备名model设备型号默认unknownmcpu/mattrCPU 型号与特性列表libs外部库列表supports_integer_dot_product是否支持整数点积。对 CUDA当sm_version 61时返回 True即 Pascal 之后支持dp4a等指令supports_cooperative_matrix是否支持协作矩阵cooperative matrixl2_cache_size_bytesL2 缓存大小featuresTargetFeatures对象可用target.features.name查询 LLVM 特性支持情况见tests/python/target/test_llvm_features_info.py的test_target_features。这些属性在调度schedule与代码生成时被用来决定线程束大小、共享内存预算、向量宽度等关键参数。规范化canonicalization静态方法Target还提供一组静态方法把各种目标类对象统一成规范形式是编译入口的公共基础设施Target.canon_target(target)把None、既有Target、字符串如cuda -archsm_80或字典如{kind: cuda, arch: sm_80}统一转成TargetTarget.canon_target_and_host(target, target_hostNone)同时规范目标与宿主机若target_host非空会警告其即将弃用并建议改用tvm.target.Target(target, hosttarget_host)Target.canon_multi_target(multi_targets)把一个目标类对象、目标列表/Array 或设备类型到目标的字典遗留的异构目标表示键被忽略转成Target数组Target.canon_multi_target_and_host(target, target_hostNone)多目标 host 的规范化Target.canon_target_map_and_host(target_map, target_hostNone)把目标 → IRModule映射的键规范化并把 host 固化到每个键中Target.target_or_current(target)若传入None则取当前上下文目标Target.current()仍为空则抛ValueError。另外Target.from_device(device)会从物理设备反查出对应 Target见设备自动检测小节。序列化与宿主设置Target.export()导出目标的 JSON 字符串表示Target.with_host(hostNone)返回带指定宿主机的新 Target 对象Target.current(allow_noneTrue)返回当前上下文中的目标配合with使用allow_noneFalse时未设置会抛异常。设备便捷构造函数从辅助函数到完整 Targettvm.target提供了一系列语义化构造函数比手写 Target 字符串更不易出错。这些函数都通过_merge_opts合并用户附加选项后构造Targettvm.target.create()已标记弃用请直接使用Target构造器见 python/tvm/target/target.py。GPU 相关函数生成的 Target说明cuda(modelunknown, archNone, optionsNone)cuda -model... -arch...未指定arch时会警告Try specifying cuda arch by adding archsm_xx to your target.因为架构直接影响 kernel 生成质量rocm(modelunknown, optionsNone)rocm -model...AMD ROCm 目标mali(modelunknown, optionsNone)opencl -devicemali -model...ARM Mali GPU 目标底层走 OpenCLintel_graphics(modelunknown, optionsNone)opencl -deviceintel_graphics -model... -thread_warp_size16Intel 核显OpenCL 后端且线程束大小固定 16bifrost(modelunknown, optionsNone)opencl -devicebifrost -model...Mali Bifrost 架构 GPUadreno(modelunknown, optionsNone)opencl -deviceadreno -model...Qualcomm Adreno GPUCUDA 的arch参数例如sm_61对应 GTX 1080 Ti 等 Pascal 卡cuda()函数在 python/tvm/target/target.py 中实现tests/python/target/test_target_target.py的test_target_string_parse验证了cuda -modelunknown -libscublas,cudnn的解析。CPU 相关arm_cpu(modelunknown, optionsNone)内置了常见开发板/手机型号到完整选项的映射表python/tvm/target/target.py型号展开的选项pixel2-modelsnapdragon835 -mtriplearm64-linux-android -mattrneonmate10/mate10pro/p20/p20pro-modelkirin970 -mtriplearm64-linux-android -mattrneonrasp3b-modelbcm2837 -mtriplearmv7l-linux-gnueabihf -mattrneonrasp4b-modelbcm2711 -mtriplearmv8l-linux-gnueabihf -mattrneon -mcpucortex-a72rasp4b64-modelbcm2711 -mtripleaarch64-linux-gnu -mattrneon -mcpucortex-a72rk3399/ultra96/thunderx对应-mtripleaarch64-linux-gnu及各自-mcpupynq-modelpynq -mtriplearmv7a-linux-eabi -mattrneonbeagleai-modelbeagleai -mtriplearmv7a-linux-gnueabihf -mattrneon,vfp4,thumb2 -mcpucortex-a15stm32mp1-modelstm32mp1 -mtriplearmv7a-linux-gnueabihf -mattrneon,vfp4,thumb2 -mcpucortex-a7最终统一生成llvm -keysarm_cpu,cpu -devicearm_cpu ...形式的目标。rasp(options)已弃用应改调arm_cpu(rasp3b)。riscv_cpu(modelsifive-u54, optionsNone)内置 SiFive 系列配置python/tvm/target/target.pysifive-e31riscv32-unknown-linux-gnu-mabiilp32、sifive-e76同上、sifive-u54riscv64-unknown-linux-gnu-mabilp64d默认、sifive-u74同上。这些 ABI 与 GCC 的-marchrv64gc -mabilp64d惯例对应。stm32(seriesunknown, optionsNone)面向 STM32 全系列 MCUpython/tvm/target/target.pySTM32_SUPPORTED_SERIES覆盖高性能H7xx/F7xx/F4xx/F2xx、主流G0xx/F0xx/F1xx/G4xx/F3xx与低功耗U5xx/L5xx/L4xx/L1xx/L0xx系列映射到c -keysarm_cpu,cpu -devicearm_cpu -mcpucortex-mX形式的 C 代码生成目标。专用设备hexagon(cpu_verv68, **kwargs)Qualcomm Hexagon DSP 目标python/tvm/target/target.py合法 CPU 版本为v65/v66/v67/v67t/v68/v69/v71/v73/v75。关键字参数包括hvxHVX 向量字节数默认 1280 禁用 HVX 代码生成合法值[0, 64, 128]、llvm_options、use_qfloat默认 v68 及以上为 True即启用 HVX QFloat 指令、use_ieee_fp默认 False、num_cores默认 4meta scheduler 需要、vtcm_capacity默认按版本取 0.25MB~8MB。例如生成的 LLVM 目标串形如-mtriplehexagon -mcpuhexagonv68 -mattrhvxv68,hvx-length128bv68 还会默认附加-force-hvx-float以支持自动向量化。注意HVX 浮点支持需要 LLVM 14。micro(modelunknown, optionsNone)microTVM 目标MICRO_SUPPORTED_MODELS覆盖atsamd51、esp32、imxrt10xx、mps2_an521、mps3_an547、nrf52840、nrf5340dk、rp2040、sam3x8e、stm32f746xx、stm32h7xx、stm32l4r5zi、stm32u5xx、zynq_mp_r5等板卡生成c -mcpu... -model...形式目标未知型号直接抛ValueError。vta(modelunknown, optionsNone)VTA 加速器生成ext_dev -devicevta -keysvta,cpu -model...。这些构造函数在 python/tvm/target/init.py 中被集中导出是日常使用最频繁的入口。Target 上下文管理with与Target.current()Target实现了上下文管理器协议python/tvm/target/target.pywith tvm.target.Target(cuda): print(tvm.target.Target.current()) # 进入作用域后即为 cuda 目标进入with块时调用_ffi_api.TargetEnterScope退出时调用TargetExitScope。这一机制让 Relay/Relax 编译、GenericFunc 分派、算子策略选择等在当前目标上下文中自动读取目标信息。tests/python/target/test_target_target.py的test_target_dispatch与test_target_temp_strategy验证了作用域内切换目标时行为一致且可嵌套回退。在编译接口中Target.target_or_current(target)则是显式传入优先否则取当前上下文的便捷入口。设备自动检测从物理设备到 TargetTarget.from_device(device)调用 python/tvm/target/detect_target.py 中的detect_target_from_device把运行时的物理设备对象反查为 Target。支持的设备类型为[cuda, metal, rocm, vulkan, opencl, cpu]SUPPORT_DEVICE字典python/tvm/target/detect_target.py。各设备的检测策略CUDA_detect_cuda读取设备的max_shared_memory_per_block、max_threads_per_block、warp_size并把compute_version如8.0转换为arch: sm_80Metalmax_shared_memory_per_block固定 32768其余从设备读取ROCmmtriple: amdgcn-amd-amdhsa-hcc其余从设备读取OpenCL从设备读取三个容量字段Vulkan通过device_api.vulkan.get_target_property查询supports_float16、supports_int8、supports_int16、supports_int64、supports_8bit_buffer、supports_16bit_buffer、supports_storage_buffer_storage_class等能力并写入 Target 属性CPU通过tvm.codegen.llvm.GetDefaultTargetTriple与tvm.codegen.llvm.GetHostCPUName获取宿主机的默认三元组与 CPU 名。若设备类型不支持自动检测或设备不存在驱动未装、TVM 未编译对应驱动会分别抛出带明确提示的ValueError。import tvm # 假设本机有可用的 CUDA 设备 # target tvm.target.Target.from_device(tvm.device(cuda, 0)) # 或在已持有 device 对象时直接反查目标标签Target Tag复用预定义目标标签机制让用户用简短名称引用预定义目标python/tvm/target/tag.pyimport tvm from tvm.target.tag import register_tag, list_tags # 注册自定义标签 register_tag(nvidia/gtx1080ti, config{ kind: cuda, arch: sm_61, }) # 查看全部标签 tags list_tags() # 返回 {名称: Target} target tvm.target.Target(nvidia/gtx1080ti)register_tag(name, config, overrideFalse)把名称映射到配置字典overrideFalse时重复注册同名标签会抛异常。模块加载时已预注册nvidia/gtx1080ticuda sm_61并在加载期调用list_tags()校验所有标签的正确性。C 侧实现见 src/target/tag.cc 的target.TargetTagAddTag与target.TargetTagListTags标签注册表刻意维护在 C 侧以支持纯 C 使用场景Python API 仅用于快速原型。list_tags()在 runtime-only 构建下返回None。GenericFunc按目标分派的通用函数GenericFunc是 TVM 的目标感知函数机制同一个函数名可根据当前目标上下文分派到不同实现python/tvm/target/generic_func.py。tvm.target.generic_func装饰器import tvm tvm.target.generic_func def my_func(a): return a 1 my_func.register(cuda) def my_func_cuda(a): return a 2 print(my_func(2)) # 3默认实现 with tvm.target.cuda(): print(my_func(2)) # 4分派到 cuda 特化其分派逻辑为取Target.current()若为None则调用默认函数否则按target.keys的顺序在分派字典中查找匹配键命中即调用特化实现未命中回退默认实现。register(key, funcNone, overrideFalse)支持传入字符串或字符串列表作为分派键overrideFalse时重复注册抛ValueError。装饰后的函数还暴露get_packed_func()返回当前目标下的分派函数。tvm.target.override_native_generic_func用于覆盖 C 侧已注册的 GenericFunc 默认实现tvm.target.override_native_generic_func(my_func) def my_func(a): return a 1 my_func.register(cuda) def my_func_cuda(a): return a 2C 侧通过generic_func.cc的注册表保存全局 GenericFuncPython 侧get_native_generic_func(name)获取不存在则新建然后set_default(fdefault, allow_overrideTrue)覆盖默认实现register(key, func, overrideTrue)注册分派特化。注意调用 generic_func 时不支持关键字参数会抛RuntimeError。这一机制是tvm.target中算子/内建函数按设备特化的关键基础设施例如tir内建函数在不同后端上有不同实现时会通过 GenericFunc 注册到cuda、opencl、vulkan等键下。关联对象VirtualDevice 与 CompilationConfigVirtualDevicepython/tvm/target/virtual_device.py是编译期的数据放置 编译方式表示from tvm.target import VirtualDevice import tvm # device 为空时使用无约束设备device type 与 id 均为 -1 vd VirtualDevice(devicetvm.device(cuda, 0), targetNone, memory_scopeglobal) print(vd.device_type)它由VirtualDevice_ForDeviceTargetAndMemoryScope构造描述数据在运行时存放在哪个设备、用哪个 target 编译其计算、使用什么内存作用域memory_scope如global、shared。这在异构编译与内存规划场景中用于把算子与设备/存储绑定。make_compilation_config(ctxt, target, target_hostNone)python/tvm/target/compilation_config.py则通过Target.canon_multi_target_and_host规范化目标后构造CompilationConfig主要用于单元测试模拟标准编译接口的表示约定。综合实战为真实模型配置目标并编译下面组合前述知识演示一个完整的 Relay 编译流程以tests/python/target/test_target_target.py的用例风格为参考import tvm from tvm import relay # 1. 构造目标CUDA 指定架构 外部库 target tvm.target.Target(cuda -archsm_80 -libscublas,cudnn) print(target.kind.name) # cuda print(target.arch) # sm_80 print(target.libs) # [cublas, cudnn] # 2. 用便捷函数构造 ARM 交叉编译目标 arm_target tvm.target.arm_cpu(rasp4b) print(arm_target) # llvm -keysarm_cpu,cpu -devicearm_cpu -modelbcm2711 ... # 3. 编译时使用 Target显式传入省略时可用 with 上下文 mod, params relay.testing.synthetic.get_workload() with tvm.transform.PassContext(opt_level3): lib relay.build(mod, targettarget, paramsparams) # 4. 从物理设备自动反查目标需设备可用 # detected tvm.target.Target.from_device(tvm.device(cuda, 0)) # 5. 运行时按目标分派 generic func with tvm.target.Target(cuda): lib.export_library(model.so)要点回顾始终显式指定关键选项CUDA 务必给archsm_xx否则会收到警告且生成的 kernel 可能不是最优交叉编译三件套-mtriple、-mcpu、-mattr决定生成代码的指令集-mabi/-mfloat-abi决定 ABI 兼容性优先用便捷构造函数arm_cpu(rasp4b)、riscv_cpu(sifive-u54)、stm32(stm32F4xx)等自带经过验证的默认配置比手写字符串更可靠host 与 target 分离异构场景用Target(target, host...)明确谁在算、谁在管。测试与验证线索本模块的行为在 tests/python/target 下有系统性覆盖可作为进一步研究入口test_target_target.py字符串解析、含空格目标往返、LLVM-cl-opt与-jit选项、list_kinds()、目标分派与临时策略、create弃用警告等test_llvm_features_info.pytarget.features查询 LLVM 特性支持含-mcpudummy这类非法 CPU 的错误路径test_arm_target.pyARM 目标在with tvm.target.Target(arm_target)上下文中的行为test_target_parser_mprofile.pyCortex-M 系列-mcpucortex-m55/m7/m3的解析与-mattrnomve等特性覆盖。结语tvm.target是 TVM 编译栈的目标总入口从target_kind [-optionvalue]...的字符串语法、Target/TargetKind对象与属性到覆盖 CUDA/ROCm/OpenCL/ARM/RISC-V/Hexagon/STM32/microTVM 的便捷构造函数再到上下文管理、标签复用、设备自动检测与 GenericFunc 目标分派它把为谁生成代码这一编译问题完整地封装为可组合、可验证的 Python API。掌握本文所述的字符串与字典两种配置方式、常用选项语义及规范化工具你就能在 TVM 中为任何目标设备写出正确且高效的编译配置。赞分享编译器深度学习模型优化【免费下载链接】tvmOpen deep learning compiler stack for cpu, gpu and specialized accelerators项目地址https://gitcode.com/gh_mirrors/tvm7/tvm点击查看免费下载相关推荐TVM Target API 完全指南tvm.target 与 Target Tag 的构造、配置与源码解析TVM Target API 完全指南tvm.target 与 Target Tag 的构造、配置与源码解析 导读 在 Apache TVMOpen Mac模型编译深度学习推理引擎TVM 设备与目标交互机制深度解析DeviceAPI、Target 与代码生成器TVM 设备与目标交互机制深度解析DeviceAPI、Target 与代码生成器 导读 本文以 TVM 运行时架构文档 docs/arch/device_t编译器深度学习模型优化TVM Device/Target 交互机制全解析DeviceAPI、Target 与代码生成器实现指南TVM Device/Target 交互机制全解析DeviceAPI、Target 与代码生成器实现指南 导读 本文以 TVM 官方架构文档 docs/arc模型编译深度学习推理引擎创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询