性能优化随笔(一)

在软件开发过程中,一般要先实现功能方面的需求,功能方面的需求开发完毕之后,往往会考虑性能方面的优化。在业务发展的初期,性能往往能满足使用的需求,这时性能优化不是必不可少的。随着业务的发展,软件复杂度的提高,性能有时会成为瓶颈,这时性能优化是必须要做的工作。

1 性能优化的一些概念

(1)性能优化的两个方面:软件和硬件

从广义上来说,性能可以从硬件和软件两个方面来优化。比如网络方面的性能优化,网卡带宽从 10M、100M 到现在的 1G、10G 甚至 100G 的网卡,同样软件的情况下,网卡硬件性能的提高可以带来整体性能的提高。内存,磁盘,cpu 等硬件,在过去的发展中,性能都得到了很大的提升。另一方面是通过优化软件来做性能优化,在实际开发中,性能优化的工作大多都是在硬件不改变前提下,通过优化软件的方式来达到提高性能的目的。

(2)性能优化的指标

性能优化的指标是评价性能高低的,可量化的指标。性能优化的指标有一些是通用的指标,有一些是和业务场景强相关的指标。

① 基础通用指标

通用的指标,包括资源的使用率,比如软件占用的 cpu、内存、磁盘的多少,这些指标是最基础的,往往也是最重要的。

② QPS

在互联网服务中经常关注这个指标, QPS(Queries Per Second),即服务每秒钟可以响应的查询次数。

③ 延时和吞吐率

在网络应用中,经常关注这两个指标。延时是指通信的双方一发一收的时间为一次通信延时,多次统计的平均值是平均延时。吞吐率指的是单位时间内处理的网络报文的个数。延时越低,性能越好;吞吐率越高,性能越好。

在硬件条件不变的情况下,延时和吞吐率往往是此消彼长的两个方面,延时降低之后,吞吐率往往会降低;吞吐率提升之后,延时往往会增长。之所以说硬件条件不变的前提,是因为从硬件方面来优化的话,延时和吞吐率可以同时优化,比如网卡从 10M 升级到 100G,那么延时和吞吐率都会提升。类似于马路上的车流量,如果正常情况下,马路上没有专用车辆(警车,救护车等),都是普通的家用车,这时候车辆可以随便开,每辆车都可以自由变道,这种情况下马路的吞吐量是最大化的。而如果这个时候马路上出现了一辆救护车,普通的家用车要给救护车让出一条专用道出来,那么普通家用车可用的车道数量就变少了,救护车的延时降低了,普通车的延时增大了,总体的吞吐率也降低了。

延时也不仅仅局限于网络性能指标,在我们开发的很多应用中,延时都是很重要很关键的一个指标。特别是现在服务器内存和磁盘相对充裕的情况下,多用一点内存或者磁盘,不是那么的严重,这时延时是最能体现业务性能的指标。

(3)平均值还是单次的最值

以延时为例,有些情况下考虑的是延时的平均值,比如路由器产品中,往往关注平均延时。互联网服务中,很多时候也关注平均延时。而有些情况下,关注的是每一次的延时,这种使用场景,吞吐量往往不是很大。比如智能驾驶系统中,业务处理时,从传感器获取到环境信息到感知模块处理,到规划、控制模块处理,到最后下发车辆的实际控制信号(减速,绕行等),整个流程的时间是有最大要求的,比如每一次的时间都不能大于 10ms,智能驾驶系统对安全性的要求非常高,要求每次都要满足这样的要求。如果有两个系统,一个系统的平均延时能到 5ms,但是偶尔的延时会达到 15ms;另一个系统的平均延时是 9ms,最大延时不会超过 10ms。那么第二种系统是满足要求的。

(4)性能测试工具

在性能优化过程中,性能测试工具是必不可少的。只有用性能测试工具对比测试出优化前后的性能指标,才能知道我们的优化是不是有效。性能测试工具,比如 linux 中的 ftrace,perf,是 linux 中自带的测试工具;还有现在比较热的 ebpf,也可以用来做性能测试;用于网络性能测试的 iperf 等。很多时候,性能测试也需要我们在代码中打桩,自己开发工具进行测试。

