【TVM 教程】如何在 GPU 上优化卷积

Apache TVM: 开源深度学习编译器栈 Apache TVM是一个开源的深度学习编译器栈,旨在弥合生产力导向的深度学习框架与性能和效率导向的硬件后端之间的差距。它为各种硬件平台提供端到端的编译优化,以提高深度学习模型的性能和效率。TVM项目的愿景是汇集机器学习、编译器和系统架构领域的专家和从业者,共同构建一个可访问、可扩展和自动化的开源框架,以优化当前和新兴的机器学习模型在任何硬件平台上的性能。Apache TVM作为一个强大而灵活的深度学习编译器栈,正在为深度学习模型的高效部署和优化提供关键支持。 阅读详情

Apache TVM 是一个端到端的深度学习编译框架,适用于 CPU、GPU 和各种机器学习加速芯片。更多 TVM 中文文档可访问 →Apache TVM 是一个端到端的深度学习编译框架,适用于 CPU、GPU 和各种机器学习加速芯片。 | Apache TVM 中文站

本教程演示了如何在 TVM 中编写高性能卷积实现。以正方形大小的输入张量和滤波器为例,假设卷积输入的 batch 较大。在此示例中,使用不同的布局来存储数据,以实现更好的数据局部性。缓冲区布局是 HWCN,分别代表高度、宽度、通道、batch。

准备和算法​

对具有 256 个通道和 14 x 14 维度的输入张量使用固定大小。batch size 为 256,卷积过滤器包含 512 个大小为 3 x 3 的过滤器,用步长为 1 和 padding size 为 1 进行卷积。以下代码定义了 TVM 中的卷积算法。

import numpy as np
import tvm
from tvm import te

# 输入和过滤器的大小
batch = 256
in_channel = 256
out_channel = 512
in_size = 14
kernel = 3
pad = 1
stride = 1

# 算法
A = te.placeholder((in_size, in_size, in_channel, batch), name="A")
W = te.placeholder((kernel, kernel, in_channel, out_channel), name="W")
out_size = (in_size - kernel + 2 * pad) // stride + 1
# Pad 输入
Apad = te.compute(
 (in_size + 2 * pad, in_size + 2 * pad, in_channel, batch),
 lambda yy, xx, cc, nn: tvm.tir.if_then_else(
        tvm.tir.all(yy >= pad, yy - pad < in_size, xx >= pad, xx - pad < in_size),
        A[yy - pad, xx - pad, cc, nn],
        tvm.tir.const(0.0, "float32"),
 ),
    name="Apad",
)
# 创建归约变量
rc = te.reduce_axis((0, in_channel), name="rc")
ry = te.reduce_axis((0, kernel), name="ry")
rx = te.reduce_axis((0, kernel), name="rx")
# 计算卷积
B = te.compute(
 (out_size, out_size, out_channel, batch),
 lambda yy, xx, ff, nn: te.sum(
        Apad[yy * stride + ry, xx * stride + rx, rc, nn] * W[ry, rx, rc, ff], axis=[ry, rx, rc]
 ),
    name="B",
)

内存层次结构​

首先指定缓冲区的内存层次结构。下图显示了 GPU 内存层次结构,与 CPU 内存层次结构的重要区别是 GPU 提供了一个称为共享内存的缓存缓冲区,由程序员管理。因此,如何最大化共享内存中的数据重用对于在 GPU 内核中实现高性能至关重要。

在本例中,将 Apad 和 W 加载到缓冲区 AA 和 WW 中(存储在共享内存中)。这些缓冲区稍后将由同一线程块中的所有线程共享以计算卷积,然后每个线程将自己的部分从共享缓冲区加载到它们的本地寄存器 AL 和 WL 中。BL 是输出 B 的本地缓存,也存储在线程本地寄存器中。

# 指定内存层次结构
s = te.create_schedule(B.op)
s[Apad].compute_inline() # compute Apad inline
AA = s.cache_read(Apad, "shared", [B])
WW = s.cache_read(W, "shared", [B])
AL = s.cache_read(AA, "local", [B])
WL = s.cache_read(WW, "local", [B])
BL = s.cache_write(B, "local")

分块​

