0803-PyTorch的Debug指南

pytorch完整教程目录:https://www.cnblogs.com/nickchen121/p/14662511.html

一、ipdb 介绍

很多初学 python 的同学会使用 print 或 log 调试程序,但是这只在小规模的程序下调试很方便,更好的调试应该是在一边运行的时候一边检查里面的变量和方法。

感兴趣的可以去了解 pycharm 的 debug 模式,功能也很强大,能够满足一般的需求,这里不多做赘述,我们这里介绍一个更适用于 pytorch 的一个灵活的 pdb 交互式调试工具。

Pdb 是一个交互式的调试工具,集成与 Python 标准库中,它能让你根据需求跳转到任意的 Python 代码断点、查看任意变量、单步执行代码,甚至还能修改变量的值,而没有必要去重启程序。

ipdb 则是一个增强版的 pdb,它提供了调试模式下的代码自动补全,还有更好的语法高亮和代码溯源,以及更好的内省功能,最重要的是它和 pdb 接口完全兼容,可以通过 pip install ipdb 安装。

二、ipdb 的使用

首先看一个例子,要使用 ipdb 的话,只需要在想要进行调试的地方插入 ipdb.set_trace(),当代码运行到这个地方时,就会自动进入交互式调试模式。

import ipdb

def sum(x):
r = 0
for ii in x:
r += ii
return r def mul(x):
r = 1
for ii in x:
r *= 11
return r ipdb.set_trace()
x = [1, 2, 3, 4, 5]
r = sum(x)
r = mul(x)
> /Users/mac/Desktop/jupyter/test.py(19)<module>()
18 ipdb.set_trace()
---> 19 x = [1, 2, 3, 4, 5]
20 r = sum(x) ipdb> l 1,5 # l(ist) 1,5 的缩写,查看第 1 行到第 5 行的代码
1 import ipdb
2
3
4 def sum(x):
5 r = 0 ipdb> n # n(ext) 的缩写执行下一步
> /Users/mac/Desktop/jupyter/test.py(20)<module>()
19 x = [1, 2, 3, 4, 5]
---> 20 r = sum(x)
21 r = mul(x) ipdb> s # s(tep) 的缩写,进入 sum 函数内部
--Call--
> /Users/mac/Desktop/jupyter/test.py(4)sum()
3
----> 4 def sum(x):
5 r = 0 ipdb> n # n(ext) 单步执行
> /Users/mac/Desktop/jupyter/test.py(5)sum()
4 def sum(x):
----> 5 r = 0
6 for ii in x: ipdb> n
> /Users/mac/Desktop/jupyter/test.py(6)sum()
5 r = 0
----> 6 for ii in x:
7 r += ii ipdb> u # u(p) 的缩写,调回上一层的调用
> /Users/mac/Desktop/jupyter/test.py(20)<module>()
19 x = [1, 2, 3, 4, 5]
---> 20 r = sum(x)
21 r = mul(x) ipdb> d # d(own) 的缩写,跳到调用的下一层
> /Users/mac/Desktop/jupyter/test.py(6)sum()
5 r = 0
----> 6 for ii in x:
7 r += ii ipdb> n
> /Users/mac/Desktop/jupyter/test.py(7)sum()
6 for ii in x:
----> 7 r += ii
8 return r ipdb> !r # 查看变量 r 的值,该变量名与调试命令 `r(eturn)` 冲突
0 ipdb> return # 继续运行知道函数返回
--Return--
15
> /Users/mac/Desktop/jupyter/test.py(8)sum()
7 r += ii
----> 8 return r
9 ipdb> n
> /Users/mac/Desktop/jupyter/test.py(21)<module>()
19 x = [1, 2, 3, 4, 5]
20 r = sum(x)
---> 21 r = mul(x) ipdb> x # 查看变量 x
[1, 2, 3, 4, 5] ipdb> x[0] = 10000 # 修改变量 x ipdb> x
[10000, 2, 3, 4, 5] ipdb> b 12 # b(reak) 的缩写,在第 10 行设置断点
Breakpoint 1 at /Users/mac/Desktop/jupyter/test.py:12 ipdb> c # c(ontinue) 的缩写,继续运行,直到遇到断点
> /Users/mac/Desktop/jupyter/test.py(12)mul()
11 def mul(x):
1--> 12 r = 1
13 for ii in x: ipdb> return # 可以看到计算的是修改之后的 x 的乘积
--Return--
1200000
> /Users/mac/Desktop/jupyter/test.py(15)mul()
14 r *= ii
---> 15 return r
16 ipdb> q # q(uit) 的缩写,退出 debug