(5)软件架构和实现细节

在做软件性能优化时,不仅要优化软件的实现细节,有时候也需要优化架构方案。当然改变架构比改变细节的工作量要大一些。架构设计和方案的选择要尽量在架构和方案评审时做充分的验证与讨论,争取选择一个最优的架构,以防到后边再做大的修改。

2 epoll

epoll 作为一种多路复用技术,相比于 select 和 poll 来说,在很多场景下都是有性能提升的。

[linux][epoll] 带着 6 个问题深入理解 epoll

epoll 相对于 select 和 poll 的优化,相当于在架构上做了优化。有时候,对架构进行优化,不是在旧的架构上进行优化,而是新定义了一种机制。比如这里的 epoll 和 select 与 poll;比如开发语言中的 c 和 c++,c++ 在 c 的基础上增加了面向对象的编程,但是支持之后的语言已经不是 c 语言了,而是成为了 c++。

3 内存

很多优化工作都涉及到内存。

(1)内存池

如果使用的内存需要频繁的申请和释放,那么使用内存池比较合适,防止每次申请和释放内存都要和系统打交道。linux 内核中的 slab 就是内存池。我们在做应用开发时,有时也会自己写内存池。

内存池往往有两个变量,一个是内存池中每个 buffer 的长度,一个是内存池中 buffer 的数量。在实际使用中,实际数据的大小是不一样,我们需要根据数据大小的分布使用不同的内存池,比如长度小于 128 字节的就使用第一个内存池,小于 1024 字节就使用第二个内存池,小于 4096 字节就使用第三个内存池。

slab 是内核中使用的内存池,其中 kmalloc-8 到 kmalloc-8k 是内核默认创建的内存池,每个内存池中内存块的长度从 8B 到 8KB 不等。另外,内核中的其它子系统也可以使用 slab 创建自己的内存池,比如 task_struct 内存池,是用来管理 task_struct 的,一个 task_struct 代表一个线程;dentry 和 inode 内存池是文件系统用来保存 inode 和 dentry 的;tcp 相关的内存池用于 tcp 创建连接和断开连接的过程。

