AI编译器技术实践:MLIR与TVM的跨硬件模型优化部署

# AI编译器技术实践:MLIR与TVM的跨硬件模型优化部署


在深度学习模型日益复杂的今天,如何实现模型在不同硬件平台的高性能部署成为关键挑战。MLIR多级中间表示和TVM张量编译器技术栈,为AI模型的跨平台优化提供了系统化的解决方案。


## MLIR架构设计与中间表示


MLIR通过分层中间表示和可扩展的方言系统,支持从高级图表示到底层硬件指令的渐进式优化。


```mlir

// resnet50.mlir - MLIR中间表示示例

module {

  // 定义Tensor类型

  !tensor_type = tensor<1x224x224x3xf32>

  !weight_type = tensor<7x7x3x64xf32>

  

  // 函数定义

  func @resnet50(%input: !tensor_type) -> !tensor_type {

    // 卷积层

    %conv_weights = "tosa.const"() {value = dense<...> : tensor<7x7x3x64xf32>} : () -> !weight_type

    

    %conv = "tosa.conv2d"(%input, %conv_weights) {

      pad = [3, 3, 3, 3],

      stride = [2, 2],

      dilation = [1, 1]

    } : (!tensor_type, !weight_type) -> tensor<1x112x112x64xf32>

    

    // 批归一化层

    %bn_gamma = "tosa.const"() {value = dense<1.0> : tensor<64xf32>} : () -> tensor<64xf32>

    %bn_beta = "tosa.const"() {value = dense<0.0> : tensor<64xf32>} : () -> tensor<64xf32>

    

    %bn = "tosa.batch_norm"(%conv, %bn_gamma, %bn_beta) {

      epsilon = 1.0e-5 : f32

    } : (tensor<1x112x112x64xf32>, tensor<64xf32>, tensor<64xf32>) -> tensor<1x112x112x64xf32>

    

    // ReLU激活

    %relu = "tosa.relu"(%bn) : (tensor<1x112x112x64xf32>) -> tensor<1x112x112x64xf32>

    

    // 最大池化

    %pool = "tosa.max_pool2d"(%relu) {

      kernel = [3, 3],

      stride = [2, 2],

      pad = [1, 1, 1, 1]

    } : (tensor<1x112x112x64xf32>) -> tensor<1x56x56x64xf32>

    

    // 残差块

    %residual_block = call @residual_block(%pool) : (tensor<1x56x56x64xf32>) -> tensor<1x56x56x64xf32>

    

    return %residual_block : tensor<1x56x56x64xf32>

  }

  

  // 残差块定义

  func @residual_block(%input: tensor<1x56x56x64xf32>) -> tensor<1x56x56x64xf32> {

    %conv1_weights = "tosa.const"() {value = dense<...> : tensor<1x1x64x64xf32>} : () -> tensor<1x1x64x64xf32>

    %conv1 = "tosa.conv2d"(%input, %conv1_weights) {

      pad = [0, 0, 0, 0],

      stride = [1, 1]

    } : (tensor<1x56x56x64xf32>, tensor<1x1x64x64xf32>) -> tensor<1x56x56x64xf32>

    

    %conv2_weights = "tosa.const"() {value = dense<...> : tensor<3x3x64x64xf32>} : () -> tensor<3x3x64x64xf32>

    %conv2 = "tosa.conv2d"(%conv1, %conv2_weights) {

      pad = [1, 1, 1, 1],

      stride = [1, 1]

    } : (tensor<1x56x56x64xf32>, tensor<3x3x64x64xf32>) -> tensor<1x56x56x64xf32>

    

    %conv3_weights = "tosa.const"() {value = dense<...> : tensor<1x1x64x256xf32>} : () -> tensor<1x1x64x256xf32>

    %conv3 = "tosa.conv2d"(%conv2, %conv3_weights) {

      pad = [0, 0, 0, 0],

      stride = [1, 1]

    } : (tensor<1x56x56x64xf32>, tensor<1x1x64x256xf32>) -> tensor<1x56x56x256xf32>

    

    // 跳跃连接

    %shortcut_weights = "tosa.const"() {value = dense<...> : tensor<1x1x64x256xf32>} : () -> tensor<1x1x64x256xf32>

    %shortcut = "tosa.conv2d"(%input, %shortcut_weights) {

      pad = [0, 0, 0, 0],

      stride = [1, 1]

    } : (tensor<1x56x56x64xf32>, tensor<1x1x64x256xf32>) -> tensor<1x56x56x256xf32>

    

    // 元素相加

    %add = "tosa.add"(%conv3, %shortcut) : (tensor<1x56x56x256xf32>, tensor<1x56x56x256xf32>) -> tensor<1x56x56x256xf32>

    

    %final_relu = "tosa.relu"(%add) : (tensor<1x56x56x256xf32>) -> tensor<1x56x56x256xf32>

    

    return %final_relu : tensor<1x56x56x256xf32>

  }

}

```


