百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

升级到PyTorch 2.0的技巧总结(如何更新pytorch版本)

off999 2025-07-21 17:07 42 浏览 0 评论

来源:DeepHub IMBA本文约6400字,建议阅读12分钟在本文将演示 PyTorch 2.0新功能的使用,以及介绍在使用它时可能遇到的一些问题。


PyTorch 2.0 发布也有一段时间了,大家是不是已经开始用了呢?PyTorch 2.0 通过引入 torch.compile,可以显着提高训练和推理速度。与 eagerly 模式相反,编译 API 将模型转换为中间计算图(FX graph),然后以某种方式将其编译为低级计算内核,这样可以提高运行速度。



对于PyTorch 2.0 而言,你看到的可能是:


“只是用 torch.compile 调用包装它们就可以提高运行速度”


但是其实有许多因素会干扰计算图编译和/或达到所需的性能改进。所以需要调整模型和达到最佳性能可能需要重新设计项目或修改一些编码习惯。


在本文中,我们将演示这个新功能的使用,以及介绍在使用它时可能遇到的一些问题。我们将分享在调整 torch.compile API 时遇到的问题的几个例子。这些例子并不全面,再实际运用是很可能会遇到此处未提及的问题,并且还要 torch.compile 仍在积极开发中,还有改进的空间。


Torch 编译背后有许多创新技术,包括 TorchDynamo、FX Graph、TorchInductor、Triton 等。我们不会在这篇文章中深入探讨不同的组件,如果你对这些感兴趣,可以查看PyTorch 文档,里面介绍的非常详细。


TensorFlow 与 PyTorch 的两个不重要的对比


1、在过去,PyTorch 和 TensorFlow 之间有着明显的区别。PyTorch 使用了 eager execution 模式,TensorFlow 使用了 graph 模式,大家都在各自发展。但后来 TensorFlow 2 引入了eager execution作为默认执行模式,TensorFlow 变得有点像 PyTorch。现在 PyTorch 也引入了自己的graph 模式解决方案,变得有点像 TensorFlow。TensorFlow 与 PyTorch 的竞争仍在继续,但两者之间的差异正在慢慢消失。


2、人工智能开发是一个时髦的行业。但是流行的 AI 模型、模型架构、学习算法、训练框架等随时间变化发展的。就论文而言,几年前我们处理的大部分模型都是用 TensorFlow 编写的。但是人们经常抱怨高级 model.fit API 限制了他们的开发灵活性,并且graph 模式使他们无法调试。然后就有好多人转向了PyTorch,他们说,“PyTorch可以以任何想要的方式构建模型并轻松调试”。但是更灵活的的自定义操作会导致开发的复杂性,PyTorch Lightening等高级的API的出现就是复制了model.fit API的特性,然后同样的人又说还有人说“我们必须适应 PyTorch Lightening,我们必须用 torch.compile 加速我们的训练”。既要灵活,又要简单是不可能同时实现的。


正文开始


下面开始介绍关于如何使用PyTorch 2编译API的技巧集合,以及一些你可能面临的潜在问题。使模型适应PyTorch的graph 模式可能需要付出不小的努力。希望这篇文章能帮助你更好地评估这一努力,并决定采取这一步的最佳方式。


安装PyTorch2


从PyTorch安装文档来看,安装PyTorch 2似乎与安装任何其他PyTorch版本没有什么不同,但是在实践中,可能会遇到一些问题。首先,PyTorch 2.0(截至本文时)需要Python 3.8或更高版本。然后就是PyTorch 2包含以前版本中不存在的包依赖项(最明显的是PyTorch-triton,这是什么我也不知道,哈),需要注意可能会会引入新的冲突。


所以如果你对Docker熟悉,建议直接使用容器,这样会简单很多。


PyTorch2兼容性


PyTorch2的优点之一是它完全向后兼容,所以我们即使不使用torch.compile,仍然可以使用PyTorch 2.0并从其他新功能和增强中受益。最多就是享受不到速度的提升,但是不会有兼容性的问题。但是如果你想进一步提升速度,那么请往下看。