以下代码将工作负载拆分为线程块和单独的线程,遵循矩阵乘法中的分块方案。如下图所示,给定一个像素坐标(y、x),一个线程块负责计算一个 block_factor x block_factor (64 x 64) 的区域,用于输出通道和 batch。由于共享内存空间的限制,每次只从 Apad 和 B 加载 step x block_factor (8 x 64) 数据到共享内存中的缓冲区。

# 平铺常量
tile = 8
num_thread = 8
block_factor = tile * num_thread
step = 8
vthread = 2

# 获取 GPU 线程索引
block_x = te.thread_axis("blockIdx.x")
block_y = te.thread_axis("blockIdx.y")
block_z = te.thread_axis("blockIdx.z")
thread_x = te.thread_axis((0, num_thread), "threadIdx.x")
thread_y = te.thread_axis((0, num_thread), "threadIdx.y")
thread_xz = te.thread_axis((0, vthread), "vthread", name="vx")
thread_yz = te.thread_axis((0, vthread), "vthread", name="vy")

# split 工作负载
hi, wi, fi, ni = s[B].op.axis
bz = s[B].fuse(hi, wi)
by, fi = s[B].split(fi, factor=block_factor)
bx, ni = s[B].split(ni, factor=block_factor)

# 将迭代变量绑定到 GPU 线程索引
s[B].bind(bz, block_z)
s[B].bind(by, block_y)
s[B].bind(bx, block_x)

虚拟线程分割​

进一步将工作负载从线程块拆分为单个线程。为了避免 memory bank conflict,使用虚拟线程将区域分成 4 个部分,然后平铺成 8x8 的网格。因此,如下图所示,每个线程计算 4 个跨步网格,每个网格的大小为 4 x 4。

tyz, fi = s[B].split(fi, nparts=vthread) # 虚拟线程 split
txz, ni = s[B].split(ni, nparts=vthread) # 虚拟线程 split
ty, fi = s[B].split(fi, nparts=num_thread)
tx, ni = s[B].split(ni, nparts=num_thread)
s[B].reorder(bz, by, bx, tyz, txz, ty, tx, fi, ni)

s[B].bind(tyz, thread_yz)
s[B].bind(txz, thread_xz)
s[B].bind(ty, thread_y)
s[B].bind(tx, thread_x)

协同获取(Cooperative Fetching)​

如前所述,每个时间步长都要将 step x block_factor 数据从 GPU 全局内存传输到共享内存。为了减少每个线程的内存传输,以下代码让同一线程块中的线程协同从全局内存中获取相关数据。

# Schedule BL 本地写入
s[BL].compute_at(s[B], tx)
yi, xi, fi, ni = s[BL].op.axis
ry, rx, rc = s[BL].op.reduce_axis
rco, rci = s[BL].split(rc, factor=step)
s[BL].reorder(rco, ry, rx, rci, fi, ni)

# 将计算附加到迭代变量
s[AA].compute_at(s[BL], rx)
s[WW].compute_at(s[BL], rx)
s[AL].compute_at(s[BL], rci)
s[WL].compute_at(s[BL], rci)

# A 的共享内存负载调度
yi, xi, ci, ni = s[AA].op.axis
ty, ci = s[AA].split(ci, nparts=num_thread)
tx, ni = s[AA].split(ni, nparts=num_thread)
_, ni = s[AA].split(ni, factor=4)
s[AA].reorder(ty, tx, yi, xi, ci, ni)
s[AA].bind(ty, thread_y)
s[AA].bind(tx, thread_x)
s[AA].vectorize(ni) # 向量化内存加载

# W 的共享内存负载调度
yi, xi, ci, fi = s[WW].op.axis
ty, ci = s[WW].split(ci, nparts=num_thread)
tx, fi = s[WW].split(fi, nparts=num_thread)
_, fi = s[WW].split(fi, factor=4)
s[WW].reorder(ty, tx, yi, xi, ci, fi)
s[WW].bind(ty, thread_y)
s[WW].bind(tx, thread_x)
s[WW].vectorize(fi) # 向量化内存加载

生成 CUDA 内核​

最后用 TVM 生成和编译 CUDA 内核,并评估卷积的延迟。

