PyTorch 模型性能分析和优化 — 第 1 部分

news2024/11/25 13:21:07

一、说明

        这篇文章的重点将是GPU上的PyTorch培训。更具体地说,我们将专注于 PyTorch 的内置性能分析器 PyTorch Profiler,以及查看其结果的方法之一,即 PyTorch Profiler TensorBoard 插件

二、深度框架

        训练深度学习模型,尤其是大型模型,可能是一项昂贵的支出。我们可以使用的用于管理这些成本的主要方法之一是性能优化。性能优化是一个迭代过程,在这个过程中,我们不断寻找提高应用程序性能的机会,然后利用这些机会。在以前的帖子中(例如,这里我们强调了拥有进行这种分析的适当工具的重要性。选择的工具可能取决于许多因素,包括训练加速器的类型(例如,GPU、HPU 或其他)和训练框架。

性能优化流程(作者)

       

        这篇文章并不是要替代PyTorch Profiler上的官方PyTorch文档,也不是使用TensorBoard插件来分析分析器结果。我们的目的是展示如何在一个人的日常发展过程中使用这些工具。事实上,如果您还没有,我们建议您在阅读这篇文章之前先查看官方文档。

        一段时间以来,我一直对TensorBoard插件教程的一部分特别感兴趣。本教程介绍了一个分类模型(基于 Resnet 架构),该模型在流行的 Cifar10 数据集上进行训练。它继续演示如何使用PyTorch Profiler和TensorBoard插件来识别和修复数据加载器中的瓶颈。输入数据管道中的性能瓶颈并不少见,我们在之前的一些文章中(例如,在这里)已经详细讨论了它们。本教程令人惊讶的是(截至撰写本文时)呈现的最终(优化后)结果,我们将其粘贴到下面:

性能跟踪优化(来自 PyTorch 网站)

        如果仔细观察,您会发现优化后的 GPU 利用率为 40.46%。现在没有办法粉饰这一点:这些结果绝对是糟糕的,应该让你夜不能寐。正如我们过去所扩展的那样(例如,在这里),GPU 是我们训练机器中最昂贵的资源,我们的目标应该是最大限度地提高其利用率。40.46% 的利用率结果通常代表培训加速和成本节约的重要机会。当然,我们可以做得更好!在这篇博文中,我们将努力做得更好。我们将首先尝试重现官方教程中提供的结果,看看我们是否可以使用相同的工具来进一步提高训练性能。

三、玩具示例

下面的代码块包含由 TensorBoard 插件教程定义的训练循环,进行了两个小的修改:

  1. 我们使用与本教程中使用的CIFAR10数据集具有相同属性和行为的假数据集。这种变化的动机可以在这里找到。
  2. 我们初始化torch.profiler.schedule,预热标志设置为3重复标志设置为1。我们发现,预热步骤数量的略微增加提高了分析结果的稳定性。
import numpy as np
import torch
import torch.nn
import torch.optim
import torch.profiler
import torch.utils.data
import torchvision.datasets
import torchvision.models
import torchvision.transforms as T
from torchvision.datasets.vision import VisionDataset
from PIL import Image

class FakeCIFAR(VisionDataset):
    def __init__(self, transform):
        super().__init__(root=None, transform=transform)
        self.data = np.random.randint(low=0,high=256,size=(10000,32,32,3),dtype=np.uint8)
        self.targets = np.random.randint(low=0,high=10,size=(10000),dtype=np.uint8).tolist()

    def __getitem__(self, index):
        img, target = self.data[index], self.targets[index]
        img = Image.fromarray(img)
        if self.transform is not None:
            img = self.transform(img)
        return img, target

    def __len__(self) -> int:
        return len(self.data)

transform = T.Compose(
    [T.Resize(224),
     T.ToTensor(),
     T.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

train_set = FakeCIFAR(transform=transform)
train_loader = torch.utils.data.DataLoader(train_set, batch_size=32, 
                                           shuffle=True)

device = torch.device("cuda:0")
model = torchvision.models.resnet18(weights='IMAGENET1K_V1').cuda(device)
criterion = torch.nn.CrossEntropyLoss().cuda(device)
optimizer = torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9)
model.train()

# train step
def train(data):
    inputs, labels = data[0].to(device=device), data[1].to(device=device)
    outputs = model(inputs)
    loss = criterion(outputs, labels)
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

# training loop wrapped with profiler object
with torch.profiler.profile(
        schedule=torch.profiler.schedule(wait=1, warmup=4, active=3, repeat=1),
        on_trace_ready=torch.profiler.tensorboard_trace_handler('./log/resnet18'),
        record_shapes=True,
        profile_memory=True,
        with_stack=True
) as prof:
    for step, batch_data in enumerate(train_loader):
        if step >= (1 + 4 + 3) * 1:
            break
        train(batch_data)
        prof.step()  # Need to call this at the end of each step

        本教程中使用的GPU是Tesla V100-DGXS-32GB。在这篇文章中,我们尝试使用包含Tesla V2-SXM3–2GB GPU的Amazon EC100 p2.16xlarge实例重现并改进本教程的性能结果。尽管它们共享相同的架构,但您可以在此处了解两个 GPU 之间存在一些差异。我们使用 AWS PyTorch 2.0 Docker 映像运行训练脚本。下图捕获了 TensorBoard 查看器概述页面中显示的训练脚本的性能结果:

TensorBoard Profiler 概述选项卡中显示的基线性能结果(由作者捕获)

        我们首先注意到,与本教程相反,我们实验中的概述页面(torch-tb-profiler 版本 0.4.1)将三个分析步骤合并为一个。因此,平均总步长时间为 80 毫秒,而不是报告的 240 毫秒。这可以在“跟踪”选项卡(根据我们的经验,几乎总是提供更准确的报告)中清楚地看到,其中每个步骤需要 ~80 毫秒。

TensorBoard Profiler 跟踪视图选项卡中显示的基线性能结果(由作者捕获)

        请注意,我们的起点 31.65% GPU 利用率和 80 毫秒的步进时间与教程中分别提供的 23.54% 和 132 毫秒的起点不同。这可能是训练环境差异的结果,包括 GPU 类型和 PyTorch 版本。我们还注意到,虽然教程基线结果清楚地将性能问题诊断为 DataLoader 中的瓶颈,但我们的结果却没有。我们经常发现,数据加载瓶颈会在“概述”选项卡中伪装成“CPU Exec”或“其他”的高比例。

3.1 优化#1:多进程数据加载

        让我们从应用多进程数据加载开始,如教程中所述。由于 Amazon EC2 p3.2xlarge 实例有 8 个 vCPU,因此我们将 DataLoader 辅助角色的数量设置为 8 个,以获得最佳性能:

train_loader = torch.utils.data.DataLoader(train_set, batch_size=32, 
                               shuffle=True, num_workers=8)

        此优化的结果如下所示:

TensorBoard Profiler 概述选项卡中多进程数据加载的结果(由作者捕获)

        对单行代码的更改使 GPU 利用率提高了 200% 以上(从 31.65% 提高到 72.81%),并将我们的训练步骤时间减少了一半以上(从 80 毫秒减少到 37 毫秒)。

本教程中的优化过程到此结束。尽管我们的GPU利用率(72.81%)比教程中的结果(40.46%)高得多,但我毫不怀疑,像我们一样,您会发现这些结果仍然不令人满意。

您应该随意跳过的个人评论:想象一下,如果在 GPU 上训练时,如果 PyTorch 默认应用多进程数据加载,可以节省多少全球资金!诚然,使用多处理可能会有一些不必要的副作用。但是,必须运行某种形式的自动检测算法,以排除潜在问题场景的存在并相应地应用此优化。

3.2 优化#2:内存固定

        如果我们分析上一个实验的跟踪视图,我们可以看到大量时间(10 毫秒中的 37 毫秒)仍然花费在将训练数据加载到 GPU 上。

“跟踪视图”选项卡中多进程数据加载的结果(由作者捕获)

        为了解决这个问题,我们将应用另一个 PyTorch 推荐的优化来简化数据输入流,即内存固定。使用固定内存可以提高主机到 GPU 数据复制的速度,更重要的是,允许我们使它们异步。这意味着我们可以在 GPU 中准备下一个训练批次,同时在当前批次上运行训练步骤。请务必注意,尽管异步执行通常会提高性能,但它也会降低时间测量的准确性。出于我们的博客文章的目的,我们将继续使用 PyTorch Profiler 报告的测量值。有关如何获得精确测量的说明,请参阅此处。有关内存固定及其副作用的其他详细信息,请参阅 PyTorch 文档。

        此内存固定优化需要更改两行代码。首先,我们将 DataLoader 的 pin_memory 标志设置为 True。

train_loader = torch.utils.data.DataLoader(train_set, batch_size=32, 
                          shuffle=True, num_workers=8, pin_memory=True)

        然后我们将主机到设备的内存传输(在训练函数中)修改为非阻塞:

inputs, labels = data[0].to(device=device, non_blocking=True), \
                 data[1].to(device=device, non_blocking=True)

内存固定优化的结果如下所示:

TensorBoard 探查器概述选项卡中的内存固定结果(由作者捕获)

        我们的 GPU 利用率现在保持在可观的 92.37%,我们的步进时间进一步减少。但我们仍然可以做得更好。请注意,尽管进行了此优化,但性能报告继续表明我们花费了大量时间将数据复制到 GPU 中。我们将在下面的步骤 4 中回到这个问题。

3.3 优化#3:增加批量大小

        对于下一个优化,我们将注意力吸引到上一个实验的内存视图:

TensorBoard Profiler 中的内存视图(由作者捕获)

        图表显示,在 16 GB 的 GPU 内存中,我们的利用率峰值不到 1 GB。这是资源利用不足的一个极端示例,通常(尽管并非总是)表示有机会提高性能。控制内存利用率的一种方法是增加批大小。在下图中,我们显示了将批大小增加到 512(内存利用率增加到 11.3 GB)时的性能结果。

在 TensorBoard 分析器概述选项卡中增加批大小的结果(由作者捕获)

        尽管 GPU 利用率测量值变化不大,但我们的训练速度已大幅提高,从每秒 1200 个样本(批大小 46 为 32 毫秒)提高到每秒 1584 个样本(批大小 324 为 512 毫秒)。

        警告:与我们之前的优化相反,增加批大小可能会影响训练应用程序的行为。不同的模型对批量大小的变化表现出不同程度的敏感度。有些可能只需要对优化器设置进行一些调整。对于其他人来说,适应大批量可能更困难甚至不可能。请参阅上一篇文章,了解大批量培训所涉及的一些挑战。

3.4 优化#4:减少主机到设备的复制

        您可能注意到了我们之前结果中饼图中代表主机到设备数据副本的大红色眼球。尝试解决这种瓶颈的最直接方法是看看我们是否可以减少每批中的数据量。请注意,在我们的图像输入的情况下,我们将数据类型从 8 位无符号整数转换为 32 位浮点数,并在执行数据复制之前应用规范化。在下面的代码块中,我们提出了对输入数据流的更改,其中我们延迟数据类型转换和规范化,直到数据在 GPU 上:

# maintain the image input as an 8-bit uint8 tensor
transform = T.Compose(
    [T.Resize(224),
     T.PILToTensor()
     ])
train_set = FakeCIFAR(transform=transform)
train_loader = torch.utils.data.DataLoader(train_set, batch_size=1024, shuffle=True, num_workers=8, pin_memory=True)

device = torch.device("cuda:0")
model = torch.compile(torchvision.models.resnet18(weights='IMAGENET1K_V1').cuda(device), fullgraph=True)
criterion = torch.nn.CrossEntropyLoss().cuda(device)
optimizer = torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9)
model.train()

# train step
def train(data):
    inputs, labels = data[0].to(device=device, non_blocking=True), \
                     data[1].to(device=device, non_blocking=True)
    # convert to float32 and normalize
    inputs = (inputs.to(torch.float32) / 255. - 0.5) / 0.5
    outputs = model(inputs)
    loss = criterion(outputs, labels)
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

由于这一变化,从 CPU 复制到 GPU 的数据量减少了 4 倍,红色的眼睛几乎消失了:

在 TensorBoard 探查器概述选项卡中将 CPU 复制到 GPU 的结果(由作者捕获)

        我们现在站在97.51%(!!)的新高GPU 利用率和每秒 1670 个样本的训练速度!让我们看看我们还能做什么。

3.5 优化#5:将梯度设置为无

        在这个阶段,我们似乎正在充分利用GPU,但这并不意味着我们不能更有效地利用它。据说可以减少 GPU 中内存操作的一种流行优化是在每个训练步骤中将模型参数梯度设置为 None 而不是。有关此优化的更多详细信息,请参阅 PyTorch 文档。实现此优化所需的只是将optimizer.zero_grad调用的set_to_none设置为 True

optimizer.zero_grad(set_to_none=True)

在我们的案例中,这种优化并没有以任何有意义的方式提高我们的性能。

3.6 优化#6:自动混合精度

        GPU 内核视图显示 GPU 内核处于活动状态的时间量,可以作为提高 GPU 利用率的有用资源:

TensorBoard Profiler 中的内核视图(由作者捕获)

        本报告中最明显的细节之一是缺乏使用 GPU 张量核心。张量核心可用于相对较新的 GPU 架构,是用于矩阵乘法的专用处理单元,可以显着提高 AI 应用程序的性能。它们的缺乏使用可能代表优化的重要机会。

        由于张量核是专门为混合精度计算而设计的,因此提高其利用率的一种直接方法是修改我们的模型以使用自动混合精度 (AMP)。在 AMP 模式下,模型的某些部分会自动转换为精度较低的 16 位浮点数,并在 GPU 张量核心上运行。

        重要的是,请注意,AMP 的完整实现可能需要梯度缩放,我们未包含在演示中。在调整之前,请务必查看有关混合精度训练的文档。

        下面的代码块演示了启用 AMP 所需的训练步骤的修改。

def train(data):
    inputs, labels = data[0].to(device=device, non_blocking=True), \
                     data[1].to(device=device, non_blocking=True)
    inputs = (inputs.to(torch.float32) / 255. - 0.5) / 0.5
    with torch.autocast(device_type='cuda', dtype=torch.float16):
        outputs = model(inputs)
        loss = criterion(outputs, labels)
    # Note - torch.cuda.amp.GradScaler() may be required  
    optimizer.zero_grad(set_to_none=True)
    loss.backward()
    optimizer.step()

        下图显示了对张量核心利用率的影响。尽管它继续表明有进一步改进的机会,但仅使用一行代码,利用率就从 0% 跃升至 26.3%。

张量核心利用率与 AMP 优化从 TensorBoard 分析器中的内核视图(由作者捕获)

        除了增加张量核心利用率外,使用 AMP 还可以降低 GPU 内存利用率,从而释放更多空间来增加批量大小。下图捕获了 AMP 优化和批量大小设置为 1024 后的训练性能结果:

TensorBoard 分析器概述选项卡中的 AMP 优化结果(由作者捕获)

        尽管 GPU 利用率略有下降,但我们的主要吞吐量指标进一步增加了近 50%,从每秒 1670 个样本增加到 2477 个。我们正在滚动!

        警告: 降低模型各部分的精度可能会对其收敛产生有意义的影响。与增加批大小(见上文)的情况一样,使用混合精度的影响将因模型而异。在某些情况下,AMP 几乎不会费力地工作。其他时候,可能需要更加努力地调整自动缩放程序。还有一些时候,您可能需要显式设置模型不同部分的精度类型(即手动混合精度)。

有关使用混合精度作为内存优化方法的更多详细信息,请参阅我们之前关于该主题的博客文章。

3.7 优化#7:在图形模式下训练

        我们将应用的最终优化是模型编译。与默认的 PyTorch 渴望执行模式相反,在这种模式下,每个 PyTorch 操作都“急切”运行,编译 API 将您的模型转换为中间计算图,然后以最适合底层训练加速器的方式编译为低级计算内核。有关 PyTorch 2 中模型编译的更多信息,请查看我们之前关于该主题的文章。

以下代码块演示了应用模型编译所需的更改:

model = torchvision.models.resnet18(weights='IMAGENET1K_V1').cuda(device)
model = torch.compile(model)

模型编译优化的结果如下所示:

TensorBoard Profiler 概述选项卡中的图形编译结果(由作者捕获)

        与上一次实验中的 3268 个样本相比,模型编译进一步将我们的通量提高到每秒 2477 个样本,性能又提高了 32% (!!)。

        图编译改变训练步骤的方式在 TensorBoard 插件的不同视图中非常明显。例如,内核视图指示使用新的(融合的)GPU 内核,而跟踪视图(如下所示)显示的模式与我们之前看到的完全不同。

Results of Graph Compilation in the TensorBoard Profiler Trace View Tab (Captured by Author)

四、Interim Results

In the table below we summarize the results of the successive optimizations we have applied.

Performance Results Summary (By Author)

通过使用 PyTorch Profiler 和 TensorBoard 插件应用我们的迭代分析和优化方法,我们能够将性能提高 817%!!

        我们的工作完成了吗?绝对不行!我们实施的每项优化都会发现新的潜在性能改进机会。这些机会以释放资源的形式呈现(例如,转向混合精度使我们能够增加批量大小的方式)或新发现的性能瓶颈的形式(例如,我们的最终优化发现主机到设备数据传输中的瓶颈的方式)。此外,还有许多其他众所周知的优化形式,我们在这篇文章中没有尝试(例如,见这里和这里)。最后,新的库优化(例如,我们在步骤 7 中演示的模型编译功能)一直在发布,进一步实现了我们的性能改进目标。正如我们在简介中强调的那样,要充分利用这些机会,性能优化必须是开发工作流程中迭代且一致的部分。

五、总结

        在这篇文章中,我们展示了玩具分类模型性能优化的巨大潜力。尽管您可以使用其他性能分析器,每种分析器都有其优点和缺点,但我们选择了PyTorch Profiler和TensorBoard插件,因为它们易于集成。

        我们应该强调的是,成功优化的路径会因训练项目的细节而有很大差异,包括模型架构和训练环境。在实践中,实现目标可能比我们在此处介绍的示例更困难。我们描述的一些技术可能对您的表现几乎没有影响,甚至可能使情况变得更糟。我们还注意到,我们选择的精确优化以及我们选择应用它们的顺序有些武断。我们强烈建议您开发自己的工具和技术,以根据项目的具体细节实现优化目标。

        机器学习工作负载的性能优化有时被视为次要的、非关键的和可恶的。我希望我们已经成功地说服您,节省开发时间和成本的潜力值得在性能分析和优化方面进行有意义的投资。而且,嘿,你甚至可能会发现它很有趣:)。