上述只是给出了 ipdb 的一部分使用方法,关于 ipdb 还有一些小的使用技巧:

  • 键能够自动补齐,补齐用法和 IPython 中的类似
  • j(ump) 能够跳过中间某些行的代码的执行
  • 可以直接在 ipdb 中修改变量的值
  • help 能够查看调试命令的用法,比如 h h 可以查看 help 命令的用法,h j(ump) 能够查看 j(ump) 命令的用法

三、在 PyTorch 中 Debug

PyTorch 作为一个动态图框架,和 ipdb 结合使用能够让调试过程更加便捷,下面我们将距离说明以下三点:

  • 如何在 PyTorch 中查看神经网络各个层的输出
  • 如何在 PyTorch 中分析各个参数的梯度
  • 如何动态修改 PyTorch 的训练流程

首先,运行上一篇文章给出的“猫狗大战”程序:python main.py train --debug-file='debug/debug.txt'

程序运行一段时间后,在debug目录下创建debug.txt标识文件,当程序检测到这个文件存在时,会自动进入debug模式。

99it [00:17,  6.07it/s]loss: 0.22854854568839075
119it [00:21, 5.79it/s]loss: 0.21267264398435753
139it [00:24, 5.99it/s]loss: 0.19839374726372108
> e:/Users/mac/Desktop/jupyter/mdFile/deeplearning/main.py(80)train()
79 loss_meter.reset()
---> 80 confusion_matrix.reset()
81 for ii, (data, label) in tqdm(enumerate(train_dataloader)): ipdb> break 88 # 在第88行设置断点,当程序运行到此处进入debug模式
Breakpoint 1 at e:/Users/mac/Desktop/jupyter/mdFile/deeplearning/main.py:88 ipdb> # 打印所有参数及其梯度的标准差
for (name,p) in model.named_parameters(): \
print(name,p.data.std(),p.grad.data.std())
model.features.0.weight tensor(0.2615, device='cuda:0') tensor(0.3769, device='cuda:0')
model.features.0.bias tensor(0.4862, device='cuda:0') tensor(0.3368, device='cuda:0')
model.features.3.squeeze.weight tensor(0.2738, device='cuda:0') tensor(0.3023, device='cuda:0')
model.features.3.squeeze.bias tensor(0.5867, device='cuda:0') tensor(0.3753, device='cuda:0')
model.features.3.expand1x1.weight tensor(0.2168, device='cuda:0') tensor(0.2883, device='cuda:0')
model.features.3.expand1x1.bias tensor(0.2256, device='cuda:0') tensor(0.1147, device='cuda:0')
model.features.3.expand3x3.weight tensor(0.0935, device='cuda:0') tensor(0.1605, device='cuda:0')
model.features.3.expand3x3.bias tensor(0.1421, device='cuda:0') tensor(0.0583, device='cuda:0')
model.features.4.squeeze.weight tensor(0.1976, device='cuda:0') tensor(0.2137, device='cuda:0')
model.features.4.squeeze.bias tensor(0.4058, device='cuda:0') tensor(0.1798, device='cuda:0')
model.features.4.expand1x1.weight tensor(0.2144, device='cuda:0') tensor(0.4214, device='cuda:0')
model.features.4.expand1x1.bias tensor(0.4994, device='cuda:0') tensor(0.0958, device='cuda:0')
model.features.4.expand3x3.weight tensor(0.1063, device='cuda:0') tensor(0.2963, device='cuda:0')
model.features.4.expand3x3.bias tensor(0.0489, device='cuda:0') tensor(0.0719, device='cuda:0')
model.features.6.squeeze.weight tensor(0.1736, device='cuda:0') tensor(0.3544, device='cuda:0')
model.features.6.squeeze.bias tensor(0.2420, device='cuda:0') tensor(0.0896, device='cuda:0')
model.features.6.expand1x1.weight tensor(0.1211, device='cuda:0') tensor(0.2428, device='cuda:0')
model.features.6.expand1x1.bias tensor(0.0670, device='cuda:0') tensor(0.0162, device='cuda:0')
model.features.6.expand3x3.weight tensor(0.0593, device='cuda:0') tensor(0.1917, device='cuda:0')
model.features.6.expand3x3.bias tensor(0.0227, device='cuda:0') tensor(0.0160, device='cuda:0')
model.features.7.squeeze.weight tensor(0.1207, device='cuda:0') tensor(0.2179, device='cuda:0')
model.features.7.squeeze.bias tensor(0.1484, device='cuda:0') tensor(0.0381, device='cuda:0')
model.features.7.expand1x1.weight tensor(0.1235, device='cuda:0') tensor(0.2279, device='cuda:0')
model.features.7.expand1x1.bias tensor(0.0450, device='cuda:0') tensor(0.0100, device='cuda:0')
model.features.7.expand3x3.weight tensor(0.0609, device='cuda:0') tensor(0.1628, device='cuda:0')
model.features.7.expand3x3.bias tensor(0.0132, device='cuda:0') tensor(0.0079, device='cuda:0')
model.features.9.squeeze.weight tensor(0.1093, device='cuda:0') tensor(0.2459, device='cuda:0')
model.features.9.squeeze.bias tensor(0.0646, device='cuda:0') tensor(0.0135, device='cuda:0')
model.features.9.expand1x1.weight tensor(0.0840, device='cuda:0') tensor(0.1860, device='cuda:0')
model.features.9.expand1x1.bias tensor(0.0177, device='cuda:0') tensor(0.0033, device='cuda:0')
model.features.9.expand3x3.weight tensor(0.0476, device='cuda:0') tensor(0.1393, device='cuda:0')
model.features.9.expand3x3.bias tensor(0.0058, device='cuda:0') tensor(0.0030, device='cuda:0')
model.features.10.squeeze.weight tensor(0.0872, device='cuda:0') tensor(0.1676, device='cuda:0')
model.features.10.squeeze.bias tensor(0.0484, device='cuda:0') tensor(0.0088, device='cuda:0')
model.features.10.expand1x1.weight tensor(0.0859, device='cuda:0') tensor(0.2145, device='cuda:0')
model.features.10.expand1x1.bias tensor(0.0160, device='cuda:0') tensor(0.0025, device='cuda:0')
model.features.10.expand3x3.weight tensor(0.0456, device='cuda:0') tensor(0.1429, device='cuda:0')
model.features.10.expand3x3.bias tensor(0.0070, device='cuda:0') tensor(0.0021, device='cuda:0')
model.features.11.squeeze.weight tensor(0.0786, device='cuda:0') tensor(0.2003, device='cuda:0')
model.features.11.squeeze.bias tensor(0.0422, device='cuda:0') tensor(0.0069, device='cuda:0')
model.features.11.expand1x1.weight tensor(0.0690, device='cuda:0') tensor(0.1400, device='cuda:0')
model.features.11.expand1x1.bias tensor(0.0138, device='cuda:0') tensor(0.0022, device='cuda:0')
model.features.11.expand3x3.weight tensor(0.0366, device='cuda:0') tensor(0.1517, device='cuda:0')
model.features.11.expand3x3.bias tensor(0.0109, device='cuda:0') tensor(0.0023, device='cuda:0')
model.features.12.squeeze.weight tensor(0.0729, device='cuda:0') tensor(0.1736, device='cuda:0')
model.features.12.squeeze.bias tensor(0.0814, device='cuda:0') tensor(0.0084, device='cuda:0')
model.features.12.expand1x1.weight tensor(0.0977, device='cuda:0') tensor(0.1385, device='cuda:0')
model.features.12.expand1x1.bias tensor(0.0102, device='cuda:0') tensor(0.0032, device='cuda:0')
model.features.12.expand3x3.weight tensor(0.0365, device='cuda:0') tensor(0.1312, device='cuda:0')
model.features.12.expand3x3.bias tensor(0.0038, device='cuda:0') tensor(0.0026, device='cuda:0')
model.classifier.1.weight tensor(0.0285, device='cuda:0') tensor(0.0865, device='cuda:0')
model.classifier.1.bias tensor(0.0362, device='cuda:0') tensor(0.0192, device='cuda:0') ipdb> opt.lr # 查看学习率
0.001 ipdb> opt.lr = 0.002 # 更改学习率 ipdb> for p in optimizer.param_groups: \
p['lr'] = opt.lr ipdb> model.save() # 保存模型
'checkpoints/squeezenet_20191004212249.pth' ipdb> c # 继续运行,直到第88行暂停
222it [16:38, 35.62s/it]> e:/Users/mac/Desktop/jupyter/mdFile/deeplearning/main.py(88)train()
87 optimizer.zero_grad()
1--> 88 score = model(input)
89 loss = criterion(score, target) ipdb> s # 进入model(input)内部,即model.__call__(input)
--Call--
> c:\programdata\anaconda3\lib\site-packages\torch\nn\modules\module.py(537)__call__()
536
--> 537 def __call__(self, *input, **kwargs):
538 for hook in self._forward_pre_hooks.values(): ipdb> n # 下一步
> c:\programdata\anaconda3\lib\site-packages\torch\nn\modules\module.py(538)__call__()
537 def __call__(self, *input, **kwargs):
--> 538 for hook in self._forward_pre_hooks.values():
539 result = hook(self, input) ipdb> n # 下一步
> c:\programdata\anaconda3\lib\site-packages\torch\nn\modules\module.py(544)__call__()
543 input = result
--> 544 if torch._C._get_tracing_state():
545 result = self._slow_forward(*input, **kwargs) ipdb> n # 下一步
> c:\programdata\anaconda3\lib\site-packages\torch\nn\modules\module.py(547)__call__()
546 else:
--> 547 result = self.forward(*input, **kwargs)
548 for hook in self._forward_hooks.values(): ipdb> s # 进入forward函数内容
--Call--
> c:\programdata\anaconda3\lib\site-packages\torch\nn\modules\loss.py(914)forward()
913
--> 914 def forward(self, input, target):
915 return F.cross_entropy(input, target, weight=self.weight, ipdb> input # 查看input变量值
tensor([[4.5005, 2.0725],
[3.5933, 7.8643],
[2.9086, 3.4209],
[2.7740, 4.4332],
[6.0164, 2.3033],
[5.2261, 3.2189],
[2.6529, 2.0749],
[6.3259, 2.2383],
[3.0629, 3.4832],
[2.7008, 8.2818],
[5.5684, 2.1567],
[3.0689, 6.1022],
[3.4848, 5.3831],
[1.7920, 5.7709],
[6.5032, 2.8080],
[2.3071, 5.2417],
[3.7474, 5.0263],
[4.3682, 3.6707],
[2.2196, 6.9298],
[5.2201, 2.3034],
[6.4315, 1.4970],
[3.4684, 4.0371],
[3.9620, 1.7629],
[1.7069, 7.8898],
[3.0462, 1.6505],
[2.4081, 6.4456],
[2.1932, 7.4614],
[2.3405, 2.7603],
[1.9478, 8.4156],
[2.7935, 7.8331],
[1.8898, 3.8836],
[3.3008, 1.6832]], device='cuda:0', grad_fn=<AsStridedBackward>) ipdb> input.data.mean() # 查看input的均值和标准差
tensor(3.9630, device='cuda:0')
ipdb> input.data.std()
tensor(1.9513, device='cuda:0') ipdb> u # 跳回上一层
> c:\programdata\anaconda3\lib\site-packages\torch\nn\modules\module.py(547)__call__()
546 else:
--> 547 result = self.forward(*input, **kwargs)
548 for hook in self._forward_hooks.values(): ipdb> u # 跳回上一层
> e:/Users/mac/Desktop/jupyter/mdFile/deeplearning/main.py(88)train()
87 optimizer.zero_grad()
1--> 88 score = model(input)
89 loss = criterion(score, target) ipdb> clear # 清除所有断点
Clear all breaks? y
Deleted breakpoint 1 at e:/Users/mac/Desktop/jupyter/mdFile/deeplearning/main.py:88 ipdb> c # 继续运行,记得先删除"debug/debug.txt",否则很快又会进入调试模式
59it [06:21, 5.75it/s]loss: 0.24856307208538073
76it [06:24, 5.91it/s]

当我们想要进入 debug 模式,修改程序中某些参数值或者想分析程序时,就可以通过创建 debug 标识文件,此时程序会进入调试模式,调试完成之后删除这个文件并在 ipdb 调试接口输入 c 继续运行程序。如果想退出程序,也可以使用这种方式,先创建 debug 标识文件,然后输入 quit 在退出 debug 的同时退出程序。这种退出程序的方式,与使用 Ctrl + C 的方式相比更安全,因为这能保证数据加载的多进程程序也能正确地退出,并释放内存、显存等资源。

PyTorch 和 ipdb 集合能完成很多其他框架所不能完成或很难完成的功能。根据笔者日常使用的总结,主要有以下几个部分:

  1. 通过 debug 暂停程序。当程序进入 debug 模式后,将不再执行 CPU 和 GPU 运算,但是内存和显存及相应的堆栈空间不会释放。
  2. 通过 debug 分析程序,查看每个层的输出,查看网络的参数情况。通过 u(p) 、 d(own) 、 s(tep) 等命令,能够进入指定的代码,通过 n(ext) 可以单步执行,从而看到每一层的运算结果,便于分析网络的数值分布等信息。
  3. 作为动态图框架, PyTorch 拥有 Python 动态语言解释执行的优点,我们能够在运行程序时,用过 ipdb 修改某些变量的值或属性,这些修改能够立即生效。例如可以在训练开始不久根据损失函数调整学习率,不必重启程序。
  4. 如果在 IPython 中通过 %run 魔法方法运行程序,那么在程序异常退出时,可以使用 %debug 命令,直接进入 debug 模式,通过 u(p) 和 d(own) 跳到报错的地方,查看对应的变量,找出原因后修改相应的代码即可。有时我们的模式训练了好几个小时,却在将要保存模式之前,因为一个小小的拼写错误异常退出。此时,如果修改错误再重新运行程序又要花费好几个小时,太浪费时间。因此最好的方法就是看利用 %debug 进入调试模式,在调试模式中直接运行 model . save() 保存模型。在 IPython 中, %pdb 魔术方法能够使得程序出现问题后,不用手动输入 %debug 而自动进入 debug 模式,建议使用。

四、 通过PyTorch实现项目中容易遇到的问题

PyTorch 调用 CuDNN 报错时,报错信息诸如 CUDNN_STATUS_BAD_PARAM,从这些报错内容很难得到有用的帮助信息,最后先利用 PCU 运行代码,此时一般会得到相对友好的报错信息,例如在 ipdb 中执行 model.cpu() (input.cpu()), PyTorch 底层的 TH 库会给出相对比较详细的信息。

常见的错误主要有以下几种:

  • 类型不匹配问题。例如 CrossEntropyLoss 的输入 target 应该是一个 LongTensor ,而很多人输入 FloatTensor 。
  • 部分数据忘记从 CPU 转移到 GPU 。例如,当 model 存放于 GPU 时,输入 input 也需要转移到 GPU 才能输入到 model 中。还有可能就是把多个 model 存放于一个 list 对象,而在执行 model.cuda() 时,这个 list 中的对象是不会被转移到 CUDA 上的,正确的用法是用 ModuleList 代替。
  • Tensor 形状不匹配。此类问题一般是输入数据形状不对,或是网络结构设计有问题,一般通过 u(p) 跳到指定代码,查看输入和模型参数的形状即可得知。

此外,可能还会经常遇到程序正常运行、没有报错,但是模型无法收敛的问题。例如对于二分类问题,交叉熵损失一直徘徊在 0.69 附近(ln2),或者是数值出现溢出等问题,此时可以进入 debug 模式,用单步执行查看,每一层输出的均值和方差,观察从哪一层的输出开始出现数值异常。还要查看每个参数梯度的均值和方差,查看是否出现梯度消失或者梯度爆炸等问题。一般来说,通过再激活函数之前增加 BatchNorm 层、合理的参数初始化、使用 Adam 优化器、学习率设为0.001,基本就能确保模型在一定程度收敛。

五、第八章总结

本章带同学们从头实现了一个 Kaggle 上的经典竞赛,重点讲解了如何合理地组合安排程序,同时介绍了一些在PyTorch中调试的技巧,下章将正式的进入编程实战之旅,其中一些细节不会再讲的如此详细,做好心理准备。

0803-PyTorch的Debug指南的更多相关文章

  1. pytorch Debug —交互式调试工具Pdb (ipdb是增强版的pdb)-1-在pytorch中使用

    参考深度学习框架pytorch:入门和实践一书第六章 以深度学习框架PyTorch一书的学习-第六章-实战指南为前提 在pytorch中Debug pytorch作为一个动态图框架,与ipdb结合能为 ...

  2. 万字综述,核心开发者全面解读PyTorch内部机制

    斯坦福大学博士生与 Facebook 人工智能研究所研究工程师 Edward Z. Yang 是 PyTorch 开源项目的核心开发者之一.他在 5 月 14 日的 PyTorch 纽约聚会上做了一个 ...

  3. jmeter sampler maven项目排错记

    eclipse 创建的maven项目,引入jar包之后出现红色叹号,一直找不到原因,连main方法都无法运行,提示找不到类: 错误: 找不到或无法加载主类 soapsampler.SoapSample ...

  4. 深度学习框架PyTorch一书的学习-第六章-实战指南

    参考:https://github.com/chenyuntc/pytorch-book/tree/v1.0/chapter6-实战指南 希望大家直接到上面的网址去查看代码,下面是本人的笔记 将上面地 ...

  5. (转载)PyTorch代码规范最佳实践和样式指南

    A PyTorch Tools, best practices & Styleguide 中文版:PyTorch代码规范最佳实践和样式指南 This is not an official st ...

  6. pytorch Debug —交互式调试工具Pdb (ipdb是增强版的pdb)-1-使用说明

    初学时大多使用print或log调试程序,这在小规模的程序下很方便 但是更好的方法是一边运行一边检查里面的变量和方法 1.Pdb Pdb是一个交互式的调试工具,集成于Python标准库中 Pdb能让你 ...

  7. Linux Kernel - Debug Guide (Linux内核调试指南 )

    http://blog.csdn.net/blizmax6/article/details/6747601 linux内核调试指南 一些前言 作者前言 知识从哪里来 为什么撰写本文档 为什么需要汇编级 ...

  8. pytorch 入门指南

    两类深度学习框架的优缺点 动态图(PyTorch) 计算图的进行与代码的运行时同时进行的. 静态图(Tensorflow <2.0) 自建命名体系 自建时序控制 难以介入 使用深度学习框架的优点 ...

  9. pytorch 0.4.0迁移指南

    总说 由于pytorch 0.4版本更新实在太大了, 以前版本的代码必须有一定程度的更新. 主要的更新在于 Variable和Tensor的合并., 当然还有Windows的支持, 其他一些就是支持s ...

随机推荐

  1. Node.js 模块化你所需要知道的事

    一.前言 我们知道,Node.js是基于CommonJS规范进行模块化管理的,模块化是面对复杂的业务场景不可或缺的工具,或许你经常使用它,但却从没有系统的了解过,所以今天我们来聊一聊Node.js模块 ...

  2. 一些比较好的国外IT网站

    1.在线编程练习: LintCode --在线刷题网站,阶梯式训练,可帮助你更快速深入地了解各类面试题型,提供专业导师写的最优代码作为参考 (Lintcode 标准答案查询--lintcode 的参考 ...

  3. python之Click的简单应用

    一.介绍 Click是一个Python包,用于以可组合的方式创建漂亮的命令行界面,只需要很少的代码.这是"命令行界面创建工具包".它具有高度可配置性,但具有开箱即用的合理默认值. ...

  4. python-给一个参数n,例如3:先输出1,2,3,4,5,6,7,8,9,每三个数后换行,后输出1,4,7,2,5,8,3,6,9

    """ 2 定义一个函数,fn(n)其中n表示输入n行n列的矩阵,需要满足的要求是在n为 3时先输出 3 1 2 3 4 4 5 6 5 7 8 9 6 后输出 7 1 ...

  5. PTA 求链式表的表长

    6-1 求链式表的表长 (10 分)   本题要求实现一个函数,求链式表的表长. 函数接口定义: int Length( List L ); 其中List结构定义如下: typedef struct ...

  6. ClickHouse性能优化?试试物化视图

    一.前言 ClickHouse是一个用于联机分析(OLAP)的列式数据库管理系统(DBMS):目前我们使用CH作为实时数仓用于统计分析,在做性能优化的时候使用了 物化视图 这一特性作为优化手段,本文主 ...

  7. day-04-列表

    列表的初识 容器型数据类型,可以承载大量的数据,有序的数据 why:int bool str:'123 True 太白' str: 存储少量的数据. str:切片还是对其进行任何操作,获取的内容全都是 ...

  8. java面试-CAS底层原理

    一.CAS是什么? 比较并交换,它是一条CPU并发原语. CAS是一种无锁算法,CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B.当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什 ...

  9. Kubernetes网络概念初探

    ------------恢复内容开始------------ Kubernetes网络是Kubernetes中一个核心概念.简而言之,Kubernetes网络模型可以确保集群上所有Kubernetes ...

  10. ASP.NET Core中使用令牌桶限流

    在限流时一般会限制每秒或每分钟的请求数,简单点一般会采用计数器算法,这种算法实现相对简单,也很高效,但是无法应对瞬时的突发流量. 比如限流每秒100次请求,绝大多数的时间里都不会超过这个数,但是偶尔某 ...