func = tvm.build(s, [A, W, B], "cuda")
dev = tvm.cuda(0)
a_np = np.random.uniform(size=(in_size, in_size, in_channel, batch)).astype(A.dtype)
w_np = np.random.uniform(size=(kernel, kernel, in_channel, out_channel)).astype(W.dtype)
a = tvm.nd.array(a_np, dev)
w = tvm.nd.array(w_np, dev)
b = tvm.nd.array(np.zeros((out_size, out_size, out_channel, batch), dtype=B.dtype), dev)
func(a, w, b)
evaluator = func.time_evaluator(func.entry_name, dev, number=1)
print("Convolution: %f ms" % (evaluator(a, w, b).mean * 1e3))

输出结果:

Convolution: 54.146944 ms

下载 Python 源代码:opt_conv_cuda.py

下载 Jupyter notebook:opt_conv_cuda.ipynb

TVM编译机器学习到 WASM 和 WebGPU TVM编译机器学习到 WASM 和 WebGPU TLDR TVM 深度学习编译器对 WASM 和 WebGPU 的支持。实验表明,TVM 的 WebGPU 后端在将模型部署到 Web 时可以接近原生 GPU 性能。 引论 计算是现代机器学习应用的支柱之一。引入 GPU 以加快深度学习工作量,大大提高了进步速度。鉴于部署机器学习无处不在的需求日益增长,浏览器成为部署智能应用程序的自然场所。 虽然 TensorFlow .js 和 ONNX .js是将机器学习引入浏览器的现有努力,但 Web 版本和本地版本 阅读详情

相关推荐

如何在 GPU 上优化卷积

如何在 GPU 上优化卷积 将演示如何在 TVM 中编写高性能卷积实现。正方形大小的输入张量和过滤器为例,假设卷积的输入具有大batch批量。在这个例子中,使用不同的布局存储数据,实现更好的数据局部性。缓冲区布局是 HWCN,代表高度、宽度、通道、批次。 准备和算法 对具有 256 个通道和 14 x 14 维度的输入张量使用固定大小。批量大小为 256。卷积过滤器包含 512 个大小为 3 x 3 的过滤器。使用步幅大小 1 和padding大小 1 进行卷积。以下代码定义了 TVM 中的卷积算法。 im

吴建明wujianming_110117 996

TVM:简介

TVM:简介概述 Apache TVM 是一个用于 CPU、GPU 和机器学习加速器的开源机器学习编译器框架。它旨在使机器学习工程师能够在任何硬件后端上高效地优化和运行计算。本教程的目的是通过定义和演示关键概念,引导您了解 TVM 的所有主要功能。新用户应该能够从头到尾完成本教程的工作,并能够操作 TVM 进行自动模型优化,同时对 TVM 架构及其工作原理有基本的了解。 内容 介绍 安装 TVM 使用命令行界面编译和优化模型 使用 Python 接口编译和优化模型 使用张量表达式使用运算符 使用模板和 A

weixin_44966641的博客 4758

TVM优化Deep Learning GPU算子

TVM优化Deep Learning GPU算子 高效的深度学习算子是深度学习系统的核心。通常,这些算子很难优化,需要HPC专家付出巨大的努力。 端到端张量IR / DSL堆栈TVM使这一过程变得更加容易。 如何在TVM的帮助下编写高性能GPU运算符内核。本文以深度卷积(即topi.nn.depthwise_conv2d_nchw)为例,并演示如何在tensorflow中改进已经手工优化的CUDA内核。在不同的工作负载下,最终版本比tf-1.2中优化的内核快2到4倍,在启用了算子融合的情况下,最终版本快3到

吴建明wujianming_110117 1460

Apache TVM - 开放深度学习编译器栈

一、关于 Apache TVM 1、项目概览 2、相关链接资源 3、功能特性 二、快速开始 三、参与贡献 其它 参考项目

AI工程化、开源分享、文档翻译、代码笔记 921

TVM在ARM GPU上优化移动深度学习

TVM在ARM GPU上优化移动深度学习 随着深度学习的巨大成功,将深度神经网络部署到移动设备的需求正在迅速增长。与在台式机平台上所做的类似,在移动设备中使用GPU可以提高推理速度和能源效率。但是,大多数现有的深度学习框架都不能很好地支持移动GPU。困难在于移动GPU架构和台式机GPU架构之间的差异。这意味着在移动GPU上进行优化需要付出特殊的努力。繁琐的额外工作最终导致大多数深度学习框架中对移动GPU的支持不佳。 TVM通过引入统一的IR堆栈解决了部署不同硬件的困难,通过该IR堆栈可以轻松完成针对不同硬件