root@wyl-virtual-machine:/home/wyl# cat /proc/slabinfo
slabinfo - version: 2.1
# name            <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> : tunables <limit> <batchcount> <sharedfactor> : slabdata <active_slabs> <num_slabs> <sharedavail>
isofs_inode_cache    147    147    656   49    8 : tunables    0    0    0 : slabdata      3      3      0
ext4_groupinfo_4k    840    840    144   56    2 : tunables    0    0    0 : slabdata     15     15      0
fsverity_info          0      0    248   66    4 : tunables    0    0    0 : slabdata      0      0      0
ip6-frags              0      0    184   44    2 : tunables    0    0    0 : slabdata      0      0      0
PINGv6               130    130   1216   26    8 : tunables    0    0    0 : slabdata      5      5      0
RAWv6                494    494   1216   26    8 : tunables    0    0    0 : slabdata     19     19      0
UDPv6                120    120   1344   24    8 : tunables    0    0    0 : slabdata      5      5      0
tw_sock_TCPv6          0      0    248   66    4 : tunables    0    0    0 : slabdata      0      0      0
request_sock_TCPv6      0      0    304   53    4 : tunables    0    0    0 : slabdata      0      0      0
TCPv6                104    104   2432   13    8 : tunables    0    0    0 : slabdata      8      8      0
kcopyd_job             0      0   3312    9    8 : tunables    0    0    0 : slabdata      0      0      0
dm_uevent              0      0   2632   12    8 : tunables    0    0    0 : slabdata      0      0      0
scsi_sense_cache    1536   1536    128   64    2 : tunables    0    0    0 : slabdata     24     24      0
mqueue_inode_cache     34     34    960   34    8 : tunables    0    0    0 : slabdata      1      1      0
fuse_request         168    168    144   56    2 : tunables    0    0    0 : slabdata      3      3      0
fuse_inode           117    117    832   39    8 : tunables    0    0    0 : slabdata      3      3      0
ecryptfs_key_record_cache      0      0    576   56    8 : tunables    0    0    0 : slabdata      0      0      0
ecryptfs_inode_cache      0      0   1024   32    8 : tunables    0    0    0 : slabdata      0      0      0
ecryptfs_file_cache      0      0     16  256    1 : tunables    0    0    0 : slabdata      0      0      0
ecryptfs_auth_tok_list_item      0      0    832   39    8 : tunables    0    0    0 : slabdata      0      0      0
fat_inode_cache       44     44    744   44    8 : tunables    0    0    0 : slabdata      1      1      0
fat_cache              0      0     40  102    1 : tunables    0    0    0 : slabdata      0      0      0
squashfs_inode_cache    414    414    704   46    8 : tunables    0    0    0 : slabdata      9      9      0
jbd2_journal_handle    340    340     48   85    1 : tunables    0    0    0 : slabdata      4      4      0
jbd2_journal_head   1020   1020    120   68    2 : tunables    0    0    0 : slabdata     15     15      0
jbd2_revoke_table_s    256    256     16  256    1 : tunables    0    0    0 : slabdata      1      1      0
ext4_inode_cache   10267  11513   1096   29    8 : tunables    0    0    0 : slabdata    397    397      0
ext4_allocation_context    256    256    128   64    2 : tunables    0    0    0 : slabdata      4      4      0
ext4_system_zone     102    102     40  102    1 : tunables    0    0    0 : slabdata      1      1      0
ext4_io_end          256    256     64   64    1 : tunables    0    0    0 : slabdata      4      4      0
ext4_pending_reservation    512    512     32  128    1 : tunables    0    0    0 : slabdata      4      4      0
ext4_extent_status   7752   7752     40  102    1 : tunables    0    0    0 : slabdata     76     76      0
mbcache              292    292     56   73    1 : tunables    0    0    0 : slabdata      4      4      0
userfaultfd_ctx_cache      0      0    192   42    2 : tunables    0    0    0 : slabdata      0      0      0
dnotify_struct         0      0     32  128    1 : tunables    0    0    0 : slabdata      0      0      0
pid_namespace          0      0    208   39    2 : tunables    0    0    0 : slabdata      0      0      0
UNIX                3810   3810   1088   30    8 : tunables    0    0    0 : slabdata    127    127      0
ip4-frags              0      0    200   40    2 : tunables    0    0    0 : slabdata      0      0      0
xfrm_state             0      0    704   46    8 : tunables    0    0    0 : slabdata      0      0      0
PING                   0      0   1024   32    8 : tunables    0    0    0 : slabdata      0      0      0
RAW                  832    832   1024   32    8 : tunables    0    0    0 : slabdata     26     26      0
tw_sock_TCP           66     66    248   66    4 : tunables    0    0    0 : slabdata      1      1      0
request_sock_TCP      53     53    304   53    4 : tunables    0    0    0 : slabdata      1      1      0
TCP                  126    126   2240   14    8 : tunables    0    0    0 : slabdata      9      9      0
hugetlbfs_inode_cache     51     51    632   51    8 : tunables    0    0    0 : slabdata      1      1      0
dquot                256    256    256   64    4 : tunables    0    0    0 : slabdata      4      4      0
eventpoll_pwq       3080   3080     72   56    1 : tunables    0    0    0 : slabdata     55     55      0
dax_cache             42     42    768   42    8 : tunables    0    0    0 : slabdata      1      1      0
request_queue         60     60   2104   15    8 : tunables    0    0    0 : slabdata      4      4      0
biovec-max           112    112   4096    8    8 : tunables    0    0    0 : slabdata     14     14      0
biovec-128            64     64   2048   16    8 : tunables    0    0    0 : slabdata      4      4      0
biovec-64            128    128   1024   32    8 : tunables    0    0    0 : slabdata      4      4      0
khugepaged_mm_slot      0      0    112   36    1 : tunables    0    0    0 : slabdata      0      0      0
user_namespace        61     61    536   61    8 : tunables    0    0    0 : slabdata      1      1      0
uid_cache            256    256    128   64    2 : tunables    0    0    0 : slabdata      4      4      0
dmaengine-unmap-256     15     15   2112   15    8 : tunables    0    0    0 : slabdata      1      1      0
dmaengine-unmap-128     30     30   1088   30    8 : tunables    0    0    0 : slabdata      1      1      0
sock_inode_cache    5850   5850    832   39    8 : tunables    0    0    0 : slabdata    150    150      0
skbuff_ext_cache     168    168    192   42    2 : tunables    0    0    0 : slabdata      4      4      0
skbuff_fclone_cache    256    256    512   64    8 : tunables    0    0    0 : slabdata      4      4      0
skbuff_head_cache  10176  10432    256   64    4 : tunables    0    0    0 : slabdata    163    163      0
configfs_dir_cache     46     46     88   46    1 : tunables    0    0    0 : slabdata      1      1      0
file_lock_cache      148    148    216   37    2 : tunables    0    0    0 : slabdata      4      4      0
fsnotify_mark_connector    640    640     32  128    1 : tunables    0    0    0 : slabdata      5      5      0
net_namespace         18     18   4800    6    8 : tunables    0    0    0 : slabdata      3      3      0
task_delay_info     4743   4743     80   51    1 : tunables    0    0    0 : slabdata     93     93      0
taskstats            188    188    344   47    4 : tunables    0    0    0 : slabdata      4      4      0
proc_dir_entry      1050   1050    192   42    2 : tunables    0    0    0 : slabdata     25     25      0
pde_opener          4998   4998     40  102    1 : tunables    0    0    0 : slabdata     49     49      0
proc_inode_cache   12165  12816    680   48    8 : tunables    0    0    0 : slabdata    267    267      0
bdev_cache           156    156    832   39    8 : tunables    0    0    0 : slabdata      4      4      0
shmem_inode_cache   3061   3105    720   45    8 : tunables    0    0    0 : slabdata     69     69      0
kernfs_node_cache 117286 117480    136   60    2 : tunables    0    0    0 : slabdata   1958   1958      0
mnt_cache           1887   1887    320   51    4 : tunables    0    0    0 : slabdata     37     37      0
filp               38146  40064    256   64    4 : tunables    0    0    0 : slabdata    626    626      0
inode_cache        53611  54166    608   53    8 : tunables    0    0    0 : slabdata   1022   1022      0
dentry             81112  84840    192   42    2 : tunables    0    0    0 : slabdata   2020   2020      0
names_cache           40     40   4096    8    8 : tunables    0    0    0 : slabdata      5      5      0
iint_cache             0      0    128   64    2 : tunables    0    0    0 : slabdata      0      0      0
lsm_file_cache      7820   7820     24  170    1 : tunables    0    0    0 : slabdata     46     46      0
buffer_head        30797  31824    104   39    1 : tunables    0    0    0 : slabdata    816    816      0
uts_namespace        111    111    440   37    4 : tunables    0    0    0 : slabdata      3      3      0
nsproxy              292    292     56   73    1 : tunables    0    0    0 : slabdata      4      4      0
vm_area_struct     49493  50466    208   39    2 : tunables    0    0    0 : slabdata   1294   1294      0
files_cache         2438   2438    704   46    8 : tunables    0    0    0 : slabdata     53     53      0
signal_cache        2268   2268   1152   28    8 : tunables    0    0    0 : slabdata     81     81      0
sighand_cache       1200   1200   2112   15    8 : tunables    0    0    0 : slabdata     80     80      0
task_struct         1053   1135   6016    5    8 : tunables    0    0    0 : slabdata    227    227      0
cred_jar            6678   6678    192   42    2 : tunables    0    0    0 : slabdata    159    159      0
anon_vma_chain     37205  37632     64   64    1 : tunables    0    0    0 : slabdata    588    588      0
anon_vma           18853  19773    104   39    1 : tunables    0    0    0 : slabdata    507    507      0
pid                 9728   9728    128   64    2 : tunables    0    0    0 : slabdata    152    152      0
Acpi-Operand       10696  10696     72   56    1 : tunables    0    0    0 : slabdata    191    191      0
Acpi-ParseExt        273    273    104   39    1 : tunables    0    0    0 : slabdata      7      7      0
Acpi-State          1020   1020     80   51    1 : tunables    0    0    0 : slabdata     20     20      0
numa_policy          186    186    264   62    4 : tunables    0    0    0 : slabdata      3      3      0
trace_event_file    1554   1554     96   42    1 : tunables    0    0    0 : slabdata     37     37      0
ftrace_event_field  11985  11985     48   85    1 : tunables    0    0    0 : slabdata    141    141      0
pool_workqueue       768    768    256   64    4 : tunables    0    0    0 : slabdata     12     12      0
radix_tree_node    12833  14784    584   56    8 : tunables    0    0    0 : slabdata    264    264      0
task_group           204    204    640   51    8 : tunables    0    0    0 : slabdata      4      4      0
mm_struct           1770   1770   1088   30    8 : tunables    0    0    0 : slabdata     59     59      0
vmap_area           2816   2816     64   64    1 : tunables    0    0    0 : slabdata     44     44      0
dma-kmalloc-8k         0      0   8192    4    8 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-4k         0      0   4096    8    8 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-2k         0      0   2048   16    8 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-1k         0      0   1024   32    8 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-512        0      0    512   64    8 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-256        0      0    256   64    4 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-128        0      0    128   64    2 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-64         0      0     64   64    1 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-32         0      0     32  128    1 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-16         0      0     16  256    1 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-8          0      0      8  512    1 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-192        0      0    192   42    2 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-96         0      0     96   42    1 : tunables    0    0    0 : slabdata      0      0      0
kmalloc-rcl-8k         0      0   8192    4    8 : tunables    0    0    0 : slabdata      0      0      0
kmalloc-rcl-4k         0      0   4096    8    8 : tunables    0    0    0 : slabdata      0      0      0
kmalloc-rcl-2k         0      0   2048   16    8 : tunables    0    0    0 : slabdata      0      0      0
kmalloc-rcl-1k         0      0   1024   32    8 : tunables    0    0    0 : slabdata      0      0      0
kmalloc-rcl-512        0      0    512   64    8 : tunables    0    0    0 : slabdata      0      0      0
kmalloc-rcl-256        0      0    256   64    4 : tunables    0    0    0 : slabdata      0      0      0
kmalloc-rcl-192        0      0    192   42    2 : tunables    0    0    0 : slabdata      0      0      0
kmalloc-rcl-128      960    960    128   64    2 : tunables    0    0    0 : slabdata     15     15      0
kmalloc-rcl-96      1303   1344     96   42    1 : tunables    0    0    0 : slabdata     32     32      0
kmalloc-rcl-64      4892   5376     64   64    1 : tunables    0    0    0 : slabdata     84     84      0
kmalloc-rcl-32         0      0     32  128    1 : tunables    0    0    0 : slabdata      0      0      0
kmalloc-rcl-16         0      0     16  256    1 : tunables    0    0    0 : slabdata      0      0      0
kmalloc-rcl-8          0      0      8  512    1 : tunables    0    0    0 : slabdata      0      0      0
kmalloc-8k           392    392   8192    4    8 : tunables    0    0    0 : slabdata     98     98      0
kmalloc-4k          2510   2552   4096    8    8 : tunables    0    0    0 : slabdata    319    319      0
kmalloc-2k          2224   2224   2048   16    8 : tunables    0    0    0 : slabdata    139    139      0
kmalloc-1k          5792   5792   1024   32    8 : tunables    0    0    0 : slabdata    181    181      0
kmalloc-512        31872  31872    512   64    8 : tunables    0    0    0 : slabdata    498    498      0
kmalloc-256         4352   4352    256   64    4 : tunables    0    0    0 : slabdata     68     68      0
kmalloc-192         4284   4284    192   42    2 : tunables    0    0    0 : slabdata    102    102      0
kmalloc-128         2240   2240    128   64    2 : tunables    0    0    0 : slabdata     35     35      0
kmalloc-96          3864   3864     96   42    1 : tunables    0    0    0 : slabdata     92     92      0
kmalloc-64         23352  23552     64   64    1 : tunables    0    0    0 : slabdata    368    368      0
kmalloc-32         35968  35968     32  128    1 : tunables    0    0    0 : slabdata    281    281      0
kmalloc-16         14848  14848     16  256    1 : tunables    0    0    0 : slabdata     58     58      0
kmalloc-8          16896  16896      8  512    1 : tunables    0    0    0 : slabdata     33     33      0
kmem_cache_node     2112   2112     64   64    1 : tunables    0    0    0 : slabdata     33     33      0
kmem_cache          1980   1980    448   36    4 : tunables    0    0    0 : slabdata     55     55      0
root@wyl-virtual-machine:/home/wyl#