简单例子


让我们从一个简单的图像分类模型的例子开始。在下面的代码块中,我们使用timm Python包(版本0.6.12)构建一个基本的Vision Transformer (ViT)模型,并在一个假数据集上训练它500步(不是轮次)。这里定义了use_compile标志来控制是否执行模型编译(torch.compile),use_amp来控制是使用自动混合精度(AMP)还是全精度(FP)运行。


 import time, os
 import torch
 from torch.utils.data import Dataset
 from timm.models.vision_transformer import VisionTransformer


 use_amp = True # toggle to enable/disable amp
 use_compile = True # toggle to use eager/graph execution mode


 # use a fake dataset (random data)
 class FakeDataset(Dataset):
   def __len__(self):
     return 1000000


   def __getitem__(self, index):
     rand_image = torch.randn([3, 224, 224], dtype=torch.float32)
     label = torch.tensor(data=[index % 1000], dtype=torch.int64)
     return rand_image, label


 def train():
   device = torch.cuda.current_device()
   dataset = FakeDataset()
   batch_size = 64


   # define an image classification model with a ViT backbone
   model = VisionTransformer()


   if use_compile:
     model = torch.compile(model)


   model.to(device)


   optimizer = torch.optim.Adam(model.parameters())
   data_loader = torch.utils.data.DataLoader(dataset,
                           batch_size=batch_size, num_workers=4)
   loss_function = torch.nn.CrossEntropyLoss()


   t0 = time.perf_counter()
   summ = 0
   count = 0


   for idx, (inputs, target) in enumerate(data_loader, start=1):
     inputs = inputs.to(device)
     targets = torch.squeeze(target.to(device), -1)


     optimizer.zero_grad()


     with torch.cuda.amp.autocast(
       enabled=use_amp,
       dtype=torch.bfloat16
    ):
       outputs = model(inputs)
       loss = loss_function(outputs, targets)


     loss.backward()
     optimizer.step()


     batch_time = time.perf_counter() - t0


     if idx > 10:  # skip first few steps
       summ += batch_time
       count += 1
     t0 = time.perf_counter()
     if idx > 500:
       break


   print(f'average step time: {summ/count}')


 if __name__ == '__main__':
   train()


在下表记录了比较性能结果。这些结果根据环境不同而有很大的变化,所以及供参考



可以看到,使用AMP(28.6%)比使用FP(4.5%)时,模型编译带来的性能提升要明显得多。这是一个众所周知的差异。如果你还没有使用AMP进行训练,那么其实对于训练速度的提升是从FP过渡到AMP,所以先推荐你使用AMP。另外就是性能提升伴随着GPU内存利用率的非常轻微的增加。


当扩展到多个gpu时,由于在编译图上实现分布式训练的方式,比较性能可能会发生变化。具体细节看官方文档。


https://pytorch.org/get-started/pytorch-2.0/#distributed


高级选项


compile API包含许多用于控制graph创建的选项,能够针对特定模型对编译进行微调,并可能进一步提高性能。下面的代码块是官方的函数介绍:


 def compile(model: Optional[Callable] = None, *,
             fullgraph: builtins.bool = False,
             dynamic: builtins.bool = False,
             backend: Union[str, Callable] = "inductor",
             mode: Union[str, None] = None,
             options: Optional[Dict[str, Union[str, builtins.int, builtins.bool]]] = None,
             disable: builtins.bool = False) -> Callable:
     """
    Optimizes given model/function using TorchDynamo and specified backend.


    Args:
        model (Callable): Module/function to optimize
        fullgraph (bool): Whether it is ok to break model into several subgraphs
        dynamic (bool): Use dynamic shape tracing
        backend (str or Callable): backend to be used
        mode (str): Can be either "default", "reduce-overhead" or "max-autotune"
        options (dict): A dictionary of options to pass to the backend.
        disable (bool): Turn torch.compile() into a no-op for testing
    """