接下来呢?

这只是冰山一角。性能优化的内容比我们在这里介绍的要多得多。在这篇文章的续集中,我们将深入探讨一个在 PyTorch 模型中很常见的性能问题,其中部分计算在 CPU 而不是 GPU 上运行,通常是以开发人员不知道的方式运行的。我们还鼓励您查看我们在 medium 上的其他帖子,其中许多文章涵盖了机器学习工作负载性能优化的不同元素。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1049624.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

江西广电会展集团总经理李悦一行莅临拓世科技集团调研参观,科技璀璨AIGC掀新潮

在江西这片充满活力的土地上,数字经济如潮水般涌动,会展文化与科技的完美结合,正如新时代的璀璨繁星照亮夜空,更预示着一场AIGC创新的壮丽篇章即将展开。作为拓世科技集团的老朋友,江西广电多位领导多次莅临拓世科技集…

【MATLAB源码-第40期】基于matlab的D*(Dstar)算法栅格路径规划仿真。

1、算法描述 D*算法路径规划 D*算法(Dynamic A*)是A*算法的一种变种,主要用于在地图中的障碍物信息发生变化时重新计算路径,而不需要从头开始。该算法适用于那些只有部分信息已知的环境中。 工作原理: 1. D*算法首先…

嵌入式Linux应用开发-第七章-RK3288和 RK3399的 LED驱动程序