## TVM张量编译器架构


TVM通过自动调度生成和硬件感知优化,实现深度学习模型在各种硬件后端的高性能执行。


```python

# tvm_model_optimization.py - TVM模型优化与部署

import tvm

from tvm import relay, auto_scheduler

from tvm.contrib import graph_executor

import numpy as np


class TVMModelCompiler:

    def __init__(self, target="llvm -mcpu=core-avx2"):

        """初始化TVM编译器"""

        self.target = tvm.target.Target(target)

        self.ctx = tvm.device(self.target.kind.name, 0)

        

    def import_from_onnx(self, onnx_path):

        """从ONNX导入模型"""

        import onnx

        

        # 加载ONNX模型

        >

        

        # 提取输入信息

        input_name = onnx_model.graph.input[0].name

        input_shape = tuple(dim.dim_value for dim in onnx_model.graph.input[0].type.tensor_type.shape.dim)

        

        # 转换为Relay IR

        shape_dict = {input_name: input_shape}

        mod, params = relay.frontend.from_onnx(onnx_model, shape_dict)

        

        return mod, params, input_name, input_shape

    

    def build_with_auto_scheduler(self, mod, params, num_measure_trials=1000):

        """使用自动调度器构建优化模型"""

        tasks, task_weights = auto_scheduler.extract_tasks(

            mod["main"], params, self.target

        )

        

        # 创建调度器

        tuner = auto_scheduler.TaskScheduler(tasks, task_weights)

        

        # 调度参数

        tune_option = auto_scheduler.TuningOptions(

            num_measure_trials=num_measure_trials,

            runner=auto_scheduler.LocalRunner(

                repeat=10,

                enable_cpu_cache_flush=True

            ),

            measure_callbacks=[auto_scheduler.RecordToFile("resnet50.json")]

        )

        

        # 执行调度

        tuner.tune(tune_option)

        

        # 使用最佳调度构建

        with auto_scheduler.ApplyHistoryBest("resnet50.json"):

            with tvm.transform.PassContext(

                opt_level=3,

                config={"relay.backend.use_auto_scheduler": True}

            ):

                lib = relay.build(mod, target=self.target, params=params)

        

        return lib

    

    def optimize_with_ansor(self, mod, params):

        """使用Ansor进行高级优化"""

        from tvm import meta_schedule as ms

        

        # 创建数据库记录

        database = ms.database.JSONDatabase(

            work_dir="./tune_records",

            module_equality="structural"

        )

        

        # 元调度配置

        sch = ms.tune_tir(

            mod=mod,

            target=self.target,

            work_dir="./tune_tir",

            max_trials_global=10000,

            num_trials_per_iter=64,

            database=database,

            strategy=ms.search_strategy.EvolutionarySearch(

                population_size=2048,

                init_measured_ratio=0.1,

                init_min_unmeasured=50,

                genetic_num_iters=3,

                genetic_mutate_prob=0.85,

                genetic_max_fail_count=10,

                eps_greedy=0.05

            )

        )

        

        # 编译优化后的模块

        with tvm.transform.PassContext(opt_level=3):

            lib = relay.build(mod, target=self.target, params=params)

        

        return lib

    

    def apply_graph_optimizations(self, mod):

        """应用图级别优化"""

        # 优化pass序列

        seq = tvm.transform.Sequential([

            # 常量折叠

            relay.transform.FoldConstant(),

            

            # 算子融合

            relay.transform.FuseOps(fuse_opt_level=2),

            

            # 布局转换优化

            relay.transform.ConvertLayout({

                "nn.conv2d": ["NHWC", "default"],

                "nn.max_pool2d": ["NHWC", "default"],

            }),

            

            # 代数简化

            relay.transform.SimplifyInference(),

            

            # 消除重复计算

            relay.transform.EliminateCommonSubexpr(),

            

            # 内存优化

            relay.transform.FoldScaleAxis(),

            relay.transform.CanonicalizeOps(),

            relay.transform.AlterOpLayout(),

            

            # 设备感知优化

            relay.transform.AnnotateTarget("llvm"),

            relay.transform.MergeCompilerRegions(),

            relay.transform.PartitionGraph(),

            

            # 内存规划

            relay.transform.InferType(),

            relay.transform.SimplifyExpr(),

            relay.transform.MemoryPlan(),

        ])

        

        optimized_mod = seq(mod)

        return optimized_mod

    

    def deploy_model(self, lib, input_shape, dtype="float32"):

        """部署编译后的模型"""

        # 创建图执行器

        module = graph_executor.GraphModule(lib["default"](self.ctx))

        

        # 准备输入数据

        input_data = tvm.nd.array(

            np.random.uniform(size=input_shape).astype(dtype)

        )

        

        # 设置输入

        module.set_input("data", input_data)

        

        # 执行推理

        module.run()

        

        # 获取输出

        output = module.get_output(0)

        

        return module, output

    

    def generate_c_source(self, lib, output_dir="./generated"):

        """生成C源代码用于嵌入式部署"""

        from tvm import relay

        from tvm.contrib import utils

        

        # 导出为C源码

        lib.export_library(f"{output_dir}/model.so")

        

        # 生成头文件

        lib_params = lib.get_params()

        

        # 保存参数

        with open(f"{output_dir}/params.bin", "wb") as f:

            f.write(relay.save_param_dict(lib_params))

        

        # 生成运行时包装代码

        runtime_code = f"""

#include

#include

#include

#include


// 模型推理接口

int model_infer(float* input, float* output, int batch_size) {{

    // 初始化TVM运行时

    DLDevice dev = {{kDLCPU, 0}};

    

    // 加载模块

    TVMModuleHandle mod;

    if (TVMModLoadFromFile("{output_dir}/model.so", "", &mod)) {{

        printf("Failed to load module\\n");

        return -1;

    }}

    

    // 获取图执行器

    TVMGraphExecutor* executor;

    if (TVMGraphExecutor_Create(mod, dev, &executor)) {{

        printf("Failed to create executor\\n");

        return -1;

    }}

    

    // 设置输入

    DLTensor input_tensor;

    input_tensor.data = input;

    input_tensor.device = dev;

    input_tensor.ndim = 4;

    input_tensor.dtype.code = kDLFloat;

    input_tensor.dtype.bits = 32;

    input_tensor.dtype.lanes = 1;

    input_tensor.shape = (int64_t[]){{batch_size, 224, 224, 3}};

    

    TVMGraphExecutor_SetInput(executor, "data", &input_tensor);

    

    // 执行推理

    TVMGraphExecutor_Run(executor);

    

    // 获取输出

    DLTensor output_tensor;

    TVMGraphExecutor_GetOutput(executor, 0, &output_tensor);

    

    // 拷贝输出数据

    memcpy(output, output_tensor.data, batch_size * 1000 * sizeof(float));

    <"tfr.j9k5.org.cn"><"dbd.j9k5.org.cn"><"uyj.j9k5.org.cn">

    // 清理资源

    TVMGraphExecutor_Release(executor);

    TVMModFree(mod);

    

    return 0;

}}

        """

        

        with open(f"{output_dir}/model_runtime.c", "w") as f:

            f.write(runtime_code)

        

        return runtime_code

```


