揭秘 AMD GPU 上 PyTorch Profiler 的性能洞察

news/2024/9/17 7:38:23/ 标签: pytorch, 人工智能, 学习

Unveiling performance insights with PyTorch Profiler on an AMD GPU — ROCm Blogs

2024年5月29日,作者:Phillip Dang。
在机器学习领域,优化性能通常和改进模型架构一样重要。在本文中,我们将深入探讨 PyTorch Profiler,这是一款设计用于帮助深入了解我们 PyTorch 模型内部状态的便捷工具,能够揭示瓶颈和低效之处。本文将介绍 PyTorch Profiler 的基本工作原理以及如何在 AMD GPU + ROCm 系统中利用它来提高模型效率。

什么是 PyTorch Profiler?

PyTorch Profiler 是一个性能分析工具,使开发人员能够检查 PyTorch 模型训练和推理的各个方面。它允许用户收集和分析详细的分析信息,包括 GPU/CPU 利用率、内存使用情况以及模型内不同操作的执行时间。通过利用 PyTorch Profiler,开发人员可以获得关于其模型运行时行为的宝贵见解,并发现潜在的优化机会。
使用 PyTorch Profiler 非常简单,只需几个步骤:
1. 标注代码:要开始对 PyTorch 代码进行分析,您需要使用分析注释对其进行标注。这些注释指定了要分析的代码区域或操作。PyTorch Profiler 提供了上下文管理器和装饰器以便于标注。
2. 配置分析器设置:根据您的分析需求配置分析器设置。您可以指定参数,如详细程度、分析模式(例如 CPU, GPU)和输出格式。
3. 运行分析:在代码标注完成且分析器设置配置好后,像往常一样运行您的 PyTorch 代码。分析器将在执行期间收集性能数据。
4. 分析分析结果:执行后,使用 PyTorch Profiler 提供的可视化工具分析分析结果。探索时间线、火焰图和内存使用图,以识别性能瓶颈和优化机会。
5. 迭代和优化:利用从分析中获得的洞见来反复优化代码。根据分析数据进行有针对性的优化,并重新运行分析器以评估您更改的影响。

先决条件

要跟随本博客的内容,您需要以下软件:
- ROCm
- PyTorch
- Linux 操作系统

有关支持的 GPU 和操作系统的列表,请参阅此页面。为了方便和稳定,我们建议您直接在 Linux 系统中使用以下代码拉取并运行 rocm/pytorch Docker:

docker run -it --ipc=host --network=host --device=/dev/kfd --device=/dev/dri \--group-add video --cap-add=SYS_PTRACE --security-opt seccomp=unconfined \--name=olmo rocm/pytorch:rocm6.0_ubuntu20.04_py3.9_pytorch_2.1.1 /bin/bash

检查您的硬件并确保系统识别到您的 GPU,请运行:

! rocm-smi --showproductname

您的输出应如下所示:

================= ROCm System Management Interface ================
========================= Product Info ============================
GPU[0] : Card series: Instinct MI210
GPU[0] : Card model: 0x0c34
GPU[0] : Card vendor: Advanced Micro Devices, Inc. [AMD/ATI]
GPU[0] : Card SKU: D67301
===================================================================
===================== End of ROCm SMI Log =========================

接下来,确保 PyTorch 检测到您的 GPU:

import torch
print(f"number of GPUs: {torch.cuda.device_count()}")
print([torch.cuda.get_device_name(i) for i in range(torch.cuda.device_count())])

您的输出应如下所示:

number of GPUs: 1
['AMD Radeon Graphics']

检测你的代码

导入我们将使用的必要库和模块。

import torch
import torch.nn as nn
import torchvision
from torchvision import transforms
from torch.profiler import profile, record_function, ProfilerActivity

模型

首先,我们创建一个非常简单的卷积神经网络模型进行分析。

class SimpleCNN(nn.Module):def __init__(self):super(SimpleCNN, self).__init__()self.conv1 = nn.Conv2d(3, 16, kernel_size=3, padding=1)self.conv2 = nn.Conv2d(16, 32, kernel_size=3, padding=1)self.fc1 = nn.Linear(32 * 8 * 8, 128)self.fc2 = nn.Linear(128, 10)def forward(self, x):x = torch.relu(self.conv1(x))x = torch.max_pool2d(x, kernel_size=2, stride=2)x = torch.relu(self.conv2(x))x = torch.max_pool2d(x, kernel_size=2, stride=2)x = x.view(-1, 32 * 8 * 8)x = torch.relu(self.fc1(x))x = self.fc2(x)return x