嵌入式Linux应用开发-第七章-RK3288和 RK3399的 LED驱动程序 RK3288和 RK3399的 LED驱动程序7.3 RK3288和 RK3399的 LED驱动程序7.3.1 原理图7.3.1.1 fireflye RK3288的 LED原理图7.3.1.2 firefly RK3399的 LED原理图 7.3.2 所涉及的寄存器操作7.3.2.1 RK3288的 GPIO8_A1引脚7.…

探索ClickHouse——使用MaterializedView存储kafka传递的数据

在《探索ClickHouse——连接Kafka和Clickhouse》中,我们讲解了如何使用kafka engin连接kafka,并读取topic中的数据。但是遇到了一个问题,就是数据只能读取一次,即使后面还有新数据发送到该topic,该表也读不出来。 为了…

Ae 效果:CC Smear

扭曲/CC Smear Distort/CC Smear CC Smear (CC 漩涡条纹)可以将两点之间的源图像内容进行拉伸扭曲变形,形成一种类似油墨被涂抹或者柔体被拉扯之后的漩涡条纹效果。 ◆ ◆ ◆ 效果属性说明 From 从 漩涡条纹效果的起点。 默认为合成的中心。…

麒麟v10获取wwn

for i in cat /proc/partitions | awk {print $4} | grep sd do echo "Device: $i WWID: /usr/lib/udev/scsi_id --page0x83 --whitelisted --device/dev/$i" done | sort -k4