mode 编译模式:允许您在最小化编译所需的开销(“reduce-overhead”)和最大化潜在的性能提升(“max-autotune”)之间进行选择。


下表比较了不同编译模式下编译上述ViT模型的结果。



可以看到编译模式的行为与命名的非常相似,“reduce-overhead”以额外的内存利用为代价减少了编译时间,“max-autotune”以高编译时间开销为代价获得了最佳性能。


backend 编译器后端:API使用哪个后端将中间表示(IR)计算图(FX graph)转换为低级内核操作。这个选项对于调试graph编译问题和更好地理解torch.compile的内部非常有用。在大多数情况下,默认的Inductor后端似乎能够提供最佳的训练性能结果。有很多后端列表,我们可以使用下面命令查看:


 from torch import _dynamo
 print(_dynamo.list_backends())


我们测试使用nvprims-nvfuser后端,可以获得比eager模式13%的性能提升(与默认后端28.6%的性能提升相比)。具体区别还是要看Pytorch文档,我们这里就不细说了,因为文档都有。


fullgraph 强制单个图:这个参数是非常有用,可以确保没有任何不希望的图截断。


dynamic 动态形状:目前 2.0对具有动态形状的张量的编译支持在某种程度上是有限的。编译具有动态形状的模型的一个常见解决方案是重新编译,但会大大增加开销并大大降低训练速度。如果您的模型确实包含动态形状,将动态标志设置为True将带来更好的性能,特别是减少重新编译的次数。


都有什么是动态形状呢,最简单的就是时间序列或文本长度不同,如果不进行对齐操作的话序列长度不同就是动态的形状。


性能分析


PyTorch Profiler是用来分析PyTorch模型性能的关键工具之一,可以评估和分析图编译优化训练步骤的方式。在下面的代码块中,我们用profiler生成TensorBoard的结果,来查看训练的性能:


   out_path = os.path.join(os.environ.get('SM_MODEL_DIR','/tmp'),'profile')
   from torch.profiler import profile, ProfilerActivity
   with profile(
           activities=[ProfilerActivity.CPU, ProfilerActivity.CUDA],
           schedule=torch.profiler.schedule(
             wait=20,
             warmup=5,
             active=10,
             repeat=1),
           on_trace_ready=torch.profiler.tensorboard_trace_handler(
                                                 dir_name=out_path)


  ) as p:
     for idx, (inputs, target) in enumerate(data_loader, start=1):
       inputs = inputs.to(device)
       targets = torch.squeeze(target.to(device), -1)
       optimizer.zero_grad()


       with torch.cuda.amp.autocast(
         enabled=use_amp,
         dtype=torch.bfloat16
      ):
         outputs = model(inputs)
         loss = loss_function(outputs, targets)
       loss.backward()
       optimizer.step()
       p.step()


下图是从PyTorch Profiler生成的TensorBoard 中截取的。它提供了在上面编译模型试验的训练步骤中在GPU上运行的内核的详细信息。



我们能够看到torch.compile 增加了GPU张量核心的利用率(从51%到60%),并且它引入了使用Triton开发的GPU内核。


调试模型编译问题


torch.compile 目前处于测试阶段,如果你遇到问题,并且幸运的话,会得到一个信息错误,我们可以直接搜索解决,或者问问chatgpt。但是如果你不那么幸运,就需要自己寻找问题的根源。


这里解决编译问题的主要资源是 TorchDynamo 故障排除文档,其中包括调试工具列表并提供诊断错误的分步指南。但是目前这些工具和技术似乎更多地针对 PyTorch 开发人员而不是 PyTorch 用户的。它们也许可以帮助解决导致编译问题的根本问题,但是非常大的可能是它们实际上跟本没有任何帮助,那怎么办呢?


这里我们演示一个自行解决问题的过程,按照这样的思路,可以解决一些问题。