数据

接下来,我们下载一个简单的数据集。

# Load CIFAR-10 dataset 
transform = transforms.Compose([transforms.Resize((32, 32)),transforms.ToTensor(),transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
])
trainset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform)

训练循环

我们将创建一个简单的训练循环,包括前向传递和反向传递,并对其进行分析。为了本博客的目的,我们将分析模型在200个批次上的前向和反向传递,而不是遍历整个数据集。

# Function to train the model
def train(model, trainloader, criterion, optimizer, device, epochs=1):for epoch in range(epochs):for i, data in enumerate(trainloader, 0):inputs, labels = datainputs = inputs.to(device)labels = labels.to(device)optimizer.zero_grad()outputs = model(inputs)loss = criterion(outputs, labels)loss.backward()optimizer.step()# exit after 200 batches if i == 200:break

此外,让我们编写一个实用函数,该函数负责设置优化器和损失函数,实例化模型,并运行实际的分析。

# utility function for running the profiler 
def run_profiler(trainloader, model, profile_memory=False):device = 'cuda'model = model.to(device)criterion = nn.CrossEntropyLoss()optimizer = torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9)activities = [ProfilerActivity.CPU, ProfilerActivity.CUDA]with profile(activities=activities, record_shapes=True, profile_memory=profile_memory) as prof:with record_function("training"):train(model, trainloader, criterion, optimizer, device, epochs=1)if profile_memory == False:print(prof.key_averages().table(sort_by="cuda_time_total", row_limit=10))else:print(prof.key_averages().table(sort_by="self_cpu_memory_usage", row_limit=10))

分析非常简单,只需用分析器上下文管理器包裹训练循环即可。

运行性能分析

有了模型训练循环和性能分析工具函数的实现后,我们可以使用 PyTorch Profiler 来分析执行时间和内存消耗。

执行时间性能分析

我们先来看看训练循环的执行时间。

model = SimpleCNN()
trainloader = torch.utils.data.DataLoader(trainset, batch_size=32, shuffle=True, num_workers=4)
run_profiler(trainloader, model)

输出结果如下所示:

-------------------------------------------------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  Name    Self CPU %      Self CPU   CPU total %     CPU total  CPU time avg     Self CUDA   Self CUDA %    CUDA total  CUDA time avg    # of Calls  
-------------------------------------------------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  training        23.76%     360.249ms        71.31%        1.081s        1.081s       0.000us         0.00%      68.837ms      68.837ms             1  
autograd::engine::evaluate_function: ConvolutionBack...         0.15%       2.271ms         3.63%      55.037ms     136.908us       0.000us         0.00%      34.770ms      86.493us           402  aten::convolution_backward         2.34%      35.480ms         3.34%      50.615ms     125.908us      18.366ms        16.60%      34.770ms      86.493us           402  ConvolutionBackward0         0.14%       2.151ms         3.46%      52.431ms     130.425us       0.000us         0.00%      34.486ms      85.786us           402  autograd::engine::evaluate_function: AddmmBackward0         0.33%       4.960ms         7.98%     120.946ms     300.861us       0.000us         0.00%      16.764ms      41.701us           402  aten::copy_         0.44%       6.674ms         2.08%      31.585ms      77.037us      15.762ms        14.25%      16.408ms      40.020us           410  aten::_to_copy         0.14%       2.079ms         2.31%      34.972ms      86.995us       0.000us         0.00%      16.306ms      40.562us           402  aten::sum         0.78%      11.818ms         0.93%      14.160ms      17.612us      14.723ms        13.31%      16.162ms      20.102us           804  aten::to         0.13%       2.031ms         2.36%      35.852ms      35.674us       0.000us         0.00%      15.783ms      15.704us          1005  CopyHostToDevice         0.00%       0.000us         0.00%       0.000us       0.000us      15.739ms        14.23%      15.739ms      39.152us           402  
-------------------------------------------------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  
Self CPU time total: 1.516s
Self CUDA time total: 110.639ms