用于时间触发的嵌入式软件的IDE

TTE Systems的RapidiTTy IDE为希望创建“时间触发”微控制器软件以提高整体系统可靠性的开发人员提供了一个独立的环境。RapidiTTy(下面的图1)旨在解决深度嵌入的应用,包括医疗,国防,汽车和工业部门以及白色和棕色商品…

【Linux】Linux远程访问Windows下的MySQL数据库

1.建立Windows防火墙规则 首先需要开放windows防火墙,针对3306端口单独创建一条规则,允许访问。 打开windows安全中心防火墙与保护,点击高级设置 进入之后,点击入站规则,新建一条规则 新建端口入站规则 端口填写330…

【计算机网络】 基于TCP的简单通讯(客户端)

文章目录 流程伪代码代码实现加载库创建套接字连接服务端收发数据关闭套接字、卸载库 测试 流程伪代码 //1、加载库//2、创建套接字//3、连接服务端while(true){//4、发送数据//5、接收数据} //6、关闭套接字、卸载库代码实现 加载库 int err 0;WORD version MAKEWORD(2, 2…

表单控件拖拽平台都有哪些特点?

什么样的表单控件拖拽平台可以提升办公协作效率?在竞争激烈的当今社会,利用低代码技术平台的优势和特点,可以将企业内部的数据资源真正利用起来,帮助更多领域的客户朋友做出更有竞争优势的经营决策,实现数字化转型和流…