(2)内存池的内存如何从系统申请 ?

假如我们要创建一个内存池,这个内存池中的每个 buffer 大小是 1024 个字节的长度,共申请 128 个 buffer。那么我们是每个 buffer 单独申请,还是申请总大小为 1024 * 128 内存,然后再自己做切分。当然是选择后者,因为后者申请的内存是一整块内存,这些内存都挨着,一方面可以减少内存的碎片化,一方面可以增加内存的缓存命中率(内存挨着,从缓存 LRU 算法的角度来看的话,可以提升缓存的命中率)。

(3)内存池的本地化

在网络应用中,往往给每个 cpu 核都申请内存池,这样每个核都有自己的内存池,在申请或释放内存的时候不用加锁,对性能友好。

(4)cpu cache 和文件系统 page cache

因为 cpu 的速度远远高于内存的访问速度,访问内存的速度又远远高于访问磁盘的速度,所以在 cpu 和内存之间以及 cpu 和磁盘之间都加了缓存。这两个缓存没有任何关系,只不过都起到了提高性能的作用,思路是类似的,所以放在这里一起说。

cpu cache 是在 cpu 和内存之间,用来增大内存访问速度的,cpu cache 硬件本身就在 cpu 中。page cache 是对访问文件系统的优化,本身使用的是内存,防止每次读写文件都要直接读写磁盘,提高文件系统的读写性能。