下面是一个简单的分布式模型,其中包括对
torch.distributed.all_reduce 的调用。模型在 eager 模式下按预期运行,但在graph编译期间失败并出现“attribute error”(
torch.classes.c10d.ProcessGroup does not have a field with name ‘shape’)。我们需要将日志级别提高到 INFO,然后发现发现错误在计算的“第 3 步”中,即 TorchInductor。然后通过验证“eager”和“aot_eager”后端的编译是否成功, 最后创建一个最小的代码示例,使用 PyTorch Minifier 重现失败。


 import os, logging
 import torch
 from torch import _dynamo


 # enable debug prints
 torch._dynamo.config.log_level = logging.INFO
 torch._dynamo.config.verbose=True


 # uncomment to run minifier
 # torch._dynamo.config.repro_after="aot"


 def build_model():
   import torch.nn as nn
   import torch.nn.functional as F


   class DumbNet(nn.Module):
     def __init__(self):
       super().__init__()
       self.conv1 = nn.Conv2d(3, 6, 5)
       self.pool = nn.MaxPool2d(2, 2)
       self.fc1 = nn.Linear(1176, 10)


     def forward(self, x):
       x = self.pool(F.relu(self.conv1(x)))
       x = torch.flatten(x, 1)
       x = self.fc1(x)
       with torch.no_grad():
         sum_vals = torch.sum(x,0)
         # this is the problematic line of code
         torch.distributed.all_reduce(sum_vals)
       # add noise
       x = x + 0.1*sum_vals
       return x


   net = DumbNet()
   return net


 def train():
   os.environ['MASTER_ADDR'] = os.environ.get('MASTER_ADDR',
                                              'localhost')
   os.environ['MASTER_PORT'] = os.environ.get('MASTER_PORT',
                                              str(2222))
   torch.distributed.init_process_group('nccl', rank=0,
                                          world_size=1)
   torch.cuda.set_device(0)
   device = torch.cuda.current_device()


   model = build_model()


   model = torch.compile(model)


   # replace with this to verfiy that error is not in TorchDynamo
   # model = torch.compile(model, 'eager')
   # replace with this to verfiy that error is not in AOTAutograd
   # model = torch.compile(model, 'aot_eager')


   model.to(device)


   rand_image = torch.randn([4, 3, 32, 32], dtype=torch.float32).to(device)


   model(rand_image)


 if __name__ == '__main__':
   train()


在这个的示例中,运行生成的 minifier_launcher.py 脚本会导致不同的属性错误(比如Repro’ object has no attribute ‘_tensor_constant0’),这个对于我们的演示没有太大帮助,我们暂时忽略他,这也说明了,torch.compile 还不完善,还需要更大的改进空间,或者说如果解决不要问题,那就别用了,至少“慢”要比不能用好,对吧(而且速度提升也有限)


常见的图截断问题


Pytorch eager 模式优势之一是能够将纯 Pythonic 代码与 PyTorch 操作交织在一起。但是这种自由在使用 torch.compile 时受到很大限制。因为 Pythonic 操作导致 TorchDynamo 将计算图拆分为多个组件,从而阻碍了性能提升的潜力。而我们代码优化的目标是尽可能减少此类图截断。最简单的办法是用 fullgraph 标志编译模型。这杨可以提示删除导致图截断的任何代码,而且还会告诉我们如何最好地适应PyTorch2的开发习惯。但是要运行分布式代码,则必须将他设为False,因为当前实现 GPU 之间通信的方式需要图拆分。我们也可以使用 torch._dynamo.explain 序来分析图截断。