【Verilog 教程】6.3Verilog状态机

关键词:状态机,售卖机 有限状态机(Finite-State Machine,FSM),简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。状态机不仅是一种电路的描述工具,而且…

0x23根据地址读取内存服务

诊断仪从ECU通过起始地址和内存大小空间来读取指定内存的数据,高地址范围未使用到的字节用0x00去填充。 请求报文格式 肯定响应报文形式

Makefile 优化编译速度

Makefile 优化编译速度 编写一个通用的 Makefile 模板,用来实现任意工程的编译管理架构搭建。 这里我主要有两种想法: 1. 用一个 Makefile 管理所有的文件。所有的文件都放到最顶层的 Makefile 中,一次性直接加载所有的 .c 文件完成编译。 …

使用ElementUI结合Mock完成主页的搭建

目录 一、Mock ( 1 ) 讲述 ( 2 ) 作用 二、引用 三、主页搭建 学习后带来的收获 一、Mock ( 1 ) 讲述 Mock.js是一个用于前端开发中模拟数据的库。它可以帮助开发人员在前端开发过程中模拟接口返回的数据,从而实现前后端分离开发。Mock.js提供了一套简单易…

优维产品最佳实践:实例视图

背 景 模型可以定义很多的字段,当这些字段越来越多的时候,直接打开实例页面,会杂乱无章的呈现出来,对于用户来说无法快速的找到想要的信息,也不便于查看数据。而且并不是所有的字段都一定会录入了数据,常常…