(5)大页

操作系统管理内存的基本单位是页,常用的页的大小是 4KB。

我们程序使用的地址都是虚拟地址,cpu 在访问内存的时候首先要将虚拟地址转换为物理地址,承担这个工作的就是 MMU,MMU 是一种硬件,集成在 cpu 中。在地址转换时,首先要查页表,页表一般缓存在 TLB 里,如果不在 TLB 里,那么就去内存中查找页表,从页表中可以找到虚拟地址对应的物理内存。如果同样大小的内存,比如 16MB,如果页的大小都是 4KB,那么 TLB 中要保存 4K 个页表项,如果页的大小是 16MB,那么 TLB 中只要保存一个页表项就可以了。TLB 一般很小,不会保存太多的页表项,这就是大页的好处,可以提升 TLB 的命中率。

(6)减少内存拷贝

减少内存拷贝很常见的,也是很容易想到的一个性能优化点。

4 cpu

提高 cpu 调度性能的方法主要有两个:

(1)调度策略和优先级

改变线程的调度策略,比如从SCHED_NORMAL 改成 SCHED_FIFO,或者提高线程的调度优先级,比如优先级从 1 提高到 99,可以提升调度性能。

linux 调度策略的几点理解_属于linux实时调度策略-CSDN博客

(2)线程绑核甚至独占

将线程绑定到指定核上,这样这个线程就只能运行在这个 cpu 核上,这样可以提升线程使用内存的 cache 命中率。试想,如果线程一会运行在一个核上,一会又换到另一个核上运行,那么当线程换到另外一个核上运行的时候,访问的内存需要重新加载到 cache(一级 cache ),一级 cache 一般是每个 cpu 核独有的。

绑核可以提高缓存的命中率,但是并不一定能提升线程的调度性能,如果有很多个线程都绑定在了同一个核上,那么这么多个线程都要在这一个核上排队调度,性能也不会高。要想让自己的目标线程的调度性能高,还要让线程独占,独占的话,就是把自己的目标线程绑定到一个核上,把其它的线程绑定到其它核上,这样就保证了独占。

  • 22
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值