## 跨硬件后端优化


```python

# cross_platform_optimization.py - 跨硬件优化配置

from tvm import relay

from tvm.relay import testing

import tvm.auto_scheduler as auto_scheduler


class CrossPlatformOptimizer:

    def __init__(self):

        self.hardware_profiles = {

            "arm_cpu": {

                "target": "llvm -device=arm_cpu -mtriple=aarch64-linux-gnu -mattr=+neon",

                "tuning_config": {

                    "num_measure_trials": 2000,

                    "early_stopping": 400,

                }

            },

            "nvidia_gpu": {

                "target": "cuda",

                "tuning_config": {

                    "num_measure_trials": 4000,

                    "early_stopping": 800,

                }

            },

            "intel_cpu": {

                "target": "llvm -mcpu=skylake-avx512",

                "tuning_config": {

                    "num_measure_trials": 3000,

                    "early_stopping": 600,

                }

            },

            "apple_m1": {

                "target": "llvm -mtriple=arm64-apple-darwin -mattr=+neon",

                "tuning_config": {

                    "num_measure_trials": 1500,

                    "early_stopping": 300,

                }

            }

        }

    

    def hardware_specific_optimization(self, mod, params, hardware_type):

        """硬件特定优化"""

        profile = self.hardware_profiles[hardware_type]

        target = tvm.target.Target(profile["target"])

        

        # 硬件感知的pass序列

        with tvm.transform.PassContext(opt_level=3):

            # 硬件特定优化

            if hardware_type == "nvidia_gpu":

                mod = self._optimize_for_gpu(mod)

            elif "arm" in hardware_type:

                mod = self._optimize_for_arm(mod)

            elif "intel" in hardware_type:

                mod = self._optimize_for_intel(mod)

            

            # 构建优化后的模块

            lib = relay.build(mod, target=target, params=params)

        

        return lib

    

    def _optimize_for_gpu(self, mod):

        """GPU特定优化"""

        seq = tvm.transform.Sequential([

            relay.transform.FoldConstant(),

            

            # GPU内存优化

            relay.transform.RewriteAnnotatedOps("cuda"),

            relay.transform.MergeCompilerRegions(),

            relay.transform.PartitionGraph(),

            

            # GPU内核优化

            relay.transform.AnnotateTarget("cuda"),

            relay.transform.FuseOps(),

            

            # 内存布局优化

            relay.transform.ConvertLayout({

                "nn.conv2d": ["NCHW", "default"],

                "nn.batch_norm": ["NCHW", "default"],

            }),

            

            relay.transform.SimplifyInference(),

            relay.transform.FoldScaleAxis(),

        ])

        

        return seq(mod)

    

    def _optimize_for_arm(self, mod):

        """ARM CPU特定优化"""

        seq = tvm.transform.Sequential([

            relay.transform.FoldConstant(),

            

            # ARM NEON优化

            relay.transform.AlterOpLayout(),

            

            # 内存访问优化

            relay.transform.ConvertLayout({

                "nn.conv2d": ["NHWC", "default"],

                "nn.depthwise_conv2d": ["NHWC", "default"],

            }),

            

            # 量化支持

            relay.transform.FakeQuantizationToInteger(),

            

            # 算子融合

            relay.transform.FuseOps(fuse_opt_level=3),

            

            # 内存规划

            relay.transform.MemoryPlan(),

        ])

        

        return seq(mod)

    

    def auto_tune_for_hardware(self, mod, params, hardware_type, log_file):

        """硬件自动调优"""

        profile = self.hardware_profiles[hardware_type]

        target = tvm.target.Target(profile["target"])

        

        # 提取任务

        tasks, task_weights = auto_scheduler.extract_tasks(

            mod["main"], params, target

        )

        

        # 创建调优器

        tuner = auto_scheduler.TaskScheduler(tasks, task_weights)

        

        # 硬件特定的测量选项

        if hardware_type == "nvidia_gpu":

            runner = auto_scheduler.LocalRunner(

                timeout=10,

                repeat=1,

                min_repeat_ms=100,

                enable_cpu_cache_flush=False

            )

        else:

            runner = auto_scheduler.LocalRunner(

                timeout=10,

                repeat=3,

                min_repeat_ms=200,

                enable_cpu_cache_flush=True

            )

        

        # 执行调优

        tuner.tune(

            auto_scheduler.TuningOptions(

                num_measure_trials=profile["tuning_config"]["num_measure_trials"],

                runner=runner,

                measure_callbacks=[auto_scheduler.RecordToFile(log_file)],

                early_stopping=profile["tuning_config"]["early_stopping"],

            )

        )

```


## MLIR与TVM集成工作流


```python

# mlir_tvm_integration.py - MLIR与TVM集成

from mlir.ir import *

from mlir.dialects import builtin, func, linalg, tensor

import tvm

from tvm import relay


class MLIRTVMIntegration:

    def __init__(self):

        self.ctx = Context()

        self.module = Module.create()

    

    def load_mlir_module(self, mlir_file):

        """加载MLIR模块"""

        with self.ctx:

            # 解析MLIR文件

            self.module = Module.parse(open(mlir_file).read())

        

        return self.module

    

    def convert_to_relay(self):

        """将MLIR转换为Relay IR"""

        with self.ctx, Location.unknown():

            # 遍历模块中的函数

            relay_functions = {}

            

            for op in self.module.operation.regions[0].blocks[0]:

                if isinstance(op, func.FuncOp):

                    func_name = op.name.value

                    relay_func = self._convert_mlir_func_to_relay(op)

                    relay_functions[func_name] = relay_func

            

            # 创建Relay模块

            relay_mod = tvm.IRModule(relay_functions)

            return relay_mod

    

    def _convert_mlir_func_to_relay(self, mlir_func):

        """转换单个MLIR函数到Relay"""

        # 提取函数签名

        func_type = mlir_func.type

        

        # 构建Relay函数参数

        relay_params = []

        for i, input_type in enumerate(func_type.inputs):

            param_name = f"arg_{i}"

            relay_type = self._convert_mlir_type_to_relay(input_type)

            relay_param = relay.var(param_name, type_=relay_type)

            relay_params.append(relay_param)

        

        # 转换函数体

        relay_body = self._convert_mlir_region_to_relay(

            mlir_func.body, relay_params

        )

        

        # 创建Relay函数

        relay_func = relay.Function(relay_params, relay_body)

        

        return relay_func

    

    def _convert_mlir_type_to_relay(self, mlir_type):

        """转换MLIR类型到Relay类型"""

        if isinstance(mlir_type, tensor.RankedTensorType):

            # 张量类型转换

            shape = list(mlir_type.shape)

            dtype = self._convert_mlir_dtype_to_relay(mlir_type.element_type)

            return relay.TensorType(shape, dtype)

        

        # 其他类型转换

        raise NotImplementedError(f"未支持的类型: {mlir_type}")

    

    def _convert_mlir_region_to_relay(self, region, params):

        """转换MLIR区域到Relay表达式"""

        # 实现操作转换逻辑

        # 这里简化处理,实际需要处理多种操作

        

        # 示例:返回第一个参数

        return params[0] if params else relay.const(0)

    

    def optimize_with_mlir_passes(self):

        """应用MLIR优化pass"""

        with self.ctx:

            # 创建pass管理器

            pm = PassManager.parse("""

            // 优化pipeline

            builtin.module(

                // 规范化

                canonicalize,

                cse,

                

                // 硬件无关优化

                affine-loop-fusion,

                affine-scalar-replace,

                memref-dataflow-opt,

                

                // 张量优化

                linalg-fuse-elementwise-ops,

                linalg-tile,

                linalg-promote,

                linalg-bufferize,

                

                // 向量化

                convert-linalg-to-loops,

                affine-super-vectorize,

                

                // LLVM lowering

                convert-vector-to-llvm,

                convert-math-to-llvm,

                convert-memref-to-llvm,

                convert-arith-to-llvm,

                convert-func-to-llvm,

                reconcile-unrealized-casts

            )

            """)

            

            # 执行优化

            pm.run(self.module.operation)

        <"huk.j9k5.org.cn"><"afd.j9k5.org.cn"><"szv.j9k5.org.cn">

        return self.module

```