注意 self cpu time 和 cpu time 之间的区别。根据[教程](PyTorch Profiler — PyTorch Tutorials 2.4.0+cu121 documentation),“操作符可以调用其它操作符,自身 cpu time 排除了在子操作符调用中花费的时间,而总的 cpu time 包括了这些时间。你可以选择通过其他指标排序,比如传递 sort_by="self_cpu_time_total" 到表格调用中来按自身 cpu time 排序。”

接下来,我们将卷积神经网络(CNN)简化为一个非常简单的线性层,再次运行性能分析。我们预计会看到 CUDA 总时间的显著减少。

class SimpleNet(nn.Module):def __init__(self):super(SimpleNet, self).__init__()self.fc1 = nn.Linear(3 * 32 * 32, 10)def forward(self, x):x = x.view(-1, 3 * 32 * 32)x = self.fc1(x)return xmodel = SimpleNet()
run_profiler(trainloader, model)

以下是输出结果:

-------------------------------------------------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  Name    Self CPU %      Self CPU   CPU total %     CPU total  CPU time avg     Self CUDA   Self CUDA %    CUDA total  CUDA time avg    # of Calls  
-------------------------------------------------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  training        23.91%     192.128ms        84.59%     679.785ms     679.785ms       0.000us         0.00%      39.361ms      39.361ms             1  aten::linear         0.10%     768.000us         1.57%      12.605ms      62.711us       0.000us         0.00%      16.955ms      84.353us           201  aten::addmm         0.99%       7.943ms         1.28%      10.247ms      50.980us      16.955ms        37.52%      16.955ms      84.353us           201  
Cijk_Alik_Bljk_SB_MT64x64x32_MI32x32x2x1_SE_1LDSB0_A...         0.00%       0.000us         0.00%       0.000us       0.000us      15.556ms        34.42%      15.556ms      77.393us           201  aten::copy_         0.25%       2.028ms         3.07%      24.636ms      60.980us      14.614ms        32.34%      14.614ms      36.173us           404  CopyHostToDevice         0.00%       0.000us         0.00%       0.000us       0.000us      14.608ms        32.32%      14.608ms      36.338us           402  aten::_to_copy         0.27%       2.130ms         3.50%      28.122ms      69.955us       0.000us         0.00%      14.554ms      36.204us           402  aten::to         0.31%       2.460ms         3.61%      28.972ms      28.771us       0.000us         0.00%      13.586ms      13.492us          1007  Optimizer.step#SGD.step         2.09%      16.809ms         2.94%      23.664ms     117.731us       0.000us         0.00%       5.557ms      27.647us           201  autograd::engine::evaluate_function: AddmmBackward0         0.28%       2.236ms         1.64%      13.185ms      65.597us       0.000us         0.00%       3.691ms      18.363us           201  
-------------------------------------------------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  
Self CPU time total: 803.604ms
Self CUDA time total: 45.193ms

正如预期的那样,CUDA 总时间显著减少(从 110.639ms 到 45.193ms)。

内存消耗性能分析

我们还可以分析在模型运算过程中分配或释放的张量所使用的内存量。

trainloader = torch.utils.data.DataLoader(trainset, batch_size=32, shuffle=True, num_workers=4)
model = SimpleCNN()
run_profiler(trainloader, model, profile_memory=True)

输出表格如下所示:

-------------------------------------------------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  Name    Self CPU %      Self CPU   CPU total %     CPU total  CPU time avg     Self CUDA   Self CUDA %    CUDA total  CUDA time avg       CPU Mem  Self CPU Mem      CUDA Mem  Self CUDA Mem    # of Calls  
-------------------------------------------------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  
enumerate(DataLoader)#_MultiProcessingDataLoaderIter...        22.44%     224.849ms        22.74%     227.911ms       1.134ms       0.000us         0.00%       0.000us       0.000us      75.42 Mb      75.42 Mb           0 b           0 b           201  aten::empty         0.22%       2.204ms         0.22%       2.204ms       2.731us       0.000us         0.00%       0.000us       0.000us     390.64 Kb     390.64 Kb       3.79 Mb       3.79 Mb           807  aten::scalar_tensor         0.00%       9.000us         0.00%       9.000us       9.000us       0.000us         0.00%       0.000us       0.000us           8 b           8 b           0 b           0 b             1  aten::random_         0.00%      25.000us         0.00%      25.000us      12.500us       0.000us         0.00%       0.000us       0.000us           0 b           0 b           0 b           0 b             2  aten::item         0.00%       9.000us         0.00%      13.000us       6.500us       0.000us         0.00%       0.000us       0.000us           0 b           0 b           0 b           0 b             2  aten::_local_scalar_dense         0.00%       4.000us         0.00%       4.000us       2.000us       0.000us         0.00%       0.000us       0.000us           0 b           0 b           0 b           0 b             2  aten::resize_         0.00%       6.000us         0.00%       6.000us       0.002us       0.000us         0.00%       0.000us       0.000us           0 b           0 b           0 b           0 b          2615  aten::resolve_conj         0.00%       0.000us         0.00%       0.000us       0.000us       0.000us         0.00%       0.000us       0.000us           0 b           0 b           0 b           0 b             1  aten::resolve_neg         0.00%       0.000us         0.00%       0.000us       0.000us       0.000us         0.00%       0.000us       0.000us           0 b           0 b           0 b           0 b             1  aten::to         0.22%       2.206ms         3.73%      37.335ms      37.149us       0.000us         0.00%      14.821ms      14.747us           0 b           0 b      75.47 Mb       2.63 Mb          1005  
-------------------------------------------------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  
Self CPU time total: 1.002s
Self CUDA time total: 109.871ms

如果我们对数据加载器的内存消耗不满意,可以通过尝试各种策略来解决内存瓶颈。这些策略可能包括减少批次大小、简化模型架构或使用混合精度训练。让我们将批次大小从 32 减少到 4,然后再次运行性能分析:

trainloader = torch.utils.data.DataLoader(trainset, batch_size=4, shuffle=True, num_workers=4)
model = SimpleCNN()
run_profiler(trainloader, model, profile_memory=True)

新的输出结果如下:

-------------------------------------------------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  Name    Self CPU %      Self CPU   CPU total %     CPU total  CPU time avg     Self CUDA   Self CUDA %    CUDA total  CUDA time avg       CPU Mem  Self CPU Mem      CUDA Mem  Self CUDA Mem    # of Calls  
-------------------------------------------------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  
enumerate(DataLoader)#_MultiProcessingDataLoaderIter...        13.45%     127.135ms        13.74%     129.910ms     646.318us       0.000us         0.00%       0.000us       0.000us       9.43 Mb       9.43 Mb           0 b           0 b           201  aten::empty         0.23%       2.193ms         0.23%       2.193ms       2.717us       0.000us         0.00%       0.000us       0.000us     390.64 Kb     390.64 Kb       3.87 Mb       3.87 Mb           807  aten::scalar_tensor         0.00%       9.000us         0.00%       9.000us       9.000us       0.000us         0.00%       0.000us       0.000us           8 b           8 b           0 b           0 b             1  aten::random_         0.00%      22.000us         0.00%      22.000us      11.000us       0.000us         0.00%       0.000us       0.000us           0 b           0 b           0 b           0 b             2  aten::item         0.00%       6.000us         0.00%      10.000us       5.000us       0.000us         0.00%       0.000us       0.000us           0 b           0 b           0 b           0 b             2  aten::_local_scalar_dense         0.00%       4.000us         0.00%       4.000us       2.000us       0.000us         0.00%       0.000us       0.000us           0 b           0 b           0 b           0 b             2  aten::resize_         0.00%       7.000us         0.00%       7.000us       0.003us       0.000us         0.00%       0.000us       0.000us           0 b           0 b           0 b           0 b          2615  aten::resolve_conj         0.00%       0.000us         0.00%       0.000us       0.000us       0.000us         0.00%       0.000us       0.000us           0 b           0 b           0 b           0 b             1  aten::resolve_neg         0.00%       0.000us         0.00%       0.000us       0.000us       0.000us         0.00%       0.000us       0.000us           0 b           0 b           0 b           0 b             1  aten::to         0.21%       2.013ms         2.86%      27.042ms      26.907us       0.000us         0.00%       5.850ms       5.821us           0 b           0 b       9.52 Mb     481.50 Kb          1005  
-------------------------------------------------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  ------------  
Self CPU time total: 945.407ms
Self CUDA time total: 83.583ms

在这里,我们显著减少了加载数据所需的 CPU 内存,从 75.42 MB 减少到 9.43 MB。

在这篇博客中,我们展示了通过分析内存性能和执行时间,我们可以有效地提高模型训练过程的效率。我们鼓励读者尝试不同的优化策略以获得最佳结果。


http://www.ppmy.cn/news/1522558.html

相关文章

基于深度学习的结构优化与生成

基于深度学习的结构优化与生成技术应用于多种领域,例如建筑设计、机械工程、材料科学等。该技术通过使用深度学习模型分析和优化结构形状、材料分布、拓扑结构等因素,旨在提高结构性能、减少材料浪费、降低成本、并加快设计流程。 1. 结构优化与生成的核…

从零开始写论文:如何借助ChatGPT生成完美摘要?

AIPaperGPT,论文写作神器~ https://www.aipapergpt.com/ 在写论文的过程中,摘要是一个非常重要的部分,它能够帮助读者快速理解论文的核心内容,决定是否进一步阅读全文。但是许多学生在写摘要的时候常常感到困惑,不知…

怎么仿同款小程序的开发制作方法介绍

很多老板想要仿小程序系统,就是想要做个和别人界面功能类似的同款小程序系统,咨询瀚林问该怎么开发制作?本次瀚林就为大家介绍一下仿制同款小程序系统的方法。 1、确认功能需求 想要模仿同款小程序系统,那么首先需要找到自己想要…

24/9/3算法笔记 kaggle泰坦尼克

题目: 这次我用两种算法做了这道题 逻辑回归二分类算法 import pandas as pd from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler from sklearn.linear_model import LogisticRegression from sklearn.metr…

CentOS 常用指令及作用解析

CentOS 常用指令及作用解析 在使用CentOS操作系统时,了解并熟练掌握常用的Linux指令是非常重要的。这些指令可以帮助你进行文件管理、系统管理、网络管理等操作。本篇文章将介绍一些CentOS下常用的指令及其主要作用。 目录 文件和目录操作指令文件内容操作指令系…

5千多道安全生产证考试题库ACCESS\EXCEL数据库

安全生产是保护劳动者的安全、健康和国家财产,促进社会生产力发展的基本保证,也是保证社会主义经济发展,进一步实行改革开放的基本条件。因此,做好安全生产工作具有重要的意义。今天的数据即是安全生产资格证、许可证考试题库。 大…

Unity --- 各种关节(Joints)来模拟物体之间的连接

目录 一:2D关节 一:1 固定关节 (Fixed Joint 2D) 功能: 适用场景: 1. 平台游戏中的固定平台: 2. 拼图游戏中的固定部件: 3. 建筑游戏中的固定结构: 一:2 铰链关节 (Hinge Joint 2D) 功能: 适用场景: 一:3 弹簧关节 (Spring Joint 2D) 功能: 适用场景: 1. …

【系统架构设计师】命令行风格

命令行风格(Command Line Interface, CLI)是一种用户与计算机程序交互的方式,它主要通过文本命令来执行程序的功能。在这种风格中,用户通过键盘输入命令,程序则通过命令行界面(通常是终端或控制台窗口)显示输出和反馈信息。命令行风格因其高效、灵活和强大的功能而广泛应…

Spring2~~~

注解配置Bean Spring的 IOC 容器检查到注解就会生成对象&#xff0c;但这个注解的具体含义不会识别 配置自动扫描的包 <!--配置容器要扫描的包1. component-scan 要对指定包下的类进行扫描, 并创建对象到容器2. base-package 指定要扫描的包3. 含义是当spring容器创建/初始…

在 Go 语言中使用模块

模块很重要,因为它们允许将相关的代码文件组织到同一个包中,并以一种提高简单性和可重复性的方式组织代码。 1. 开始使用模块 从代码的角度看,模块是 Go 包和文件以及名为 go.mod 的文件的集合。在接下来的步骤中,将学习如何创建模块,然后使用它。 2. 第一步:创建项目目…

MATLAB绘图基础5:MATLAB数据导入