以下代码块演示了一个简单模型,在其前向传递中有四个潜在的图截断,但是这种在使用方式在典型的 PyTorch 模型中并不少见。


 import torch
 from torch import _dynamo
 import numpy as np


 def build_model():
   import torch.nn as nn
   import torch.nn.functional as F


   class DumbNet(nn.Module):
     def __init__(self):
       super().__init__()
       self.conv1 = nn.Conv2d(3, 6, 5)
       self.pool = nn.MaxPool2d(2, 2)
       self.fc1 = nn.Linear(1176, 10)
       self.fc2 = nn.Linear(10, 10)
       self.fc3 = nn.Linear(10, 10)
       self.fc4 = nn.Linear(10, 10)
       self.d = {}




     def forward(self, x):
       x = self.pool(F.relu(self.conv1(x)))
       x = torch.flatten(x, 1)
       assert torch.all(x >= 0) # graph break
       x = self.fc1(x)
       self.d['fc1-out'] = x.sum().item() # graph break
       x = self.fc2(x)
       for k in np.arange(1): # graph break
         x = self.fc3(x)
       print(x)  # graph break
       x = self.fc4(x)
       return x


   net = DumbNet()
   return net


 def train():
   model = build_model()
   rand_image = torch.randn([4, 3, 32, 32], dtype=torch.float32)
   explanation = torch._dynamo.explain(model, rand_image)
   print(explanation)


 if __name__ == '__main__':
   train()


图截断不会导致编译失败(除非设置了fullgraph标志)。所以很有可能模型正在编译和运行,但实际上包含多个图截断,这会减慢它的速度。


训练问题故障排除


在目前来说,使用Pytorch2成功编译的模型就可以认为是一项值得庆祝的成就,但这并不能保证训练一定会成功。


在 GPU 上运行的低级内核在 eager 模式和graph模式之间会有所不同。某些高级操作可能会表现出不同的行为。你可能会发现在eager 模式下运行的操作在graph 模式下会失败(例如torch.argmin)。或者会发现计算中的数值差异会影响训练。


graph模式下的调试比 eager 模式下的调试困难得多。在 eager 模式下,每一行代码都是独立执行的,我们可以在代码中的任意点放置断点获得前张量值。而在graph 模式下,代码定义的模型在处理之前会经历多次转换,设置的断点可能不会被触发。


所以可以先使用eager 模式,模型跑通以后,再将torch.compile 分别应用于每个部分,或者通过插入打印和/或 Tensor.numpy 调用来生成图截断,这样我们可能会会成功触发代码中的断点。也就是说如果用torch.compile的话对于开发来说,要耗费更长的时间,所以训练和开发速度的取舍就要看你自己的选择了。


但是别忘了我们上面说的你的模型在添加了torch.compile后也不一定能正确运行,这又是一个无形的成本。


在图中包含损失函数


通过使用torch.compile调用包装PyTorch模型(或函数)来启用graph模式。但是损失函数不是编译调用的一部分,也不是生成图的一部分。所以损失函数是训练步骤中相对较小的一部分,如果使用eager 模式运行它不会产生太多开销。但是如果有一个计算量他别大的损失函数,也是可以通过将其包含在编译的计算图中来进一步提高性能的。


在下面的代码中,我们定义了一个损失函数,用于执行从大型ViT模型(具有24个ViT块)到较小的ViT模型(具有12个ViT块)的模型蒸馏。


 import torch
 from timm.models.vision_transformer import VisionTransformer


 class ExpensiveLoss(torch.nn.Module):
   def __init__(self):
     super(ExpensiveLoss, self).__init__()
     self.expert_model = VisionTransformer(depth=24)
     if torch.cuda.is_available():
       self.expert_model.to(torch.cuda.current_device())
     self.mse_loss = torch.nn.MSELoss()


   def forward(self, input, outputs):
     expert_output = self.expert_model(input)
     return self.mse_loss(outputs, expert_output)


这是一个比CrossEntropyLoss计算量大得多的损失函数,这里又2种方法让他执行的更快,


1、loss函数封装在torch.compile调用中,如下所示:


 loss_function = ExpensiveLoss()
 compiled_loss = torch.compile(loss_function)


这个方法的缺点是损失函数的编译图与模型的编译图不相交,但是它的优点非常明显,就是简单。


