-
环境鲲鹏 920 128 核Bisheng JDK 17.0.1 / 17.0.4运行 BenchmarkSQL + ShardingSphere-JDBC网卡队列绑核 0-15,BenchmarkSQL 绑核 16-127。现象概率性出现运行 BenchmarkSQL us 较高(几乎压满 CPU 核),sy 较低。之前使用的是 GraalVM EE,本来以为是 GraalVM EE 才有的现象,后续测试发现使用 Bisheng JDK 17 也有相似现象。多次启动 BenchmarkSQL 进程,性能时好时坏。正常情况下,使用 BenchmarkSQL + ShardingSphere-JDBC 性能可以达到 200 万 tpmC 以上。最近启动进程时有较高概率发生性能异常,性能相比正常情况下降约 30%。Bisheng JDK 17 现象正常情况us 相对较低,网卡中断几乎跑满前 16 核。正常情况下 tpmC 在 200 万左右。异常现象最近测试发现,经常出现 us 较高,中断相比性能正常时更低。tpmC 在 160 万左右。性能下降至正常的 70% 左右。升级 Bisheng JDK 17.0.4 问题未解决async-profiler 采样对比正常情况与异常情况的采样,表面现象为计算开销增加,在代码路径上没有看出其他异常。 GraalVM EE 现象偶发现象:CPU us 特别高,sy 较低。退出压测进程重新运行后现象消失。6 节点测试中,该现象频繁出现GraalVM 发生该现象时对性能影响非常明显,tpmC 相比正常几乎减半。
-
本文针对静态图模式,介绍如何运用Dump工具对网络数据进行分析。 分为异步dump和同步dump两种方式。注:推荐用异步dump。MindSpore默认开启内存复用,而同步dump会关闭内存复用,可能会影响训练状态。一、异步Dump大型网络(如Bert Large)使用同步Dump时会导致内存溢出,MindSpore通过异步Dump提供了大型网络的调试能力。1、创建json格式的配置文件json文件名称和位置可自定义设置。{ "common_dump_settings": { "dump_mode": 0, "path": "/absolute_path", "net_name": "ResNet50", "iteration": "0", "input_output": 0, "kernels": ["Default/Conv-op12"], "support_device": [0,1,2,3,4,5,6,7], "op_debug_mode": 0 }}参数说明:dump_mode:设置成0,表示Dump出改网络中的所有算子;设置成1,表示Dump"kernels"里面指定的算子。path:Dump保存数据的绝对路径。net_name:自定义的网络名称,例如:”ResNet50”。iteration:指定需要Dump的迭代。类型为str,用“|”分离要保存的不同区间的step的数据。如”0|5-8|100-120”表示Dump参数初始值,第1个,第6个到第9个, 第101个到第121个step的数据。指定“all”,表示Dump所有迭代的数据。input_output:设置成0,表示Dump出算子的输入和算子的输出;设置成1,表示Dump出算子的输入;设置成2,表示Dump出算子的输出。kernels:算子的名称列表。开启IR保存开关context.set_context(save_graphs=True)并执行用例,从生成的trace_code_graph_{graph_id}IR文件中获取算子名称。kernels仅支持TBE算子、AiCPU算子、通信算子,若设置成通信算子的名称,将会Dump出通信算子的输入算子的数据。详细说明可以参照教程:如何保存IR。support_device:支持的设备,默认设置成0到7即可;在分布式训练场景下,需要dump个别设备上的数据,可以只在support_device中指定需要Dump的设备Id。注:本示例是单卡任务,用默认配置即可。enable:开启异步Dump,如果同时开启同步Dump和异步Dump,那么只有同步Dump会生效。op_debug_mode:该属性用于算子溢出调试,设置成0,表示不开启溢出;设置成1,表示开启AiCore溢出检测;设置成2,表示开启Atomic溢出检测;设置成3,表示开启全部溢出检测功能。在Dump数据的时候请设置成0,若设置成其他值,则只会Dump溢出算子的数据。2、设置Dump环境变量export MINDSPORE_DUMP_CONFIG=/home/ma-user/xxx/data_dump.json此处路径应为json配置文件的绝对路径。3、脚本修改设置context.set_context(reserve_class_name_in_scope=False),避免Dump文件名称过长导致Dump数据文件生成失败。4、运行脚本启动命令:python MindSpore_1P.py异步Dump保存的数据目录结构如下所示:{path}/ |-- {device_id}/ |-- {new_name}_graph_{graph_id}/ |-- {graph_id}/ |-- {iteration}/ |-- {op_type}.{op_name}.{task_id}.{timestamp} … |-- graphs/ ms_output_trace_code_graph_{graph_id}.pb ms_output_trace_code_graph_{graph_id}.ir |-- execution_order/ ms_execution_order_graph_{graph_id}.csv |-- .metadata/ data_dump.jsonpath:data_dump.json文件中设置的绝对路径。net_name:data_dump.json文件中设置的网络名称。device_id:训练的卡号。graph_id:训练的图标号。iteration:训练的轮次。op_type:算子类型。op_name:算子名称。taskid:任务标号。timestamp:时间戳。5、异步Dump数据分析样例以Resnet50脚本为例:def _conv2d(in_channel, out_channel, kernel_size, stride=1, padding=0): scale = math.sqrt(1/(in_channel*kernel_size*kernel_size)) if padding == 0: return nn.Conv2d(in_channel, out_channel, kernel_size=kernel_size, stride=stride, padding=padding, pad_mode='same', weight_init=mindspore.common.initializer.Uniform(scale=scale)) else: return nn.Conv2d(in_channel, out_channel, kernel_size=kernel_size, stride=stride, padding=padding, pad_mode='pad', weight_init=mindspore.common.initializer.Uniform(scale=scale))...class ResNet(nn.Cell): def __init__(self, num_blocks, num_classes=10): super(ResNet, self).__init__() self.in_planes = 64 self.conv1 = _conv2d(3, 64, kernel_size=3, stride=1, padding=1) self.bn1 = nn.BatchNorm2d(64) self.relu = nn.ReLU() self.layer1 = self._make_layer(64, num_blocks[0], stride=1) self.layer2 = self._make_layer(128, num_blocks[1], stride=2) self.layer3 = self._make_layer(256, num_blocks[2], stride=2) self.layer4 = self._make_layer(512, num_blocks[3], stride=2) self.avgpool2d = nn.AvgPool2d(kernel_size=4, stride=4) self.reshape = mindspore.ops.Reshape() self.linear = _dense(2048, num_classes) self.print = P.Print() self.print_grad = P.InsertGradientOf(self.save_gradient) def save_gradient(self, dout): return dout def _make_layer(self, planes, num_blocks, stride): strides = [stride] + [1]*(num_blocks-1) layers = [] for stride in strides: layers.append(ResidualBlock(self.in_planes, planes, stride)) self.in_planes = EXPANSION*planes return nn.SequentialCell(*layers) def construct(self, x): x = self.conv1(x) out = self.relu(self.bn1(x)) out = self.layer1(out) out = self.layer2(out) out = self.layer3(out) out = self.layer4(out) out = self.avgpool2d(out) out = self.reshape(out, (out.shape[0], 2048)) out = self.linear(out) return out若用户想查看脚本中第一个卷积算子的权重 :x = self.conv1(x)(1) 查找算子对应的数据文件执行完训练网络后,可以从最终执行图(ms_output_trace_code_graph_{graph_id}.ir文件)中查找到该行代码所对应的多个算子信息,文件内容如下所示:...%4(equivoutput) = Conv2D(%1, %3) {instance name: conv2d} primitive_attrs: {visited: true, pri_format: NC1HWC0, IsFeatureMapInputList: (0), out_channel: 64, kernel_size: (3, 3), IsFeatureMapOutput: true, pad_mode: pad, stride: (1, 1, 1, 1), mode: 1, pad: (1, 1, 1, 1), pad_list: (1, 1, 1, 1), group: 1, format: NCHW, dilation: (1, 1, 1, 1), input_names: [x, w], output_names: [output], groups: 1}: (, ) -> (): (, ) -> (): (Default/network/_backbone/conv1/Conv2D-op515)# In file /home/ma-user/miniconda3/envs/MindSpore-python3.7-aarch64/lib/python3.7/site-packages/mindspore/nn/layer/conv.py(258)/ output = self.conv2d(x, self.weight)/# In file /home/ma-user/work/xxx/Resnet50_cifar10/youhuahou/src/resnet.py(88)/ x = self.conv1(x)/...以上所示文件包括:算子在Host侧(第一行)和Device侧(第二行,有些算子可能不存在)的输入输出情况。: (, ) -> (): (, ) -> ()算子名称Default/network/_backbone/conv1/Conv2D-op515算子对应训练脚本代码# In file /home/ma-user/work/xxx/Resnet50_cifar10/src/resnet.py(88)/ x = self.conv1(x)/根据算子名称中的op515,在Dump生成的数据文件目录({iteration})中,查找对应的Tensor数据文件。搜索到相应的文件名:Conv2D.Default_network__backbone_conv1_Conv2D-op515.58.16.1624605159829577(2)使用海思Run包中提供的msaccucmp.py ,解析Dump出来的文件 。注:不同的环境上msaccucmp.py文件所在的路径可能不同,可以通过find命令进行查找:find ${run_path} -name "msaccucmp.py"run包的安装路径。(3)找到msaccucmp.py后,到/absolute_path目录下,运行如下命令解析Dump数据:python ${The absolute path of msaccucmp.py} convert -d {file path of dump} -out {file path of output}数据在Device侧的格式可能和Host侧计算图中的定义不同,异步Dump的数据格式为Device侧格式,如果想要转为Host侧格式,可以参考如何进行dump数据文件Format转换。由于转换中存在FRACTAL_Z to NCHW格式的转换,工具当前未支持,因此需要编写自定义转换脚本,可使用示例代码包中的convert_FRACTAL_Z_to_NCHW.py脚本进行转换。将convert_FRACTAL_Z_to_NCHW.py文件放在新建的format_convert文件夹中。-c后指定为format_convert目录的上一层目录。执行: (示例中format_convert文件夹新建在../目录下,也可以创建在其他目录下)python /usr/local/Ascend/toolkit/tools/operator_cmp/compare/msaccucmp.py convert -d ./Conv2D.Default_network__backbone_conv1_Conv2D-op515.58.16.1624605159829577 -out ../output/ -f NCHW -c ../在./output下生成该算子的所有输入输出数据。每个数据以.npy后缀的文件保存,生成结果如下:Conv2D.Default_network__backbone_conv1_Conv2D-op515.58.16.1624605159829577.input.0.128x3x32x32.npyConv2D.Default_network__backbone_conv1_Conv2D-op515.58.16.1624605159829577.input.1.64x3x3x3.npyConv2D.Default_network__backbone_conv1_Conv2D-op515.58.16.1624605159829577.output.0.128x64x32x32.npy在文件名的末尾可以看到该文件是算子的第几个输入或输出,再结合维度信息可以判断数据的含义:例如,上面三个文件依次表示该卷积算子的输入、权重和输出。(4)通过numpy.load接口读取要查看的数据在终端依次输入:python->import numpy->numpy.load("Conv2D.Default_network__backbone_conv1_Conv2D-op515.58.16.1624605159829577.input.1.64x3x3x3.npy")输出数据: array([[[[ 0.01878 , 0.0828 , 0.03955 ], [ 0.01727 , -0.02939 , 0.05615 ], [-0.02402 , 0.1508 , 0.1785 ]], ... [[ 0.1145 , 0.1186 , 0.1643 ], [-0.148 , -0.1088 , 0.0935 ], [-0.117 , -0.0822 , -0.1283 ]]]], dtype=float16)二、同步Dump1、创建json格式的配置文件json文件名称和位置可自定义设置。{ "common_dump_settings": { "dump_mode": 0, "path": "/absolute_path", "net_name": "ResNet50", "iteration": "0", "input_output": 0, "kernels": ["Default/Conv-op12"], "support_device": [0,1,2,3,4,5,6,7] }, "e2e_dump_settings": { "enable": true, "trans_flag": true }}参数说明:dump_mode:设置成0,表示Dump出该网络中的所有算子;设置成1,表示Dump"kernels"里面指定的算子。path:Dump保存数据的绝对路径。net_name:自定义的网络名称,例如:”ResNet50”。iteration:指定需要Dump数据的迭代。类型为str,用“|”分离要保存的不同区间的step的数据。如”0|5-8|100-120”表示Dump参数初始值,第1个,第6个到第9个, 第101个到第121个step的数据。指定“all”,表示Dump所有迭代的数据。input_output:设置成0,表示Dump出算子的输入和算子的输出;设置成1,表示Dump出算子的输入;设置成2,表示Dump出算子的输出。该配置参数仅支持Ascend和CPU,GPU只能Dump算子的输出。kernels:算子的名称列表。开启IR保存开关context.set_context(save_graphs=True)并执行用例,从生成的IR文件trace_code_graph_{graph_id}中获取算子名称。详细说明可以参照教程:如何保存IR。support_device:支持的设备,默认设置成0到7即可;在分布式训练场景下,需要dump个别设备上的数据,可以只在support_device中指定需要Dump的设备Id。该配置参数在CPU上无效,因为CPU下没有device这个概念。注:本示例是单卡任务,用默认配置即可。enable:开启E2E Dump,如果同时开启同步Dump和异步Dump,那么只有同步Dump会生效。trans_flag:开启格式转换。将设备上的数据格式转换成NCHW格式。若为True,则数据会以Host侧的4D格式(NCHW)格式保存;若为False,则保留Device侧的数据格式。该配置参数在CPU上无效,因为CPU上没有format转换。2、设置Dump环境变量export MINDSPORE_DUMP_CONFIG=/home/ma-user/xxx/data_dump.json此处路径应为json配置文件的绝对路径。3、脚本修改设置model.train中的dataset_sink_mode参数为False 。同步模式下Dump数据,必须采用非数据下沉模式,以保证可以获取每个step的Dump数据。设置context.set_context(reserve_class_name_in_scope=False)。避免Dump文件名称过长导致Dump数据文件生成失败。4、运行脚本启动命令:python MindSpore_1P.py同步Dump保存的数据目录结构如下所示:{path}/ |-- {net_name}/ |-- {device_id}/ |-- iteration_{iteration}/ -- {op_name}_{input_output_index}_{shape}_{data_type}_{format}.bin … |-- graphs/ ms_output_trace_code_graph_{graph_id}.pb ms_output_trace_code_graph_{graph_id}.ir |-- execution_order/ ms_execution_order_graph_{graph_id}.csv |-- .metadata/ data_dump.jsonpath:data_dump.json配置文件中设置的绝对路径。net_name:data_dump.json配置文件中设置的网络名称。device_id:训练的卡号。graph_id:训练的图标号。iteration:训练的轮次。operator_name:算子名称。input_output_index :输入或输出标号,例如output_0表示该文件是该算子的第1个输出Tensor的数据。shape: 张量维度信息。data_type: 数据类型。format: 数据格式。5、同步Dump数据分析样例对于Ascend场景,在通过Dump功能将脚本对应的图保存到磁盘上后,会产生最终执行图文件ms_output_trace_code_graph_{graph_id}.ir。该文件中保存了对应的图中每个算子的堆栈信息。若用户想查看Resnet50脚本(同异步Dump数据分析样例)中第一个卷积算子的权重:x = self.conv1(x)(1) 查找算子对应的数据文件与异步Dump数据分析样例中查找方式相同。搜索到相应的文件名:Default--network-TrainOneStepCell--network-WithLossCell--_backbone-ResNet--conv1-Conv2d--Conv2D-op516_input_1_shape_64_3_3_3_Float32_DefaultFormat.bin文件名中可以得到如下信息:shape: 张量维度是(64,3,3,3);data_type: 数据类型为Float32;通过numpy.fromfile接口,还原数据:(2)在终端中依次执行:python->import numpy->array = numpy.fromfile("Default--network-TrainOneStepCell--network-WithLossCell--_backbone-ResNet--conv1-Conv2d--Conv2D- op516_input_1_shape_64_3_3_3_Float32_DefaultFormat.bin", numpy.float32)->numpy.reshape(array, (64,3,3,3))还原原始shape数据:array([[[[-0.01689148, 0.05319214, 0.00757217], [ 0.02868652, 0.00693512, 0.08831787], [ 0.01548767, 0.20080566, 0.22070312]], ... [[ 0.09954834, 0.07037354, 0.15905762], [-0.14477539, -0.11743164, 0.11804199], [-0.16235352, -0.13134766, -0.13427734]]]], dtype=float32)
-
背景信息当开发人员遇到数据量较大的张量或字符串输出时,默认情况下,程序会将输出的数据打印到屏幕上,海量的数据无法用人眼直接识别有价值的数据,此时mindspore.ops.Print方法应运而生,该方法通过context接口中print_file_path参数将其保存在文件中。输出数据将保存在指定路径的文件中。之后可使用mindspore.parse_print()方法加载保存的数据。一、保存print数据1、示例代码import numpy as npimport mindspore.ops as opsfrom mindspore import Tensor, contextfrom mindspore.ops import operations as Pimport mindspore.nn as nnimport mindspore#保存数据context.set_context(mode=context.GRAPH_MODE, print_file_path='./save_print_data', device_target="Ascend")class PrintDemo(nn.Cell): def __init__(self): super(PrintDemo, self).__init__() self.conv1 = nn.Conv2d(in_channels=1, out_channels=6, kernel_size=4, stride=1 ,has_bias=False, weight_init='normal', pad_mode='valid') self.conv2 = nn.Conv2d(in_channels=6, out_channels=2, kernel_size=2, pad_mode="valid") self.conv3 = nn.Conv2d(in_channels=2, out_channels=6, kernel_size=2, pad_mode="valid") self.print = P.Print() #打印出每层计算输出结果 def construct(self, input_data): x = self.conv1(input_data) self.print('self.conv1', x) x = self.conv2(x) self.print('self.conv2', x) x = self.conv3(x) self.print('self.conv3', x) return xdef test(): input_data = Tensor(np.ones([1, 1, 32, 32]), mindspore.float32) net = PrintDemo() net(input_data) return net(input_data)test()2、代码解析:context设置为context.set_context方法添加print_file_path参数来设置保存输出数据的路径:mindspore.context.set_context(mode=context.GRAPH_MODE, device_target="Ascend",print_file_path='./xxx_data')二、解析print数据1、示例代码data = mindspore.parse_print('./xxx_data')for i in range(0,len(data)): if isinstance(data[i], str): print(data[i]) continue else: tmp = data[i].asnumpy() data[i] = tmpprint(data)2、代码解析mindspore.ops.Print方法所保存的数据为二进制文件,不便于开发人员使用,故需要进一步的文件解析。在mindspore中提供了该文件的解析方法如下所示:data = mindspore.parse_print('./xxx_data')此时该文件解析后的数据格式为:list['输入名称(str)', Tensor],示例代码运行的结果如下所示:由于输入将转变为Tensor数据类型输出,不便于开发人员进行数据计算操作,故对此数据类型转换为numpy数据类型:data = mindspore.parse_print('./xxx.data')for i in range(0,len(data)): if isinstance(data[i], str): print(data[i]) continue else: tmp = data[i].asnumpy() data[i] = tmpprint(data)
-
1.简介别名分析是编译器理论中的一种技术,用于确定存储位置是否可以以多种方式访问。如果两个指针指向相同的位置,则称这两个指针为别名。 但是,它不能与指针分析混淆,指针分析解决的问题是一个指针可能指向哪些对象或者指向哪些地址,而别名分析解决的是两个指针指向的是否是同一个对象。指针分析和别名分析通常通过静态代码分析来实现。别名分析在编译器理论中非常重要,在代码优化和安全方面有着非常广泛且重要的应用。编译器级优化需要指针别名信息来执行死代码消除(删除不影响程序结果的代码)、冗余加载/存储指令消除、指令调度(重排列指令)等。编译器级别的程序安全使用别名分析来检测内存泄漏和内存相关的安全漏洞。2.别名分析分类别名分析种类繁多,通常按如下属性进行分类:域敏感(field-sensitivity)、过程内分析(Intra-Procedural)v.s.过程间分析(Inter-Procedural)、上下文敏感度(context-sensitivity)和流敏感度(flow-sensitivity)。2.1 域敏感(Field-Sensitivity)域敏感是对用户自定义类型进行分析的一种策略(亦可以处理数组)。在域敏感维度共有三种分析策略:域敏感(field-sensitive)、域非敏感(field-insensitive)、域基础分析(field-based)。以下面代码为例:struct Test { int field1; int field2; } Test a1; Test a2;Note:field这里为结构体或者类的数据成员。域非敏感:对每个对象建模,而对对象中的成员不进行处理;其建模后的结果如下图,仅有a1.*和a2.*的区别:域基础分析:仅对结构体中的成员进行建模,而不感知对象。其建模后的结果如下图,仅有*.field1和*.field2:域敏感:既对对象建模,又对成员变量进行处理。其建模后的结果如下图,有a1.field1、a1.field2、a2.field1、a2.field2:处理数组时,相同的原则亦适用。以C整数数组为例:int a[10],域非敏感分析仅使用一个节点建模:a[*],而域敏感分析创建10个节点:a[0]、a[1]、...、a[9]。总结:域敏感别名分析准确性高,但是当存在嵌套结构或者大数组时,节点数量会迅速增加,分析成本也会陡然上升。2.2 过程内分析(Intra-Procedural)v.s.过程间分析(Inter-Procedural)过程内分析仅分析函数体内部的指针,并没有考虑与其他函数之间的相互影响。需要特别指出的是,过程内分析当处理包含指针入参的函数或者返回指针的函数时,其分析可能不够准确。相反,过程间分析会在函数调用过程中处理指针的行为。过程内分析不易于扩展,精度较低。相比过程间分析,过程内分析更容易实现,且过程内/间分析与上下文敏感度分析高度相关,因为一个上下文敏感分析必定是一个过程间分析。2.3 上下文敏感度(Context-Sensitivity)上下文敏感度用来控制函数调用该如何分析。有两种分析方法:上下文敏感(context-sensitive) 和上下文非敏感(context-insensitive)。上下文敏感在分析函数调用的目标(被调用者)时考虑调用上下文(调用者)。以如下代码为参考[1]:1 public static void main(String[] args) { 2 String name1 = getName(3); // Tainted 3 String sql1 = "select * from user where name = " + name1; 4 sqlExecute(sql1); // Taint Sink 5 6 String name2 = getName(-1); // Not Tainted 7 String sql2 = "select * from user where name = " + name2; 8 sqlExecute(sql2); 9 } 10 11 private static String getName(int x) { 12 if (x > 0) { 13 return System.getProperty("name"); 14 } else { 15 return "zhangsan"; 16 } 17 }如上所示,getName()方法基于入参的不同,会返回不同的结果,在第2行和第6行,获取到的name1和name2的污点信息不同,当入参为3时,返回的是一个从环境变量中获取的污染的数据,导致sql注入,而当入参为-1时,返回的是一个常量,不是污染数据,不会有问题。 在上下文敏感的分析中,在第4行应该报一个sql注入问题,而在第8行则不应该报sql注入问题。而上下文非敏感的分析中,不考虑传入参数的不同,getName()方法则全部返回一个{System.getProperty("name")}∨{zhangsan},从而导致第4行和第8行都会报一个sql注入的问题。上下文敏感别名分析需要有一种方法,为函数getName创建抽象描述,以便每次调用它时,分析器都可以将调用上下文应用于抽象描述。总结:上下文敏感分析比较准确,但是增加了复杂度。2.4 流敏感度Flow-Sensitivity流敏感度是一种是否考虑代码顺序的原则。有两种方法:流敏感(flow-sensitive)和流非敏感(flow-insensitive)。流非敏感不考虑代码顺序,并为整个程序生成一组别名分析结果,而流敏感考虑代码顺序,计算程序中每个指针出现的位置的别名信息。以如下代码为例:1 int a,b; 2 int *p; 3 p = &a; 4 p = &b;流非敏感的分析结果是针对整个代码块,其结果应该是:指针p可能指向变量a或者变量b。流敏感生成的别名信息是,在第3行,指针p指向变量a,在第4行以后指针p指向变量b。Note:当程序具有许多条件语句、循环或递归函数时,流敏感分析的复杂性会大大增加。要执行流敏感分析,需要完整的控制流图。因此,流敏感分析非常精确,但对于大多数情况来说,它的分析成本过高,无法在整个程序上执行。3.别名分析常见算法介绍常见的别名算法共有三种:Andersen's指针分析算法、Steensgaard's指针分析算法和数据结构分析算法。Andersen's指针分析是一种流非敏感和上下文非敏感的分析算法。Andersen's指针分析算法复杂度较高,实践应用性较差,其时间复杂度为,其中n为指针节点个数。Steensgaard's指针分析算法也是一种流非敏感,上下文非敏感且域非敏感的别名分析算法。其时间复杂度较低,实现相对简单,实践应用广,其时间复杂度为,其中无限接近于1,但是其别名分析的准确性较低。数据结构分析算法是一种流非敏感,上下文敏感和域敏感的算法。其时间复杂度较低,为O(n * log(n)) ,应用性较好,但是由于不支持MustAlias(参考“AliasAnalysis Class概览”章节),导致其应用有局限性。4.别名分析在LLVM中的应用与实现4.1 应用别名分析在代码优化和安全方面有着非常重要且广泛的应用,以下面C代码为例,来简单介绍别名分析在代码优化方面的应用[2]。int foo (int __attribute__((address_space(0)))* a, int __attribute__((address_space(1)))* b) { *a = 42; *b = 20; return *a; }__attribute__属性指定了变量a指向地址0,变量b指向地址1。我们知道在ARM架构中,地址0和地址1是完全不同的,修改地址0中的内存永远不会修改地址1中的内存。以下为该函数可能生成的LLVM IR信息:define i32 @foo(i32 addrspace(0)* %a, i32 addrspace(1)* %b) #0 { entry: store i32 42, i32 addrspace(0)* %a, align 4 store i32 20, i32 addrspace(1)* %b, align 4 %0 = load i32, i32* %a, align 4 ret i32 %0 }第一个store将42存储到变量a指向的地址,第二个store指令将20存储到变量b指向的地址。%0 = ... 指向的行将变量a中的值加载到一个临时变量0中,并在最后一行返回该临时变量0。上述代码是未对foo函数进行优化的情况,下面我们考虑对foo函数进行优化。我们优化后的代码可能如下:删除了load指令对应的行,最后一行直接返回了常量42。define i32 @foo(i32 addrspace(0)* %a, i32 addrspace(1)* %b) #0 { entry: store i32 42, i32 addrspace(0)* %a, align 4 store i32 20, i32 addrspace(1)* %b, align 4 ret i32 42 }然而,我们进行优化的时候需要仔细一些,因为上述优化仅在a和b指向的地址不会相互影响时有效。例如:当我们给foo函数传递的指针相互影响时:int i = 0; int result = foo(&i, &i);在未开启优化的版本中,变量i将先被设置为42,然后被设置为20,最后返回20。然而,在优化版本中,虽然我们执行了两次store操作依次将42、20赋值给变量i,但是返回值是42,而不是20。因此优化版本破坏了foo函数本身的行为。如果应用了别名分析,编译器能够合理的执行上述优化。在执行优化前判断入参a和b是否为别名,如果是别名,则不执行删除load指令对应行的操作,否则执行删除操作。4.2 实现本文以LLVM16.0.0版本为参考,从代码接口入手,带领大家学习别名分析的代码实现。LLVM AliasAnalysis类是LLVM系统中客户使用和别名分析实现的主要接口,或者说一个“基类” 。除了简单的别名分析信息外,这个类还声明了Mod/Ref信息,从而使强大的分析和转换能够很好地协同工作。源码参考链接:AliasAnalysis.h[3]、AliasAnalysis.cpp[4]。4.2.1 基础知识MemoryLocation:LLVM中对内存地址的描述,主要应用在别名分析中,我们需要掌握该类中三个属性:其中,Ptr表示内存开始地址,Size表示内存大小,AATags是描述内存位置别名的metadata节点集合 。4.2.2 AliasAnalysis Class 概览AliasAnalysis类定义了各种别名分析实现应该支持的接口。这个类导出两个重要的枚举:AliasResult和ModRefResult,它们分别表示别名查询或mod/ref查询的结果。1、关键代码如下,AliasAnalysis为AAResults类别名:2、AliasResult关键代码如下:其中NoAlias表示两个内存对象没有任何重叠区域;MayAlias表示两个指针可能指向同一对象;PartialAlias表示两个内存对象对应的地址空间有重叠;MustAlias表示两个内存对象总是从同一位置开始。3、ModRefResult关键代码其中NoModRef表示访问内存的操作既不会修改该内存也不会引用该内存; Ref表示访问内存的操作会可能引用该内存;Mod表示访问内存的操作可能会修改该内存;ModRef表示访问内存的操作既可能引用该内存也可能修改该内存。alias接口其接口定义如下:别名方法是用于确定两个MemoryLocation对象是否相互别名的主要接口。它接受两个MemoryLocation对象作为输入,并根据需要返回MustAlias、PartialAlias、MayAlias或NoAlias。 与所有AliasAnalysis接口一样,alias方法要求其入参的两个MemoryLocation对象定义在同一个函数中,或者至少有一个值是常量。其接口实现如下:getModRefInfo 接口getModReInfo方法返回关于给定的指令执行是否可以读取或修改给定内存位置的信息。Mod/Ref信息具有保守性:如果一条指令可能读或写一个位置,则返回ModRef。 其接口定义众多,我们以如下接口为例来进行学习。其接口实现如下:从上述代码可知,处理共分为四步:(1)遍历AAs,如果发现其任一结果是NoModRef,则直接返回,对应代码行228-234;(2)调用节点(call)操作中是否访问了一个在LLVM IR中无法访问的地址,如果是的话,直接返回NoModRef,否则获取其调用节点的ModRefInfo信息,对应代码行239-240;(3)处理调用节点中指针入参的ModRefInfo信息,如果发现是NoModRef,则直接返回NoModRef,否则将ModRefInfo信息和之前的结果合并,对应代码行247-266;(4)如果getModRefInfo函数中的入参Loc指定的内存地址具有常量属性并且ModRefInfo信息包含Mod,则调用节点一定不会修改Loc内存,因此需要将Ref属于与之前的结果做逻辑与操作,对应代码行271-272。4.2.3 LLVM中已经实现的别名分析-basic-aa pass-basic-aa pass是一种激进的本地分析,它提供许多重要的事实信息[5]:不同的全局变量、堆栈分配和堆分配永远不能别名。全局变量、栈分配的变量和堆分配变量永远不会和空指针别名。结构体中的不同字段不能别名。同一数组,索引不同的两个对象不能别名。许多通用的标准C库函数从不访问内存或只读取内存。-globals-aa pass这个pass实现了一个简单的对内部全局变量(该变量的地址没有被获取过)进行上下文敏感的mod/ref分析和别名分析。 如果某个全局变量的地址没有被获取,则该pass可以得出如下结论:没有指针作为该全局变量的别名。该pass还会识别从不访问内存或从不读取内存的函数。这允许某些指定的优化(例如GVN)完全消除调用指令。这个pass的真正威力在于它为调用指令提供了上下文敏感的mod/ref信息。这使优化器清楚的了解到对于某些函数的调用不会破坏或读取全局变量的值,从而允许消除加载和存储指令。Note:该pass在使用范围上有一定限制,仅支持没有被取过地址的全局变量,但是该pass分析速度非常快。除了上述pass外,LLVM中还实现了cfl-steens-aa、cfl-anders-aa、tbaa、scev-aa。目前LLVM中O1,O2,O3优化默认开启的别名分析是basic-aa,globals-aa和tb-aa。5.写在最后编译器技术从20世纪50年代起,已经发展了近70年的历史,但是编译器技术发展到今天,依然是一个非常热门的技术,各大硬件厂商都在开发自己的编译器,包括因特尔推出的Inter C++、ARM公司推出的armclang以及华为推出的毕昇编译器等,且上述三款编译器都是基于LLVM开发。编译器技术是一门庞大且繁杂的技术,对于初学者来说,这条学习之路道阻且长,盼那些热爱这门技术的赶路人能够行而不辍,未来可期。参考[1]cid:link_3[2]cid:link_2[3]cid:link_1[4]cid:link_0[5]cid:link_4后记欢迎加入Compiler SIG交流群与大家共同交流学习编译技术相关内容,扫码添加小助手微信邀请你进入Compiler SIG交流群。
-
鲲鹏软件性能调优学习笔记 在鲲鹏社区的鲲鹏软件性能调优实践微认证课程中,我主要学习了鲲鹏软件性能调优机制,了解和掌握了如何根据硬件特点进行性能调优,以及项目中性能调优的基本思路和常用性能采集工具。性能调优的本质是充分发挥硬件的性能,而通过软硬协同可以带来万倍的代码性能提升。从冯诺依曼计算机体系结构来看,性能的优化可以从四个层面来进行探讨,即CPU/内存、磁盘、网卡和应用。 1. 在CPU/内存层面基于鲲鹏处理器的加速可以分为软加速和硬加速。软加速又分为针对单核的单核加速和针对多核的多核加速。基于鲲鹏芯片微架构的编译器性能优化主要包括了指令布局优化、内存布局优化和循环优化。JDK性能优化主要包括序列化、JVM循环、JIT优化、GC优化和向量化等。在SMP系统中,核数的扩展受到内存总线的限制,可以采用非统一内存访问架构( Non-uniform memory access)解决这一问题。发挥NUMA性能,需要克服内存访问速度不均匀的挑战。利用CPU亲和性与内存分配策略,让进程与内存的距离更“短”。基于鲲鹏技术优势构建的加速库,针对基础、压缩、加解密和多媒体四类业务提供9大加速库,实现典型场景10%-100%性能提升。此外,鲲鹏RSA加密加速方案,相较于传统方案可以有效提升Web应用Https性能。2. 在磁盘层面文件系统决定了磁盘加载到内存过程的快慢。磁盘预取可以充分利用磁盘带宽。3. 在网卡层面网卡中断产生频率会影响应用的吞吐和延迟。具体而言,中断产生的频率高会消耗大量的CPU时间片,频率低会影响应用收到数据的时延。可以通过调整网卡中断聚合,在低时延和高吞吐中取一个平衡点。降低网卡中断频率,可以带来约10%的吞吐提升,但也会造增加20%左右的延迟。4. 在应用层面软件调优的本质是充分发挥硬件性能。减少资源抢占,提升并行度,发挥多核性能优势。Tcmalloc使用线程缓存,尺寸小于256K的小内存申请均由ThreadCache进行分配;通过ThreadCache分配过程中不需要任何锁,可以极大的提高分配速度。总结:鲲鹏软件性能调优主要通过以下手段实现。
-
玩转PB级分布式数仓GaussDB(DWS)性能调优黑科技玩转PB级数仓深度调优之依“计”行事
-
本讲为您介绍华为云数仓GaussDB(DWS) 调优的基本理论、常见的SQL性能问题的定位手段和解决方案
-
#### 1.功能描述 使能图算融合尝试网络性能调优 #### 2.功能简介 当前网络性能要进行调优的时候,可以尝试打开图算融合开关。而单纯设置图算融合为True的时候,并不一定挖掘了其所有的优化空间,在图算融合内部,优化的幅度是分等级的,一般分为以下4个等级。 * 0: Disable GraphKernel * 1: Enable GraphKernel with basic features only. * 2: Enable GraphKernel with all stable features. * 3: Enable GraphKernel with all experimental features. 对于当前的GPU网络使能图算融合而言,一般都是设置了enable_graph_kernel=True,而这个设置实际上只优化到了第二个等级,也即把基本的融合优化,和一些稳定的优化使能了。 #### 3.解决方法 正如前文提及的,图算融合的优化幅度是有等级的,具体的等级设置可以用一个flag `opt_level`来进行控制,具体的操作,则是可以在正式训练前使用context进行设置,如在run_train()一类的函数前加一行使能图算最高等级的优化: ``` context.set_context(enable_graph_kernel=True,graph_kernel_flags="--opt_level=3") ``` 这样便可将图算尚且还在实验中的一些优化也给加上了,通过这样的设置可以方便观察性能是否有了进一步的提升。 如下代码所示,分别是默认开图算和开图算且设置了最高阶优化的形式: ``` # Copyright 2021 Huawei Technologies Co., Ltd # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================ import numpy as np import mindspore.context as context from mindspore import Tensor import mindspore.nn as nn from mindspore.nn import Cell from mindspore.ops import operations as P import mindspore.ops.functional as F import pytest context.set_context(mode=context.GRAPH_MODE, device_target="GPU") # enable graph kernel optimization. context.set_context(enable_graph_kernel=True) class BertAttentionPiece(Cell): def __init__(self): super(BertAttentionPiece, self).__init__() self.add = P.Add() self.dropout = nn.Dropout(1 - 0.1) self.softmax = nn.Softmax() self.multiply_data = -10000.0 self.sub = P.Sub() self.multiply = P.Mul() self.get_dtype = P.DType() self.cast = P.Cast() def construct(self, attention_mask, attention_scores): multiply_out = self.sub(self.cast(F.tuple_to_array((1.0,)), self.get_dtype(attention_scores)), self.cast(attention_mask, self.get_dtype(attention_scores))) adder = self.multiply(multiply_out, self.multiply_data) attention_scores = self.add(adder, attention_scores) attention_probs = self.softmax(attention_scores) attention_probs = self.dropout(attention_probs) return attention_probs def get_rtol_atol(dtype): if dtype == np.float16: return 1.e-3, 1.e-3 return 1.e-4, 1.e-4 def compare_result(expect, output, dtype): rtol, atol = get_rtol_atol(dtype) if isinstance(expect, (list, tuple)): assert isinstance(output, (list, tuple)) and len(expect) == len(output) expect_list = list(expect) output_list = list(output) for e, o in zip(expect_list, output_list): assert np.allclose(e.asnumpy(), o.asnumpy(), rtol, atol, equal_nan=True) else: assert np.allclose(expect.asnumpy(), output.asnumpy(), rtol, atol, equal_nan=True) def get_softmax_output(x, y, use_experimental_features): # use experimental features such as stitch fusion. if use_experimental_features: context.set_context(graph_kernel_flags="--opt_level=3") net = BertAttentionPiece() result = net(x, y) return result def test_softmax(shape, dtype): np.random.seed(0) x = Tensor(np.random.normal(0, 1, shape).astype(dtype)) y = Tensor(np.random.normal(0, 1, shape).astype(dtype)) expect = get_softmax_output(x, y, False) output = get_softmax_output(x, y, True) compare_result(expect, output, dtype) def test_softmax_gpu(): context.set_context(mode=context.GRAPH_MODE, device_target="GPU") test_softmax([64, 12, 128, 128], np.float16) if __name__ == '__main__': test_softmax_gpu() ``` 在均能正确跑出结果的同时,使能了opt_level=3的话,会对这个用例进行一个stitch fusion的优化,以便获取更优性能。  上图为单纯使能图算会生成的融合pattern,为3个,而下图为使能了高阶优化后,图算融合会多生成一个融合pattern,增加了融合机会,从而能够获取更优性能。  #### 4.建议与总结 对于GPU后端而言,使能图算融合,一般可以直接打开进行优化尝试,也即优化等级开到2。而在Ascend后端上使能图算融合,可能优化等级开到2会有潜在Bug,如果想尝试图算优化,却遇到Bug的情况,请将opt_level设置为1,也即把最基本的融合优化打开试验性能的优劣情况。最后,尽管GPU后端可以尽可能的使能高的优化层级,但毕竟最高阶的优化在上面也有提及是属于内部实验阶段,可能打开后不一定会有优化,还需用户自己多进行尝试。 #### 5.相关参考文档 对于图算融合的内部flag设置,详情可以参考[图算flag定义文档](https://gitee.com/mindspore/mindspore/issues/I3MBFN) 下的说明,可以参照这个说明更灵活的使用图算融合,以尝试获取更优的网络性能。
-
网络训练跑通了,精度OK了,对很多人来说可能已经万事大吉了,但如果网络需要在生产环境跑,还有一个点不得不去关注,那就是性能。对于大的网络,训练一次可能需要上月的时间,这时候就真正的体会到时间就是金钱了。提高网络的性能,缩短训练的时间,可能会节省上百万的金钱。下面给大家介绍下之前对ResNet50网络的性能调优案例,希望能帮助到大家。调优过程中用到了MindSpore中的调试调优工具:MindInsight,不仅可以调试性能,还可以调试精度,感兴趣的同学可以到官网了解下:https://www.mindspore.cn/mindinsight现象我们将ResNet50网络batch size设置为32(代码可参考MindSpore ModelZoo中的ResNet50网络,当前代码已经是调优后的。本次主要给大家介绍下调优的经历),跑网络训练后发现单step时间约为90ms,性能很差。通常batch size为32时,单step耗时应在20ms以内。原因分析分析过程中,我们主要用到了MindInsight中的Profiler模块。如何跑Profiler收集性能数据大家可以到官网上查看教程,此次不再赘述。将性能数据用UI页面可视化后,我们开始了此次的性能调优过程。首先,我们先看页面里的迭代轨迹部分。迭代轨迹将训练过程分为了3部分:迭代间隙表示前一个迭代结束到后一个迭代开始的时间,该段时间可以认为是后一个迭代等待数据的时间;前反向时间表示的是真正的前反向计算的时间;迭代拖尾表示的是反向完成后进行梯度更新的时间。从下图中我们看到迭代间隙的时间占了整个迭代的很大部分,因此我们需要重点关注数据处理的性能。第二步,我们到数据准备详情页面,确认数据处理是否是性能瓶颈点。查看数据准备详情页面中的迭代间隙标签页,我们观察到,数据队列在前期有较多的数据,后期数据的个数变为0,分析原因是前期在图编译阶段已经开始了数据集的加载和增强,队列中随即缓存了多条数据;而后期正常训练开始后,队列中的数据被消费的速度要快于被生产的速度,因此数据队列逐渐变为空,说明此时数据变成了瓶颈。观察主机队列也是同样的情况。综合分析,正常训练过程中,数据处理为性能瓶颈点。第三步,我们到数据准备详情页面中的数据处理标签页来查看具体问题。通过观察数据处理标签页的算子间队列关系,我们发现,Queue_3及其之后的队列使用率较低,即MapOp_3作为生产者生产数据的速度较慢,因此可以判定MapOp_3的性能还有优化空间,需要对该算子进行性能优化。我们查看了数据处理的代码,发现map算子的num_parallel_workers参数没有设置,而该参数的默认为1,代码如下:if do_train: trans = [ C.RandomCropDecodeResize(image_size, scale=(0.08, 1.0), ratio=(0.75, 1.333)), C.RandomHorizontalFlip(prob=0.5), C.Normalize(mean=mean, std=std), C.HWC2CHW() ]else: trans = [ C.Decode(), C.Resize(256), C.CenterCrop(image_size), C.Normalize(mean=mean, std=std), C.HWC2CHW() ]data_set = data_set.map(operations=trans, input_columns="image")因此我们分析可以尝试提高该算子的并发数来提高网络的性能。效果验证我们将num_parallel_workers参数调整为12后,再次运行训练脚本,优化参考代码如下: data_set = data_set.map(operations=trans, input_columns="image", num_parallel_workers=12)通过MindInsight性能分析页面观察迭代轨迹,可以看到迭代间隙时长由72.8ms缩短到0.25ms,单step时长由90ms缩短到18.07ms。至此,我们完成了ResNet50网络的性能调优工作。总结MindInsight调优工具对性能调优和精度调优提供了大量的数据,并形成了一套完整的方法论,需要的同学可以参考奥。 MindSpore官方资料GitHub : https://github.com/mindspore-ai/mindsporeGitee : https : //gitee.com/mindspore/mindspore官方QQ群 : 871543426
-
网络训练跑通了,精度OK了,对很多人来说可能已经万事大吉了,但如果网络需要在生产环境跑,还有一个点不得不去关注,那就是性能。对于大的网络,训练一次可能需要上月的时间,这时候就真正的体会到时间就是金钱了。提高网络的性能,缩短训练的时间,可能会节省上百万的金钱。 下面给大家介绍下之前对ResNet50网络的性能调优案例,希望能帮助到大家。调优过程中用到了MindSpore中的调试调优工具:MindInsight,不仅可以调试性能,还可以调试精度,感兴趣的同学可以到官网了解下:https://www.mindspore.cn/mindinsight # 现象 我们将ResNet50网络batch size设置为32(代码可参考MindSpore ModelZoo中的ResNet50网络,当前代码已经是调优后的。本次主要给大家介绍下调优的经历),跑网络训练后发现单step时间约为90ms,性能很差。通常batch size为32时,单step耗时应在20ms以内。 # 原因分析 分析过程中,我们主要用到了MindInsight中的Profiler模块。如何跑Profiler收集性能数据大家可以到官网上查看教程,此次不再赘述。 将性能数据用UI页面可视化后,我们开始了此次的性能调优过程。 首先我们先看页面里的迭代轨迹部分。迭代轨迹将训练过程分为了3部分:迭代间隙表示前一个迭代结束到后一个迭代开始的时间,该段时间可以认为是后一个迭代等待数据的时间;前反向时间表示的是真正的前反向计算的时间;迭代拖尾表示的是反向完成后进行梯度更新的时间。从下图中我们看到迭代间隙的时间占了整个迭代的很大部分,因此我们需要重点关注数据处理的性能。  第二步,我们到数据准备详情页面,确认数据处理是否是性能瓶颈点。查看数据准备详情页面中的迭代间隙标签页,我们观察到,数据队列在前期有较多的数据,后期数据的个数变为0,分析原因是前期在图编译阶段已经开始了数据集的加载和增强,队列中随即缓存了多条数据;而后期正常训练开始后,队列中的数据被消费的速度要快于被生产的速度,因此数据队列逐渐变为空,说明此时数据变成了瓶颈。观察主机队列也是同样的情况。综合分析,正常训练过程中,数据处理为性能瓶颈点。  第三步,我们到数据准备详情页面中的数据处理标签页来查看具体问题。通过观察数据处理标签页的算子间队列关系,我们发现,Queue_3及其之后的队列使用率较低,即MapOp_3作为生产者生产数据的速度较慢,因此可以判定MapOp_3的性能还有优化空间,需要对该算子进行性能优化。  我们查看了数据处理的代码,发现map算子的num_parallel_workers参数没有设置,而该参数的默认为1,代码如下: if do_train: trans = [ C.RandomCropDecodeResize(image_size, scale=(0.08, 1.0), ratio=(0.75, 1.333)), C.RandomHorizontalFlip(prob=0.5), C.Normalize(mean=mean, std=std), C.HWC2CHW() ] else: trans = [ C.Decode(), C.Resize(256), C.CenterCrop(image_size), C.Normalize(mean=mean, std=std), C.HWC2CHW() ] data_set = data_set.map(operations=trans, input_columns="image") 因此我们分析可以尝试提高该算子的并发数来提高网络的性能。 # 效果验证 我们将num_parallel_workers参数调整为12后,再次运行训练脚本,优化参考代码如下: `data_set = data_set.map(operations=trans, input_columns="image", num_parallel_workers=12)` 通过MindInsight性能分析页面观察迭代轨迹,可以看到迭代间隙时长由72.8ms缩短到0.25ms,单step时长由90ms缩短到18.07ms。  至此,我们完成了ResNet50网络的性能调优工作。 # 总结 MindInsight调优工具对性能调优和精度调优提供了大量的数据,并形成了一套完整的方法论,需要的同学可以参考奥。
-
目前使用鹏城实验室提供的云端环境,在Ascend 910上运行TensorFlow代码,目前需要做性能调优,进行Profiling分析,但是查看昇腾官方文档或者官方仓库Wiki,如下所示,都需要root权限下进入指定文件夹,寻找脚本,但是云端环境无法获取root权限,请问有什么办法解决吗?1. https://support.huaweicloud.com/Development-tg-cann202training1/atlasprofilingtrain_16_0015.html2. https://gitee.com/ascend/modelzoo/wikis/%E8%AE%AD%E7%BB%83%E6%80%A7%E8%83%BD%E5%A4%AA%E5%B7%AE%E4%BA%86%EF%BC%8C%E6%9C%89%E4%BB%80%E4%B9%88%E5%A5%BD%E5%8A%9E%E6%B3%95%E5%90%97%EF%BC%9F%EF%BC%88Apulis%E7%89%88%EF%BC%89?sort_id=3148795如图,无root权限:
-
前言关于单点性能调优的帖子很多,这里从基础理论、实战演练、现网案例三个维度汇总单点性能调优的方法基础理论analyze统计信息https://bbs.huaweicloud.com/blogs/192029explain计划信息https://bbs.huaweicloud.com/blogs/197945分布式计划详解https://bbs.huaweicloud.com/blogs/200449实战演练总体调优策略https://bbs.huaweicloud.com/blogs/200906味道SQL识别https://bbs.huaweicloud.com/blogs/197413好味道表定义https://bbs.huaweicloud.com/blogs/203219SQL改写https://bbs.huaweicloud.com/blogs/203420路径干预https://bbs.huaweicloud.com/blogs/212459Plan hint运用https://bbs.huaweicloud.com/blogs/213195现网案例单点性能案例集锦https://bbs.huaweicloud.com/forum/thread-90331-1-1.html
-
【功能模块】用VSCode 安装性能分析工具报空间不足安装过程:【操作步骤&问题现象】报错:请帮忙查看该问题【日志信息】(可选,上传日志内容或者附件)
-
【操作步骤&问题现象】1、添加采样分析报错,且采样状态卡死【截图信息】
-
【操作步骤&问题现象】1、连接目标环境 非root账户 报联系管理2、具体需要那个写文件权限【截图信息】
推荐直播
-
HDC深度解读系列 - Serverless与MCP融合创新,构建AI应用全新智能中枢2025/08/20 周三 16:30-18:00
张昆鹏 HCDG北京核心组代表
HDC2025期间,华为云展示了Serverless与MCP融合创新的解决方案,本期访谈直播,由华为云开发者专家(HCDE)兼华为云开发者社区组织HCDG北京核心组代表张鹏先生主持,华为云PaaS服务产品部 Serverless总监Ewen为大家深度解读华为云Serverless与MCP如何融合构建AI应用全新智能中枢
回顾中 -
关于RISC-V生态发展的思考2025/09/02 周二 17:00-18:00
中国科学院计算技术研究所副所长包云岗教授
中科院包云岗老师将在本次直播中,探讨处理器生态的关键要素及其联系,分享过去几年推动RISC-V生态建设实践过程中的经验与教训。
回顾中 -
一键搞定华为云万级资源,3步轻松管理企业成本2025/09/09 周二 15:00-16:00
阿言 华为云交易产品经理
本直播重点介绍如何一键续费万级资源,3步轻松管理成本,帮助提升日常管理效率!
回顾中
热门标签