软件测试/测试开发丨结对编程助手 GitHubCopilot

点此获取更多相关资料 简介 GitHub Copilot 是一款 AI 结对程序员,可帮助您更快、更少地编写代码。GitHub Copilot 由 GitHub、OpenAI 和 Microsoft 开发的生成式 AI 模型提供支持。它可作为 Visual Studio Code、Visual Studio、Neovim 和 JetBrains 集成开发环境…

Vovsoft Text Edit Plus 专业文本编辑器工具软件:简洁高效的创作利器

作为一名专业软件评测人员,我有幸使用了一款备受赞誉的文本编辑器工具软件——Vovsoft Text Edit Plus。在这篇评测中,我将客观、细致地分析它的实用性和使用场景,同时揭示它的优缺点,帮助您更好地了解这款软件。 第一部分&#x…

ROS2 从头开始:第 4 部分 - 使用 ROS2 组合构建强大的机器人系统

一、说明 组件编程是软件规模庞大后,有限的程序个体不能满足系统的动态扩张的系统设计方案。组件编程的首要用途就是计算机操作系统。而ROS2也是操作系统,动态扩大或缩减是必然存在的,因此需要组件实现。 组件编程的其它参考资料: 【BOOST C++】组件编程(…

01-http概述

HTTP概述 HTTP使用的是可靠地数据传输协议。HTTP属于应用层协议 Web客户端和服务器 web服务器:又称http服务器,用于存储web内容,并向web客户端提供web内容web客户端:用于请求web服务器的应用软件,常见为浏览器 资源…

Java实现word excel ppt模板渲染与导出及预览 LibreOffice jodconverter

Java Office 一、文档格式转换 文档格式转换是office操作中经常需要进行一个操作,例如将docx文档转换成pdf格式。 java在这方面有许多的操作方式,大致可以分为内部调用(无需要安装额外软件),外部调用(需…