吴建明wujianming_110117 1196

如何在GPU上优化卷积

如何在GPU上优化卷积 本文将演示如何在TVM中编写高性能的卷积实现。以平方大小的输入张量和滤波器为例,并假设卷积的输入量很大。使用不同的布局来存储数据,以实现更好的数据局部性。缓冲区布局为HWCN,代表高度,宽度,通道,批次。 准备和算法 将固定大小用于256通道和14 x 14尺寸的输入张量。批处理大小为256。卷积过滤器包含512个大小为3 x 3的过滤器。对于卷积,使用步幅大小1和填充大小1。以下代码定义了TVM中的卷积算法。 import numpy as np import tvm from t

吴建明wujianming_110117 800

TVM Compiler中文教程:TVM如何生成优化GPU卷积

文章目录TVM如何优化GPU卷积准备和算法内存层次结构分块虚拟线程分裂并发数据获取生成CUDA内核 TVM如何优化GPU卷积 本教程,我们将演示如何在TVM中编写高性能卷积实现。我们使用方形尺寸的输入张量和滤波器作为示例,并假设卷积的输入具有大批量。在此示例中,我们使用不同的布局来存储数据,以实现更好的数据局部性。缓冲区布局为HWCN,代表高度,宽度,通道,批次。 准备和算法 我们使用固定尺寸14...

Mars_WH的专栏 4085

GPU上如何优化卷积

GUP上如何优化卷积 本文将演示如何在TVM中编写高性能卷积实现。我们以平方大小的输入张量和滤波器为例,假设卷积的输入是大批量的。在本例中,使用不同的布局来存储数据,以实现更好的数据局部性。缓冲区布局为HWCN,代表高度、宽度、通道、批次。 Preparation and Algorithm 对于256个通道和14 x 14维的输入张量,使用固定大小。批量大小是256。卷积滤波器包含512个尺寸为3 x 3的滤波器。使用步幅大小1和填充大小1进行卷积。下面的代码定义了TVM中的卷积算法。 import nu

吴建明wujianming_110117 698

TVM Compiler中文教程:TVM使用autotvm调优NVIDIA GPU上的高性能卷积

文章目录TVM使用autotvm调优NVIDIA GPU上的高性能卷积加载依赖库步骤一:定义搜索空间步骤二:搜索空间 TVM使用autotvm调优NVIDIA GPU上的高性能卷积 这是为NVIDIA GPU编写高性能可调模板的高级教程。通过在此模板上运行自动调优器,在许多情况下,我们可以胜过供应商提供的CuDNN库。 加载依赖库 安装步骤与TVM Compiler中文教程:使用TVM编写可调模板...

Mars_WH的专栏 6616

TVM 优化 ARM GPU 上的移动深度学习

TVM 优化 ARM GPU 上的移动深度学习 随着深度学习的巨大成功,将深度神经网络部署到移动设备的需求正在迅速增长。与桌面平台上所做的类似,在移动设备中使用 GPU 既有利于推理速度,也有利于能源效率。但是,大多数现有的深度学习框架并不很好地支持移动 GPU。难点在于移动 GPU 架构和桌面 GPU 架构之间的区别。这意味着在移动 GPU 上进行优化需要特别努力。非平凡的额外工作最终导致移动 GPU 在大多数深度学习框架中支持不力。 TVM 通过引入统一的 IR 堆栈,解决为不同硬件部署的困难,从而轻松

吴建明wujianming_110117 1277

TVM学习教程

目录Step 1:TVM 视频介绍Step 2:研读 TVM 论文Step 3:了解 TVM 官网中可以学习的资料Step 4:上手 TVM CodeStep 5:TVM 其他相关资料 Step 1:TVM 视频介绍 https://www.bilibili.com/video/av29521815/ Step 2:研读 TVM 论文 https://www.usenix.org/system/files/osdi18-chen.pdf Step 3:了解 TVM 官网中可以学习的资料 https://tv

qq_33287871的博客 3077

TVM Compiler中文教程:TVM为NVIDIA GPU自动调优卷积网络

文章目录TVM为NVIDIA GPU自动调优卷积网络加载依赖库定义网络设置调优选项开始调优使用多个设备扩展测量范围 TVM为NVIDIA GPU自动调优卷积网络 针对特定设备和工作负载进行自动调优对于获得最佳性能至关重要。这篇教程是关于TVM如何为NVIDIA GPU调优整个卷积网络。 TVM中NVIDIA GPU的算子实现是以模板template形式编写的。模板有许多可调旋钮knobs(平铺ti...

Mars_WH的专栏 3883

Apache TVM 是一个开放源代码的机器学习编译器框架,用于 CPU,GPU 和机器学习加速器

Apache TVM 是一个开放源代码的机器学习编译器框架,用于 CPU,GPU 和机器学习加速器。Apache TVM 是深度学习系统的编译器栈。它旨在缩小注重生产力的深度学习框架与注重性能和效率的硬件后端之间的差距。TVM 与深度学习框架协同工作,为不同的后端提供端到端的编译。 使用方法请见README.md文档。

使用TVM优化深度学习GPU算子:深度卷积实例

以下内容翻译自:Optimize Deep Learning GPU Operators with TVM: A Depthwise Convolution Example 高效的深度学习算子是深度学习系统的核心。通常这些算子很难优化,并且需要高性能计算专家的努力。TVM,端到端张量IR/DSL堆栈,使得这项任务更容易。 这个博客教你如何在TVM的帮助下编写高性能GPU运算核心。我们使用深度卷...

yiran103的专栏 1万+

强化学习代码实现PPO

梯度更新:通过截断(clipping)限制策略更新幅度,避免策略变化过大影响稳定性。首先是数据预处理,将相关数据(状态、采取的动作、状态奖励、下一个状态)放到gpu上。根据当前状态 state,利用策略网络生成每个动作的概率分布 probs。2. 修改了策略模型的输出,从每个动作的概率分布变成了动作的均值和方差。1. 修改了动作空间,离散空间变成了连续空间,使用正太分布来采样。3. 修改了动作的ratio的计算,从离散分布变成了正太分布计算。Update函数:通过经验缓冲数据来优化策略模型和价值模型。

wlxsp的博客 635

硬件基础(4):(6)高精度ADC设计中模拟地与数字地的划分与连接

在实际 PCB 设计中,**接地与布线**对于获得干净的参考电压和高精度 ADC 测量至关重要。下面详细阐述**如何在硬件层面减少数字噪声耦合到模拟电路、保障参考电压的稳定性**。

FCZlll的博客 2645

【TVM 教程】如何使用 TensorCores 优化卷积

​每个 Tensor Core 都提供了一个 4x4x4 矩阵处理数组,它使得,其中 A、B、C 和 D 是 4x4 矩阵,如图所示。矩阵乘法输入 A 和 B 是 FP16 矩阵,而累加矩阵 C 和 D 可以是 FP16 或 FP32 矩阵。但是,CUDA 开发者只能使用 warp 级原语在张量核上执行 16x16x16 半精度矩阵乘法。调用矩阵乘法之前,开发者必须使用原始显式地将数据从内存加载到寄存器中。NVCC 编译器将该原语转换为多个内存加载指令。

HyperAI超神经 1096

iOS自动化之打包-xcodebuild命令行打包.app并安装到ios模拟器

xcode xcodebuild xcrun ios模拟器 iOS UI自动化测试 ios自动打包安装到ios模拟器

z12347891的博客 5074

【TVM教程】为 GPU 自动调度卷积层

Apache TVM 是一个深度的深度学习编译框架,适用于 CPU、GPU 和各种机器学习加速芯片。更多 TVM 中文文档可访问 →https://tvm.hyper.ai/作者:Lianmin Zheng, Chengfan Jia本文介绍如何为 GPU 使用 auto-scheduler。与 AutoTVM 不同,AutoTVM 依赖手动模板来定义搜索空间,而 auto-scheduler 不需要任何模板。用户只需编写计算声明,无需任何调度命令或模板。auto-scheduler 可以自动生成一个大的搜

HyperAI超神经 972

请叫我鱼拆,2024扫地机器人的第一次拆机

从背面可以看出,这个机器人是两轮差速模型,两个驱动轮,一个万向轮,驱动轮位于机器人中心位置,这样原地旋转的圆心就是机器人的中心。可以看到设备已经连接,但是没有办法进shell,厂家应该有做了安全保护之类的,有知道从这个口可以登录系统办法的小伙伴可以后台留言。但这台机器人一直报警说,机器人轮子离地,小鱼 估计是防跌落模块有问题,误触发了,所以打算先拆机看看。接着小鱼试了下,机器可以开机,然后给他重新配置wifi,连上 米家。Allwinner,全志的片子,不知道哪个型号的,看产品id,应该支持adb。

生活不止眼前的苟且 3931

在UI界面中播放视频_unity基础开发教程

之前我写过一篇在场景中播放视频的文章,但是在开发中有时候也会在UI的界面中播放视频,这期我们做一下在UI的界面中播放视频。操作步骤比较简单,到这里就完成了,如果有问题的话可以在评论区留言,下期见,拜拜!

为者常成,行者常至! 2010

初识 TVM

如有图像或公式显示错误,可以访问我的个人博客:https://www.wanglichun.tech/2019/11/15/tvm/ 笔者也是最近偶然的机会才开始接触TVM,使用过后发现,经过auto-tuning后的TVM模型在速度是竟然超过了TensorRT,并且笔者使用的是MXNet框架,TVM对MXNet绝对的友好,对于Pytorch等模型,可以使用ONNX,操作也一样简单,使用起来基本类...

小菜鸟的AI之路 7152

如何下载安装Qt各个版本?(2025-4-28更新)

本文将教您快速获取Qt各版本安装包!妈妈再也不用担心我找不到Qt安装包了!

程序区 5119

【TVM 教程】在 NVIDIA GPU 上调优高性能卷积

Apache TVM 是一个端到端的深度学习编译框架,适用于 CPU、GPU 和各种机器学习加速芯片。更多 TVM 中文文档可访问 →https://tvm.hyper.ai/ 作者:Lianmin Zheng 本教程介绍如何为 NVIDIA GPU 编写高性能可调模板。通过在此模板上运行自动调优器,可在许多情况下胜过供应商提供的 cuDNN 库。 注意,本教程不会在 Windows 或最新版本的 macOS 上运行。如需运行,请将本教程的主体放在 if name == “main”: 代码块中。 安装依赖

HyperAI超神经 833

【计算机视觉】基于复杂环境下的车牌识别

本文针对复杂环境下(光照变化、车牌变形、噪声干扰)的车牌识别问题,提出了一套完整的优化方案。通过四个核心环节进行改进:1)采用CLAHE、同态滤波等技术优化光照处理;2)使用边缘检测和透视变换应对车牌变形;3)结合中值滤波和暗通道去噪算法消除干扰;4)改进字符分割与识别方法。文章提供了详细的Python实现代码,展示了从预处理到字符识别的完整流程,并通过多策略融合显著提升了复杂场景下的识别准确率。实验结果表明,该方法能有效应对各类复杂环境挑战,为实际应用提供了可靠解决方案。

2401_84149564的博客 1627

基于共识的捆绑算法CBBA在多无人机多任务调度中的实现

在捆绑过程中,每个无人机根据自己的任务清单和其他无人机的任务清单,确定一个被称为“束”的集合。通过合理的任务分配和无人机协作,CBBA算法能够实现最优的任务调度,提高了多无人机系统的效率和执行能力。在每一轮迭代中,无人机根据自己的当前任务和位置信息将任务清单发送给其他无人机,并接收其他无人机发送过来的任务清单。通过比较其他无人机的任务清单和自身的任务清单,每个无人机可以更新自己的任务分配情况。在这一步中,每个无人机根据自己选择的新任务,更新自身的任务清单,并将更新后的清单发送给其他无人机。

持续更新 1156
上一篇: 今晚 19 点,看直播抽奖赢算力!
下一篇: 精准预测美国失业率和贫困率,谷歌人口动态基础模型PDFM已开源,可增强现有地理空间模型
HyperAI超神经
博客等级 码龄7年 8873粉丝 1753原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值