参考书&#xff1a;《 M A T L A B {\rm MATLAB} MATLAB与学术图表绘制》(关东升)。 5.MATLAB数据导入 5.1 从CSV文件读取数据 C S V {\rm CSV} CSV文件是一种纯文本文件&#xff0c;文件中的数据以逗号为分隔符进行字段分隔&#xff0c;每一行数据代表一条记录&#xff0c;每…

力扣416-分割等和子集(Java详细题解)

题目链接&#xff1a;416. 分割等和子集 - 力扣&#xff08;LeetCode&#xff09; 前情提要&#xff1a; 因为本人最近都来刷dp类的题目所以该题就默认用dp方法来做。 最近刚学完01背包&#xff0c;所以现在的题解都是以01背包问题为基础再来写的。 如果大家不懂01背包的话…

人生苦短我用Python Excel文件基本操作

人生苦短我用Python Excel文件基本操作 前言文件基本操作的模块和类pathlib.Path 类os.stat_result 类time.struct_time 命名元组time 模块shutil 模块 示例查看属性拷贝文件重命名文件查找文件批量操作 测试 前言 本文主要介绍通过Python中的pathlib模块&#xff0c;完成Exce…

【Android面试八股文】你能说说FragmentPagerAdapter 和 FragmentStatePagerAdapter的区别吗?

文章目录 一、FragmentPagerAdapter1.1 工作方式1.2 生命周期1.3 优缺点1.4 适用场景1.5 示例二、FragmentStatePagerAdapter2.1 工作方式2.2 生命周期2.3 优缺点2.4 适用场景2.4 示例三、FragmentPagerAdapter和FragmentStatePagerAdapter关于instantiateItem()方法和destroyI…

【Java中的位运算和逻辑运算详解及其区别】

Java中的位运算和逻辑运算详解及其区别 在 Java 编程中&#xff0c;位运算和逻辑运算是常见的两种操作类型。位运算用于操作整数的二进制位&#xff0c;而逻辑运算则是处理布尔值 (boolean) 的运算。本文将详细讲解这两种运算及其主要区别&#xff0c;并给出相应示例。 应用场…

Docker入门学习-01

Docker 官方文档 1. Docker 基础知识 1.1 什么是 Docker&#xff1f; Docker 是一个开源的平台&#xff0c;用于开发、交付和运行应用程序。它使用容器技术&#xff0c;将应用程序及其依赖打包在一个轻量级的可移植容器中。 1.2 Docker 的主要组件 镜像&#xff08;Image&a…

Django form.save 方法的详细分析

在 Django 中&#xff0c;form.save() 方法是用于将表单中的数据保存到数据库的核心方法。它的功能和实现可以分为几个重要的部分&#xff0c;下面就是我对 form.save() 方法的详细分析&#xff1a; 1、问题背景 在 Django 中&#xff0c;我们经常会使用 Form 来处理用户提交的…

2024 年高教社杯全国大学生数学建模竞赛 C 题 农作物的种植策略(详细思路+matlab代码+python代码+论文范例)

持续更新中,2024年数学建模比赛思路代码论文都会发布到专栏内,只需订阅一次! 完整论文+代码+数据结果链接在文末! 一、第一问 问题描述:假定各种农作物未来的预期销售量、种植成本、亩产量和销售价格相对于 2023 年保持稳定,每季种植的农作物在当季销售。如果某种作物每…

mysql基础知识-锁机制

文章目录 锁类型1. 共享锁&#xff08;Shared Locks, S锁&#xff09;2. 排他锁&#xff08;Exclusive Locks, X锁&#xff09;3. 意向锁&#xff08;Intention Locks&#xff09;4. 记录锁&#xff08;Record Locks&#xff09;5. 间隙锁&#xff08;Gap Locks&#xff09;6. …

SpringBoot和Mybatis框架怎么防止SQL注入

在 Spring Boot 和 MyBatis 中&#xff0c;防止 SQL 注入的主要方法包括&#xff1a; 1.使用 MyBatis 的动态 SQL MyBatis 提供了安全构建 SQL 查询的方式&#xff0c;推荐使用动态 SQL 标签&#xff08;如 <if>、<choose>、<foreach> 等&#xff09;构建查…