2、创建一个包含模型和损失的包装器模型来将模型和损失一起编译,并将结果损失作为输出返回。


 import time, os
 import torch
 from torch.utils.data import Dataset
 from torch import nn
 from timm.models.vision_transformer import VisionTransformer


 # use a fake dataset (random data)
 class FakeDataset(Dataset):
   def __len__(self):
     return 1000000


   def __getitem__(self, index):
     rand_image = torch.randn([3, 224, 224], dtype=torch.float32)
     label = torch.tensor(data=[index % 1000], dtype=torch.int64)
     return rand_image, label


 # create a wrapper model for the ViT model and loss
 class SuperModel(torch.nn.Module):
   def __init__(self):
     super(SuperModel, self).__init__()
     self.model = VisionTransformer()
     self.expert_model = VisionTransformer(depth=24 if torch.cuda.is_available() else 2)
     self.mse_loss = torch.nn.MSELoss()


   def forward(self, inputs):
     outputs = self.model(inputs)
     with torch.no_grad():
       expert_output = self.expert_model(inputs)
     return self.mse_loss(outputs, expert_output)


 # a loss that simply passes through the model output
 class PassthroughLoss(nn.Module):
   def __call__(self, model_output):
     return model_output


 def train():
   device = torch.cuda.current_device()
   dataset = FakeDataset()
   batch_size = 64


   # create and compile the model
   model = SuperModel()
   model = torch.compile(model)


   model.to(device)


   optimizer = torch.optim.Adam(model.parameters())
   data_loader = torch.utils.data.DataLoader(dataset,
                           batch_size=batch_size, num_workers=4)


   loss_function = PassthroughLoss()


   t0 = time.perf_counter()
   summ = 0
   count = 0


   for idx, (inputs, target) in enumerate(data_loader, start=1):
     inputs = inputs.to(device)
     targets = torch.squeeze(target.to(device), -1)


     optimizer.zero_grad()


     with torch.cuda.amp.autocast(
       enabled=True,
       dtype=torch.bfloat16
    ):
       outputs = model(inputs)
       loss = loss_function(outputs)


     loss.backward()
     optimizer.step()


     batch_time = time.perf_counter() - t0


     if idx > 10:  # skip first few steps
       summ += batch_time
       count += 1
     t0 = time.perf_counter()
     if idx > 500:
       break


   print(f'average step time: {summ/count}')


 if __name__ == '__main__':
   train()


这种方法的缺点是,当在推理模式下运行模型时,需要从包装器模型中提取内部的实际模型。


这两种选项的性能提升幅度大致相同都是8%,也就是说,对loss进行编译也是优化的一个重要部分。


动态形状


官方也说了torch.compile对动态形状的模型的编译支持是有限的。compile API包含dynamic 参数,用于向编译器发出信号,但是这种方式对于性能提升帮助的程度是值得怀疑的。如果你正在尝试编译和优化动态图并面临问题,那么还是不要使用torch.compile,因为太麻烦了。


总结


PyTorch 2.0编译模式具有显著提高训练和推理速度的潜力,可以显著节省成本,但是模型实现这一潜力所需的工作量可能会有很大差异。许多公共模型只需要修改一行代码。而其他模型特别是那些包含非标准操作、动态形状和/或大量交错Python代码的模型,可能得不偿失甚至无法进行。但是现在开始修改模型是一个很好的选择,因为目前来看torch.compile对于PyTorch2来说是一个重要且持续的特性。

相关推荐

Linux 网络协议栈_linux网络协议栈

前言;更多学习资料(包含视频、技术学习路线图谱、文档等)后台私信《资料》免费领取技术点包含了C/C++,Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,Z...

揭秘 BPF map 前生今世_bpfdm

1.前言众所周知,map可用于内核BPF程序和用户应用程序之间实现双向的数据交换,为BPF技术中的重要基础数据结构。在BPF程序中可以通过声明structbpf_map_def...

教你简单 提取fmpeg 视频,音频,字幕 方法