## 性能基准测试框架


```python

# performance_benchmark.py - 性能基准测试

import time

import statistics

from typing import Dict, List

import numpy as np


class ModelBenchmark:

    def __init__(self, warmup_iterations=100, test_iterations=1000):

        self.warmup_iterations = warmup_iterations

        self.test_iterations = test_iterations

        self.results = {}

    

    def benchmark_model(self, model_runner, input_data, hardware_info=None):

        """基准测试模型性能"""

        print(f"开始基准测试: {hardware_info or '未知硬件'}")

        

        # 预热运行

        print("预热阶段...")

        for i in range(self.warmup_iterations):

            model_runner(input_data)

        

        # 正式测试

        print("性能测试阶段...")

        latencies = []

        memory_usages = []

        

        for i in range(self.test_iterations):

            start_time = time.perf_counter()

            

            # 执行推理

            output = model_runner(input_data)

            

            end_time = time.perf_counter()

            latency = (end_time - start_time) * 1000  # 转换为毫秒

            latencies.append(latency)

            

            # 记录内存使用(如果支持)

            if hasattr(model_runner, 'get_memory_usage'):

                memory_usages.append(model_runner.get_memory_usage())

        

        # 计算统计信息

        stats = self._compute_statistics(latencies, memory_usages)

        

        # 存储结果

        test_id = f"{hardware_info}_{time.strftime('%Y%m%d_%H%M%S')}"

        self.results[test_id] = {

            "hardware_info": hardware_info,

            "statistics": stats,

            "raw_latencies": latencies,

            "timestamp": time.time()

        }

        

        return stats

    

    def _compute_statistics(self, latencies: List[float], 

                          memory_usages: List[float]) -> Dict:

        """计算性能统计"""

        stats = {

            "latency_ms": {

                "mean": statistics.mean(latencies),

                "median": statistics.median(latencies),

                "p90": np.percentile(latencies, 90),

                "p95": np.percentile(latencies, 95),

                "p99": np.percentile(latencies, 99),

                "std": statistics.stdev(latencies) if len(latencies) > 1 else 0,

                "min": min(latencies),

                "max": max(latencies),

            },

            "throughput_fps": {

                "mean": 1000 / statistics.mean(latencies),

            }

        }

        

        if memory_usages:

            stats["memory_mb"] = {

                "mean": statistics.mean(memory_usages) / (1024 * 1024),

                "max": max(memory_usages) / (1024 * 1024),

            }

        

        return stats

    

    def compare_backends(self, backend_results: Dict[str, Dict]):

        """比较不同后端的性能"""

        comparison = {}

        

        for backend_name, result in backend_results.items():

            comparison[backend_name] = {

                "mean_latency_ms": result["statistics"]["latency_ms"]["mean"],

                "p95_latency_ms": result["statistics"]["latency_ms"]["p95"],

                "throughput_fps": result["statistics"]["throughput_fps"]["mean"],

            }

            

            if "memory_mb" in result["statistics"]:

                comparison[backend_name]["mean_memory_mb"] = \

                    result["statistics"]["memory_mb"]["mean"]

        

        # 生成性能报告

        report = self._generate_comparison_report(comparison)

        

        return comparison, report

    

    def _generate_comparison_report(self, comparison: Dict) -> str:

        """生成比较报告"""

        report_lines = ["# 性能比较报告", ""]

        

        # 按延迟排序

        sorted_backends = sorted(

            comparison.items(),

            key=lambda x: x[1]["mean_latency_ms"]

        )

        

        report_lines.append("## 延迟比较(毫秒)")

        report_lines.append("| 后端 | 平均延迟 | P95延迟 | 吞吐量(FPS) |")

        report_lines.append("|------|----------|---------|-------------|")

        

        for backend, metrics in sorted_backends:

            report_lines.append(

                f"| {backend} | "

                f"{metrics['mean_latency_ms']:.2f} | "

                f"{metrics['p95_latency_ms']:.2f} | "

                f"{metrics['throughput_fps']:.2f} |"

            )

        

        report_lines.append("\n## 分析结果")

        

        # 计算性能提升

        if len(sorted_backends) >= 2:

            best = sorted_backends[0][1]

            worst = sorted_backends[-1][1]

            

            latency_improvement = (worst["mean_latency_ms"] - best["mean_latency_ms"]) / worst["mean_latency_ms"] * 100

            throughput_improvement = (best["throughput_fps"] - worst["throughput_fps"]) / worst["throughput_fps"] * 100

            

            report_lines.append(f"- 最佳后端比最差后端延迟降低: {latency_improvement:.1f}%")

            report_lines.append(f"- 最佳后端比最差后端吞吐量提升: {throughput_improvement:.1f}%")

        

        return "\n".join(report_lines)

```


基于MLIR多级中间表示和TVM张量编译器技术栈,为深度学习模型的跨硬件部署提供了完整的解决方案。MLIR通过分层中间表示支持渐进式优化,而TVM通过自动调度生成实现硬件特定的高性能代码生成。在实践中,应根据目标硬件特性选择合适的优化策略,建立完整的性能基准测试流程,确保模型在不同平台上的最优执行效率。随着硬件生态的多样化,这种编译器驱动的优化方法将成为AI模型部署的关键技术支撑。


请使用浏览器的分享功能分享到微信等