当前位置: 首页 > news >正文

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

一、说明

        这篇文章的重点将是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 Imageclass 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, targetdef __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:breaktrain(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 normalizeinputs = (inputs.to(torch.float32) / 255. - 0.5) / 0.5outputs = 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.5with 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 上的其他帖子,其中许多文章涵盖了机器学习工作负载性能优化的不同元素。

相关文章:

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

一、说明 这篇文章的重点将是GPU上的PyTorch培训。更具体地说,我们将专注于 PyTorch 的内置性能分析器 PyTorch Profiler,以及查看其结果的方法之一,即 PyTorch Profiler TensorBoard 插件。 二、深度框架 训练深度学习模型,尤其是…...

Unity3D 简易音频管理器

依赖于Addressable 依赖于单例模板&#xff1a;传送门 using System.Collections.Generic; using System.Security.Cryptography; using System; using UnityEngine; using UnityEngine.AddressableAssets;namespace EasyAVG {public class AudioManager : MonoSingleton<…...

【李沐深度学习笔记】线性回归

课程地址和说明 线性回归p1 本系列文章是我学习李沐老师深度学习系列课程的学习笔记&#xff0c;可能会对李沐老师上课没讲到的进行补充。 线性回归 如何在美国买房&#xff08;经典买房预测问题&#xff09; 一个简化的模型 线性模型 其中&#xff0c; x → [ x 1 , x 2 ,…...

微信收款码费率0.38太坑了

作为一个有多年运营经验的商家&#xff0c;我本人在申请收款功能时曾经走过了不少弯路。我找遍了市面上的知名的支付公司&#xff0c;但了解到的收款手续费率通常都在0.6左右&#xff0c;最低也只能降到0.38。这个过程吃过不少苦头。毕竟&#xff0c;收款功能是我们商家的命脉&…...

【学习笔记】CF1103D Professional layer

首先分析不出啥性质&#xff0c;所以肯定是暴力优化&#x1f605; 常见的暴力优化手段有均摊&#xff0c;剪枝&#xff0c;数据范围分治&#xff08;points&#xff09;&#xff0c;答案值域分析之类的。 比较经典的题目是 CF1870E Another MEX Problem&#xff0c;可以用剪枝…...

vue之Pinia

定义 Store | Pinia 开发文档 1.什么是Pinaia Pinia 是 Vue 的专属状态管理库&#xff0c;它允许你跨组件或页面共享状态。 2.理解Pinaia核心概念 定义Store 在深入研究核心概念之前&#xff0c;我们得知道 Store 是用 defineStore() 定义的&#xff0c;它的第一个参数要求是一…...

antd-vue 级联选择器默认值不生效解决方案

一、业务场景&#xff1a; 最近在使用Vue框架和antd-vue组件库的时候&#xff0c;发现在做编辑回显时** 级联选择器** 组件的默认值不生效。为了大家后面遇到和我一样的问题&#xff0c;给大家分享一下 二、bug信息&#xff1a; 三、问题原因&#xff1a; 确定不了唯一的值&a…...

分享53个Python源码源代码总有一个是你想要的

分享53个Python源码源代码总有一个是你想要的 链接&#xff1a;https://pan.baidu.com/s/1ew3w2_DXlSBrK7Mybx3Ttg?pwd8888 提取码&#xff1a;8888 项目名称 100-Python ControlXiaomiDevices DRF-ADMIN 后台管理系统 FishC-Python3小甲鱼 Flask框架的api项目脚手架 …...

【每日一题】658. 找到 K 个最接近的元素

658. 找到 K 个最接近的元素 - 力扣&#xff08;LeetCode&#xff09; 给定一个 排序好 的数组 arr &#xff0c;两个整数 k 和 x &#xff0c;从数组中找到最靠近 x&#xff08;两数之差最小&#xff09;的 k 个数。返回的结果必须要是按升序排好的。 整数 a 比整数 b 更接近 …...

并发任务队列(字节青训测试题)

需求描述 封装一个并发任务队列类&#xff0c;用于对一些异步任务按指定的并发数量进行并发执行。 /*** 延迟函数* param {number} time - 延迟时间* return {Promise} delayFn - 延迟函数(异步封装)*/ function timeout(time) {return new Promise((resolve) > {setTimeo…...

Ubuntu 安装Nacos

1、官网下载最新版nacos https://github.com/alibaba/nacos/releases 本人环境JDK8&#xff0c;Maven3.6.3&#xff0c;启动Nacos2.2.1启动失败&#xff0c;故切换到2.1.0启动成功 2、放到服务器目录下&#xff0c;我的在/home/xxx/apps下 3、解压 $ tar -zxvf nacos-serve…...

CSS 小球随着椭圆移动

html代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><…...

【李沐深度学习笔记】线性代数

课程地址和说明 线性代数p1 本系列文章是我学习李沐老师深度学习系列课程的学习笔记&#xff0c;可能会对李沐老师上课没讲到的进行补充。 线性代数 标量 标量&#xff08;scalar&#xff09;&#xff0c;亦称“无向量”。有些物理量&#xff0c;只具有数值大小&#xff0c…...

vuejs - - - - - 递归组件的实现

递归组件的实现 1. 需求描述&#xff1a;2. 效果图&#xff1a;3. 代码3.1 封装组件代码3.2 父组件使用 1. 需求描述&#xff1a; 点击添加行&#xff0c;增加一级目录结构当类型为object or array时&#xff0c;点击右侧➕&#xff0c;增加子集点击右侧&#x1f6ae;&#x…...

精准对接促合作:飞讯受邀参加市工信局举办的企业供需对接会

2023年9月21日&#xff0c;由惠州市工业和信息化局主办的惠州市工业软件企业与制造业企业供需对接会成功举办&#xff0c;对接会旨在促进本地工业软件企业与制造业企业的紧密合作&#xff0c;推动数字化转型的深入发展。此次会议在市工业和信息化局16楼会议室举行&#xff0c;会…...

数学建模之遗传算法

文章目录 前言遗传算法算法思想生物的表示初始种群的生成下一代种群的产生适应度函数轮盘赌交配变异混合产生新种群 停止迭代的条件遗传算法在01背包中的应用01背包问题介绍01背包的其它解法01背包的遗传算法解法生物的表示初始种群的生成下一代种群的产生适应度函数轮盘赌交配…...

ISO9001认证常见的不符合项

今天&#xff0c;整理了一些关于ISO9001质量管理体系审核最常见的不合格项&#xff0c;以供大家参考。 一、质量管理体系 1、质量手册&#xff08;标准条款4.2.2&#xff09; &#xff08;1&#xff09;各部门执行的文件与手册的规定不一致。 &#xff08;2&#xff09;质量…...

crypto:看我回旋踢

题目 下载压缩包后解压可得到提示文本 经过观察&#xff0c;synt{}这个提示与flag{}形式很像 由题目名中的回旋可以推测为凯撒密码&#xff0c;由凯撒密码的定义可知&#xff0c;需要先推出移位数&#xff0c;s->f数13次&#xff0c;因此移位数为13&#xff0c;解码可得...

Springcloud实战之自研分布式id生成器

一&#xff0c;背景 日常开发中&#xff0c;我们需要对系统中的各种数据使用 ID 唯一表示&#xff0c;比如用户 ID 对应且仅对应一个人&#xff0c;商品 ID 对应且仅对应一件商品&#xff0c;订单 ID 对应且仅对应 一个订单。我们现实生活中也有各种 ID &#xff0c;比如身…...

java 企业工程管理系统软件源码 自主研发 工程行业适用

工程项目管理软件&#xff08;工程项目管理系统&#xff09;对建设工程项目管理组织建设、项目策划决策、规划设计、施工建设到竣工交付、总结评估、运维运营&#xff0c;全过程、全方位的对项目进行综合管理 工程项目各模块及其功能点清单 一、系统管理 1、数据字典&am…...

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周&#xff0c;有很多同学在写期末Java web作业时&#xff0c;运行tomcat出现乱码问题&#xff0c;经过多次解决与研究&#xff0c;我做了如下整理&#xff1a; 原因&#xff1a; IDEA本身编码与tomcat的编码与Windows编码不同导致&#xff0c;Windows 系统控制台…...

linux之kylin系统nginx的安装

一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源&#xff08;HTML/CSS/图片等&#xff09;&#xff0c;响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址&#xff0c;提高安全性 3.负载均衡服务器 支持多种策略分发流量…...

线程与协程

1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指&#xff1a;像函数调用/返回一样轻量地完成任务切换。 举例说明&#xff1a; 当你在程序中写一个函数调用&#xff1a; funcA() 然后 funcA 执行完后返回&…...

解锁数据库简洁之道:FastAPI与SQLModel实战指南

在构建现代Web应用程序时&#xff0c;与数据库的交互无疑是核心环节。虽然传统的数据库操作方式&#xff08;如直接编写SQL语句与psycopg2交互&#xff09;赋予了我们精细的控制权&#xff0c;但在面对日益复杂的业务逻辑和快速迭代的需求时&#xff0c;这种方式的开发效率和可…...

【HTML-16】深入理解HTML中的块元素与行内元素

HTML元素根据其显示特性可以分为两大类&#xff1a;块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...

【JavaWeb】Docker项目部署

引言 之前学习了Linux操作系统的常见命令&#xff0c;在Linux上安装软件&#xff0c;以及如何在Linux上部署一个单体项目&#xff0c;大多数同学都会有相同的感受&#xff0c;那就是麻烦。 核心体现在三点&#xff1a; 命令太多了&#xff0c;记不住 软件安装包名字复杂&…...

JAVA后端开发——多租户

数据隔离是多租户系统中的核心概念&#xff0c;确保一个租户&#xff08;在这个系统中可能是一个公司或一个独立的客户&#xff09;的数据对其他租户是不可见的。在 RuoYi 框架&#xff08;您当前项目所使用的基础框架&#xff09;中&#xff0c;这通常是通过在数据表中增加一个…...

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...

NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合

在汽车智能化的汹涌浪潮中&#xff0c;车辆不再仅仅是传统的交通工具&#xff0c;而是逐步演变为高度智能的移动终端。这一转变的核心支撑&#xff0c;来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒&#xff08;T-Box&#xff09;方案&#xff1a;NXP S32K146 与…...

Web中间件--tomcat学习

Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机&#xff0c;它可以执行Java字节码。Java虚拟机是Java平台的一部分&#xff0c;Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...