ffmpeg提取视频,音频,字幕方法(HowtoExtractVideo,Audio,SubtitlefromOriginalVideo?)1.提取视频(ExtractVi...

Linux内核原理到代码详解《内核视频教程》

Linux内核原理-进程入门进程进程不仅仅是一段可执行程序的代码,通常进程还包括其他资源,比如打开的文件,挂起的信号,内核内部的数据结构,处理器状态,内存地址空间,或多个执行线程,存放全局变量的数据段...

Linux C Socket UDP编程详解及实例分享

1、UDP网络编程主要流程UDP协议的程序设计框架,客户端和服务器之间的差别在于服务器必须使用bind()函数来绑定侦听的本地UDP端口,而客户端则可以不进行绑定,直接发送到服务器地址的某个端口地址。...

libevent源码分析之bufferevent使用详解

libevent的bufferevent在event的基础上自己维护了一个buffer,这样的话,就不需要再自己管理一个buffer了。先看看structbufferevent这个结构体struct...

一次解决Linux内核内存泄漏实战全过程

什么是内存泄漏:程序向系统申请内存,使用完不需要之后,不释放内存还给系统回收,造成申请的内存被浪费.发现系统中内存使用量随着时间的流逝,消耗的越来越多,例如下图所示:接下来的排查思路是:1.监控系统中...

彻底搞清楚内存泄漏的原因,如何避免内存泄漏,如何定位内存泄漏

作为C/C++开发人员,内存泄漏是最容易遇到的问题之一,这是由C/C++语言的特性引起的。C/C++语言与其他语言不同,需要开发者去申请和释放内存,即需要开发者去管理内存,如果内存使用不当,就容易造成...

linux网络编程常见API详解_linux网络编程视频教程

Linux网络编程API函数初步剖析今天我们来分析一下前几篇博文中提到的网络编程中几个核心的API,探究一下当我们调用每个API时,内核中具体做了哪些准备和初始化工作。1、socket(family...

Linux下C++访问web—使用libcurl库调用http接口发送解析json数据

一、背景这两天由于一些原因研究了研究如何在客户端C++代码中调用web服务端接口,需要访问url,并传入json数据,拿到返回值,并解析。 现在的情形是远程服务端的接口参数和返回类型都是json的字符...

平衡感知调节:“系统如人” 视角下的架构设计与业务稳定之道

在今天这个到处都是数字化的时代,系统可不是一堆冷冰冰的代码。它就像一个活生生的“数字人”,没了它,业务根本转不起来。总说“技术要为业务服务”,但实际操作起来问题不少:系统怎么才能快速响应业务需求?...

谈谈分布式文件系统下的本地缓存_什么是分布式文件存储

在分布式文件系统中,为了提高系统的性能,常常会引入不同类型的缓存存储系统(算法优化所带来的的效果可能远远不如缓存带来的优化效果)。在软件中缓存存储系统一般可分为了两类:一、分布式缓存,例如:Memca...

进程间通信之信号量semaphore--linux内核剖析

什么是信号量信号量的使用主要是用来保护共享资源,使得资源在一个时刻只有一个进程(线程)所拥有。信号量的值为正的时候,说明它空闲。所测试的线程可以锁定而使用它。若为0,说明它被占用,测试的线程要进入睡眠...

Qt编写推流程序/支持webrtc265/从此不用再转码/打开新世界的大门

一、前言在推流领域,尤其是监控行业,现在主流设备基本上都是265格式的视频流,想要在网页上直接显示监控流,之前的方案是,要么转成hls,要么魔改支持265格式的flv,要么265转成264,如果要追求...

30 分钟搞定 SpringBoot 视频推拉流!实战避坑指南

30分钟搞定SpringBoot视频推拉流!实战避坑指南在音视频开发领域,SpringBoot凭借其快速开发特性,成为很多开发者实现视频推拉流功能的首选框架。但实际开发中,从环境搭建到流处理优...

取消回复欢迎 发表评论: