速度为单GPU1.6倍,kaggle双GPU(ddp模式)加速pytorch攻略

news2024/9/19 9:05:29

accelerate 是huggingface开源的一个方便将pytorch模型迁移到 GPU/multi-GPUs/TPU/fp16 模式下训练的小巧工具。

和标准的 pytorch 方法相比,使用accelerate 进行多GPU DDP模式/TPU/fp16 训练你的模型变得非常简单(只需要在标准的pytorch训练代码中改动不几行代码就可以适应于cpu/单GPU/多GPU的DDP模式/TPU 等不同的训练环境),而且速度与原生pytorch相当,非常之快。

官方范例:https://github.com/huggingface/accelerate/tree/main/examples

本文将以一个图片分类模型为例,演示在accelerate的帮助下使用pytorch编写一套可以在 cpu/单GPU/多GPU(DDP)模式/TPU 下通用的训练代码。

在我们的演示范例中,在kaggle的双GPU环境下,双GPU的DDP模式是单GPU训练速度的1.6倍,加速效果非常明显。

公众号算法美食屋后台回复关键词:ddp 获取本教程完整jupyter notebook代码和B站视频演示范例。

DP和DDP的区别

  • DP(DataParallel):实现简单但更慢。只能单机多卡使用。GPU分成server节点和worker节点,有负载不均衡。

  • DDP(DistributedDataParallel):更快但实现麻烦。可单机多卡也可多机多卡。各个GPU是平等的,无负载不均衡。

参考文章:《pytorch中的分布式训练之DP VS DDP》https://zhuanlan.zhihu.com/p/356967195

#从git安装最新的accelerate仓库
!pip install git+https://github.com/huggingface/accelerate

一,使用 CPU/单GPU 训练你的pytorch模型

当系统存在GPU时,accelerate 会自动使用GPU训练你的pytorch模型,否则会使用CPU训练模型。

import os,PIL 
import numpy as np
from torch.utils.data import DataLoader, Dataset
import torch 
from torch import nn 

import torchvision 
from torchvision import transforms
import datetime

#======================================================================
# import accelerate
from accelerate import Accelerator
from accelerate.utils import set_seed
#======================================================================


def create_dataloaders(batch_size=64):
    transform = transforms.Compose([transforms.ToTensor()])

    ds_train = torchvision.datasets.MNIST(root="./minist/",train=True,download=True,transform=transform)
    ds_val = torchvision.datasets.MNIST(root="./minist/",train=False,download=True,transform=transform)

    dl_train =  torch.utils.data.DataLoader(ds_train, batch_size=batch_size, shuffle=True,
                                            num_workers=2,drop_last=True)
    dl_val =  torch.utils.data.DataLoader(ds_val, batch_size=batch_size, shuffle=False, 
                                          num_workers=2,drop_last=True)
    return dl_train,dl_val


def create_net():
    net = nn.Sequential()
    net.add_module("conv1",nn.Conv2d(in_channels=1,out_channels=512,kernel_size = 3))
    net.add_module("pool1",nn.MaxPool2d(kernel_size = 2,stride = 2)) 
    net.add_module("conv2",nn.Conv2d(in_channels=512,out_channels=256,kernel_size = 5))
    net.add_module("pool2",nn.MaxPool2d(kernel_size = 2,stride = 2))
    net.add_module("dropout",nn.Dropout2d(p = 0.1))
    net.add_module("adaptive_pool",nn.AdaptiveMaxPool2d((1,1)))
    net.add_module("flatten",nn.Flatten())
    net.add_module("linear1",nn.Linear(256,128))
    net.add_module("relu",nn.ReLU())
    net.add_module("linear2",nn.Linear(128,10))
    return net 



def training_loop(epochs = 5,
                  lr = 1e-3,
                  batch_size= 1024,
                  ckpt_path = "checkpoint.pt",
                  mixed_precision="no", #'fp16'
                 ):
    
    train_dataloader, eval_dataloader = create_dataloaders(batch_size)
    model = create_net()
    

    optimizer = torch.optim.AdamW(params=model.parameters(), lr=lr)
    lr_scheduler = torch.optim.lr_scheduler.OneCycleLR(optimizer=optimizer, max_lr=25*lr, 
                              epochs=epochs, steps_per_epoch=len(train_dataloader))
    
    #======================================================================
    # initialize accelerator and auto move data/model to accelerator.device
    set_seed(42)
    accelerator = Accelerator(mixed_precision=mixed_precision)
    accelerator.print(f'device {str(accelerator.device)} is used!')
    model, optimizer,lr_scheduler, train_dataloader, eval_dataloader = accelerator.prepare(
        model, optimizer,lr_scheduler, train_dataloader, eval_dataloader)
    #======================================================================
    

    for epoch in range(epochs):
        model.train()
        for step, batch in enumerate(train_dataloader):
            features,labels = batch
            preds = model(features)
            loss = nn.CrossEntropyLoss()(preds,labels)
            
            #======================================================================
            #attention here! 
            accelerator.backward(loss) #loss.backward()
            #======================================================================
            
            optimizer.step()
            lr_scheduler.step()
            optimizer.zero_grad()
            
            

        model.eval()
        accurate = 0
        num_elems = 0
        
        for _, batch in enumerate(eval_dataloader):
            features,labels = batch
            with torch.no_grad():
                preds = model(features)
            predictions = preds.argmax(dim=-1)
            
            #======================================================================
            #gather data from multi-gpus (used when in ddp mode)
            predictions = accelerator.gather(predictions)
            labels = accelerator.gather(labels)
            #======================================================================
            
            accurate_preds =  (predictions==labels)
            num_elems += accurate_preds.shape[0]
            accurate += accurate_preds.long().sum()

        eval_metric = accurate.item() / num_elems
        
        #======================================================================
        #print logs and save ckpt  
        accelerator.wait_for_everyone()
        nowtime = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        accelerator.print(f"epoch【{epoch}】@{nowtime} --> eval_metric= {100 * eval_metric:.2f}%")
        unwrapped_net = accelerator.unwrap_model(model)
        accelerator.save(unwrapped_net.state_dict(),ckpt_path+"_"+str(epoch))
        #======================================================================
        
#training_loop(epochs = 5,lr = 1e-3,batch_size= 1024,ckpt_path = "checkpoint.pt",
#             mixed_precision="no") #mixed_precision='fp16'
training_loop(epochs = 5,lr = 1e-4,batch_size= 1024,
              ckpt_path = "checkpoint.pt",
              mixed_precision="no") #mixed_precision='fp16'
device cuda is used!
epoch【0】@2023-01-15 12:06:45 --> eval_metric= 95.20%
epoch【1】@2023-01-15 12:07:01 --> eval_metric= 96.79%
epoch【2】@2023-01-15 12:07:17 --> eval_metric= 98.47%
epoch【3】@2023-01-15 12:07:34 --> eval_metric= 98.78%
epoch【4】@2023-01-15 12:07:51 --> eval_metric= 98.87%

二,使用多GPU DDP模式训练你的pytorch模型

Kaggle中右边settings 中的 ACCELERATOR选择 GPU T4x2。

1,设置config

import os
from accelerate.utils import write_basic_config
write_basic_config() # Write a config file
os._exit(0) # Restart the notebook to reload info from the latest config file
# or answer some question to create a config
#!accelerate config

2,训练代码

与之前代码完全一致。

如果是脚本方式启动,需要将训练代码写入到脚本文件中,如cv_example.py

%%writefile cv_example.py 
import os,PIL 
import numpy as np
from torch.utils.data import DataLoader, Dataset
import torch 
from torch import nn 

import torchvision 
from torchvision import transforms
import datetime

#======================================================================
# import accelerate
from accelerate import Accelerator
from accelerate.utils import set_seed
#======================================================================


def create_dataloaders(batch_size=64):
    transform = transforms.Compose([transforms.ToTensor()])

    ds_train = torchvision.datasets.MNIST(root="./minist/",train=True,download=True,transform=transform)
    ds_val = torchvision.datasets.MNIST(root="./minist/",train=False,download=True,transform=transform)

    dl_train =  torch.utils.data.DataLoader(ds_train, batch_size=batch_size, shuffle=True,
                                            num_workers=2,drop_last=True)
    dl_val =  torch.utils.data.DataLoader(ds_val, batch_size=batch_size, shuffle=False, 
                                          num_workers=2,drop_last=True)
    return dl_train,dl_val


def create_net():
    net = nn.Sequential()
    net.add_module("conv1",nn.Conv2d(in_channels=1,out_channels=512,kernel_size = 3))
    net.add_module("pool1",nn.MaxPool2d(kernel_size = 2,stride = 2)) 
    net.add_module("conv2",nn.Conv2d(in_channels=512,out_channels=256,kernel_size = 5))
    net.add_module("pool2",nn.MaxPool2d(kernel_size = 2,stride = 2))
    net.add_module("dropout",nn.Dropout2d(p = 0.1))
    net.add_module("adaptive_pool",nn.AdaptiveMaxPool2d((1,1)))
    net.add_module("flatten",nn.Flatten())
    net.add_module("linear1",nn.Linear(256,128))
    net.add_module("relu",nn.ReLU())
    net.add_module("linear2",nn.Linear(128,10))
    return net 



def training_loop(epochs = 5,
                  lr = 1e-3,
                  batch_size= 1024,
                  ckpt_path = "checkpoint.pt",
                  mixed_precision="no", #'fp16'
                 ):
    
    train_dataloader, eval_dataloader = create_dataloaders(batch_size)
    model = create_net()
    

    optimizer = torch.optim.AdamW(params=model.parameters(), lr=lr)
    lr_scheduler = torch.optim.lr_scheduler.OneCycleLR(optimizer=optimizer, max_lr=25*lr, 
                              epochs=epochs, steps_per_epoch=len(train_dataloader))
    
    #======================================================================
    # initialize accelerator and auto move data/model to accelerator.device
    set_seed(42)
    accelerator = Accelerator(mixed_precision=mixed_precision)
    accelerator.print(f'device {str(accelerator.device)} is used!')
    model, optimizer,lr_scheduler, train_dataloader, eval_dataloader = accelerator.prepare(
        model, optimizer,lr_scheduler, train_dataloader, eval_dataloader)
    #======================================================================
    

    for epoch in range(epochs):
        model.train()
        for step, batch in enumerate(train_dataloader):
            features,labels = batch
            preds = model(features)
            loss = nn.CrossEntropyLoss()(preds,labels)
            
            #======================================================================
            #attention here! 
            accelerator.backward(loss) #loss.backward()
            #======================================================================
            
            optimizer.step()
            lr_scheduler.step()
            optimizer.zero_grad()
            
            

        model.eval()
        accurate = 0
        num_elems = 0
        
        for _, batch in enumerate(eval_dataloader):
            features,labels = batch
            with torch.no_grad():
                preds = model(features)
            predictions = preds.argmax(dim=-1)
            
            #======================================================================
            #gather data from multi-gpus (used when in ddp mode)
            predictions = accelerator.gather(predictions)
            labels = accelerator.gather(labels)
            #======================================================================
            
            accurate_preds =  (predictions==labels)
            num_elems += accurate_preds.shape[0]
            accurate += accurate_preds.long().sum()

        eval_metric = accurate.item() / num_elems
        
        #======================================================================
        #print logs and save ckpt  
        accelerator.wait_for_everyone()
        nowtime = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        accelerator.print(f"epoch【{epoch}】@{nowtime} --> eval_metric= {100 * eval_metric:.2f}%")
        unwrapped_net = accelerator.unwrap_model(model)
        accelerator.save(unwrapped_net.state_dict(),ckpt_path+"_"+str(epoch))
        #======================================================================
        
training_loop(epochs = 5,lr = 1e-4,batch_size= 1024,ckpt_path = "checkpoint.pt",
            mixed_precision="no") #mixed_precision='fp16'

3,执行代码

方式1,在notebook中启动

from accelerate import notebook_launcher
#args = (5,1e-4,1024,'checkpoint.pt','no')
args = dict(epochs = 5,
        lr = 1e-4,
        batch_size= 1024,
        ckpt_path = "checkpoint.pt",
        mixed_precision="no").values()
notebook_launcher(training_loop, args, num_processes=2)
Launching training on 2 GPUs.
device cuda:0 is used!
epoch【0】@2023-01-15 12:10:48 --> eval_metric= 89.18%
epoch【1】@2023-01-15 12:10:58 --> eval_metric= 97.20%
epoch【2】@2023-01-15 12:11:08 --> eval_metric= 98.03%
epoch【3】@2023-01-15 12:11:19 --> eval_metric= 98.16%
epoch【4】@2023-01-15 12:11:30 --> eval_metric= 98.32%

方式2,accelerate方式执行脚本

!accelerate launch ./cv_example.py

方式3,torch方式执行脚本

# or traditional pytorch style
!python -m torch.distributed.launch --nproc_per_node 2 --use_env ./cv_example.py
device cuda:0 is used!
epoch【0】@2023-01-15 12:18:26 --> eval_metric= 94.79%
epoch【1】@2023-01-15 12:18:37 --> eval_metric= 96.44%
epoch【2】@2023-01-15 12:18:48 --> eval_metric= 98.34%
epoch【3】@2023-01-15 12:18:59 --> eval_metric= 98.41%
epoch【4】@2023-01-15 12:19:10 --> eval_metric= 98.51%

三,使用TPU加速你的pytorch模型

Kaggle中右边settings 中的 ACCELERATOR选择 TPU v3-8。

1,安装torch_xla

#安装torch_xla支持
!pip uninstall -y torch torch_xla 
!pip install torch==1.8.2+cpu -f https://download.pytorch.org/whl/lts/1.8/torch_lts.html
!pip install cloud-tpu-client==0.10 https://storage.googleapis.com/tpu-pytorch/wheels/torch_xla-1.8-cp37-cp37m-linux_x86_64.whl
#从git安装最新的accelerate仓库
!pip install git+https://github.com/huggingface/accelerate
#检查是否成功安装 torch_xla 
import torch_xla

2,训练代码

和之前代码完全一样。

import os,PIL 
import numpy as np
from torch.utils.data import DataLoader, Dataset
import torch 
from torch import nn 

import torchvision 
from torchvision import transforms
import datetime

#======================================================================
# import accelerate
from accelerate import Accelerator
from accelerate.utils import set_seed
#======================================================================


def create_dataloaders(batch_size=64):
    transform = transforms.Compose([transforms.ToTensor()])

    ds_train = torchvision.datasets.MNIST(root="./minist/",train=True,download=True,transform=transform)
    ds_val = torchvision.datasets.MNIST(root="./minist/",train=False,download=True,transform=transform)

    dl_train =  torch.utils.data.DataLoader(ds_train, batch_size=batch_size, shuffle=True,
                                            num_workers=2,drop_last=True)
    dl_val =  torch.utils.data.DataLoader(ds_val, batch_size=batch_size, shuffle=False, 
                                          num_workers=2,drop_last=True)
    return dl_train,dl_val


def create_net():
    net = nn.Sequential()
    net.add_module("conv1",nn.Conv2d(in_channels=1,out_channels=512,kernel_size = 3))
    net.add_module("pool1",nn.MaxPool2d(kernel_size = 2,stride = 2)) 
    net.add_module("conv2",nn.Conv2d(in_channels=512,out_channels=256,kernel_size = 5))
    net.add_module("pool2",nn.MaxPool2d(kernel_size = 2,stride = 2))
    net.add_module("dropout",nn.Dropout2d(p = 0.1))
    net.add_module("adaptive_pool",nn.AdaptiveMaxPool2d((1,1)))
    net.add_module("flatten",nn.Flatten())
    net.add_module("linear1",nn.Linear(256,128))
    net.add_module("relu",nn.ReLU())
    net.add_module("linear2",nn.Linear(128,10))
    return net 



def training_loop(epochs = 5,
                  lr = 1e-3,
                  batch_size= 1024,
                  ckpt_path = "checkpoint.pt",
                  mixed_precision="no", #'fp16'
                 ):
    
    train_dataloader, eval_dataloader = create_dataloaders(batch_size)
    model = create_net()
    

    optimizer = torch.optim.AdamW(params=model.parameters(), lr=lr)
    lr_scheduler = torch.optim.lr_scheduler.OneCycleLR(optimizer=optimizer, max_lr=25*lr, 
                              epochs=epochs, steps_per_epoch=len(train_dataloader))
    
    #======================================================================
    # initialize accelerator and auto move data/model to accelerator.device
    set_seed(42)
    accelerator = Accelerator(mixed_precision=mixed_precision)
    accelerator.print(f'device {str(accelerator.device)} is used!')
    model, optimizer,lr_scheduler, train_dataloader, eval_dataloader = accelerator.prepare(
        model, optimizer,lr_scheduler, train_dataloader, eval_dataloader)
    #======================================================================
    

    for epoch in range(epochs):
        model.train()
        for step, batch in enumerate(train_dataloader):
            features,labels = batch
            preds = model(features)
            loss = nn.CrossEntropyLoss()(preds,labels)
            
            #======================================================================
            #attention here! 
            accelerator.backward(loss) #loss.backward()
            #======================================================================
            
            optimizer.step()
            lr_scheduler.step()
            optimizer.zero_grad()
            
            

        model.eval()
        accurate = 0
        num_elems = 0
        
        for _, batch in enumerate(eval_dataloader):
            features,labels = batch
            with torch.no_grad():
                preds = model(features)
            predictions = preds.argmax(dim=-1)
            
            #======================================================================
            #gather data from multi-gpus (used when in ddp mode)
            predictions = accelerator.gather(predictions)
            labels = accelerator.gather(labels)
            #======================================================================
            
            accurate_preds =  (predictions==labels)
            num_elems += accurate_preds.shape[0]
            accurate += accurate_preds.long().sum()

        eval_metric = accurate.item() / num_elems
        
        #======================================================================
        #print logs and save ckpt  
        accelerator.wait_for_everyone()
        nowtime = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        accelerator.print(f"epoch【{epoch}】@{nowtime} --> eval_metric= {100 * eval_metric:.2f}%")
        unwrapped_net = accelerator.unwrap_model(model)
        accelerator.save(unwrapped_net.state_dict(),ckpt_path+"_"+str(epoch))
        #======================================================================

3,启动训练

from accelerate import notebook_launcher
#args = (5,1e-4,1024,'checkpoint.pt','no')
args = dict(epochs = 5,
        lr = 1e-4,
        batch_size= 1024,
        ckpt_path = "checkpoint.pt",
        mixed_precision="no").values()
notebook_launcher(training_loop, args, num_processes=8)

3c9ce71c2db989b179c0019ec0ed8d28.png

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

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

相关文章

linux基功系列之man帮助命令实战

文章目录前言一、man命令介绍二、常用参数2.1 语法2.2 常用参数2.3 man页面的操作命令man命令使用案例1. 直接查看手册2. -aw 参数找到可以被查询的章节2.3 一次性查阅所有章节2.4 搜索手册页2.5 -L 设置查询语言总结前言 linux系统中的命令数量有上千的,即使是常用…

前端——周总结系列二

1 JS数组排序sort()方法 不传参数排序,默认根据Unicode排序 附录 传参数,使用比较函数,自己定义比较规则 简单数组排序 // 升序 function ascSort(a, b) {return a - b; } // 降序 function ascSort(a, b) {return b - a; }数组对象排序…

算法leetcode|31. 下一个排列(rust重拳出击)

文章目录31. 下一个排列:样例 1:样例 2:样例 3:提示:分析:题解:rustgoccpythonjava31. 下一个排列: 整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。 例如&#xff0…

ROS2机器人编程简述humble-第二章-First Steps with ROS2 .1

ROS2机器人编程简述新书推荐-A Concise Introduction to Robot Programming with ROS2学习笔记流水账-推荐阅读原书。第二章主要就是一些ROS的基本概念,其实ROS1和ROS2的基本概念很多都是类似的。ROS2机器人个人教程博客汇总(2021共6套)如何更…

Linux chgrp 命令

Linux chgrp(英文全拼:change group)命令用于变更文件或目录的所属群组。与 chown 命令不同,chgrp 允许普通用户改变文件所属的组,只要该用户是该组的一员。在 UNIX 系统家族里,文件或目录权限的掌控以拥有…

(一)Jenkins部署、基础配置

目录 1、前言 1.1、Jenkins是什么 1.2、jenkins有什么用 2、 Jenkins安装 2.1、jdk安装 2.2、安装Jenkins 3、Jenkins配置 3.1、解锁Jenkins 3.2、插件安装 3.3、创建管理员 3.4、实例配置 4、汉化 4.1、下载Locale插件 4.2、设置为中文 5、设置中文失效解决步骤 1…

U-Boot 之零 源码文件、启动阶段(TPL、SPL)、FALCON、设备树

最近,工作重心要从裸机开发转移到嵌入式 Linux 系统开发,在之前的博文 Linux 之八 完整嵌入式 Linux 环境、(交叉)编译工具链、CPU 体系架构、嵌入式系统构建工具 中详细介绍了嵌入式 Linux 环境,接下来就是重点学习一…

【Spring6源码・AOP】代理对象的创建

前三篇Spring IOC的源码解析与这一章的AOP是紧密相连的: 【Spring6源码・IOC】BeanDefinition的加载 【Spring6源码・IOC】Bean的实例化 【Spring6源码・IOC】Bean的初始化 - 终结篇 首先介绍我们本章的demo: 一个接口,一个实现&#xf…

【论文速递】ECCV2022 - 开销聚合与四维卷积Swin Transformer_小样本分割

【论文速递】ECCV2022 - 开销聚合与四维卷积Swin Transformer_小样本分割 【论文原文】:Cost Aggregation with 4D Convolutional Swin Transformer for Few-Shot Segmentation 获取地址:https://arxiv.org/pdf/2207.10866.pdf博主关键词: …

紧聚焦涡旋光束app设计-VVB2.0

紧聚焦涡旋光束app设计-VVB2.0前言界面预览功能演示写在最后前言 时隔几个月,花了点时间,将之前用matlab设计的app紧聚焦涡旋光束matlab gui设计进行一次修改,这次发布2.0版本,本次修改的范围主要是将原来的界面进行重做&#xf…

软件设计师中级复习小总结

软件设计师中级复习小总结 计算机与体系结构 K 1024 k 1000 B 字节 b 位 1字节 8位 8bit(位)1Byte(字节) 1024Byte(字节)1KB KB,MB,GB之间的换算关系是:1024KB1MB,1024MB1GB,1024GB1TB K,M&#x…

DevOps 实战概述

一、背景越来越多的团队使用DevOps,个人觉得原因有二,其一市场需求,从瀑布到敏捷的过程能看出市场就是需要团队响应快,小步快跑,风险低效率高,但是敏捷只解决了开发团队的问题并没有解决运维团队的问题&…

16、Javaweb_ajax的JSjQuery实现方式JSON_Java对象互转用户校验案例

AJAX: 1. 概念: ASynchronous JavaScript And XML 异步的JavaScript 和 XML 1. 异步和同步:客户端和服务器端相互通信的基础上 * 客户端必须等待服务器端的响应。在等待的期间客户端不能做其他操作。 * 客户端不需要…

[LeetCode周赛复盘] 第 328 场周赛20230115

[LeetCode周赛复盘] 第 328 场周赛20230115 一、本周周赛总结二、 [Easy] 6291. 数组元素和与数字和的绝对差1. 题目描述2. 思路分析3. 代码实现三、[Medium] 6292. 子矩阵元素加 11. 题目描述2. 思路分析3. 代码实现四、[Medium] 6293. 统计好子数组的数目1. 题目描述2. 思路分…

文献阅读总结--合成生物学工程促进大肠杆菌中莽草酸的高水平积累

题目:Systems engineering of Escherichia coli for high-level shikimate production (ME 2022) 0 前言 本版块内容为记录阅读的文献内容总结经典方法手段。本文内容来自相关文献,在文末做来源进行详细说明对文献中内容不做真实性评价。 1 具体内容 …

标准化和归一化概念澄清与梳理

标准化和归一化是特征缩放(feature scalingscaling)的主要手段,其核心原理可以简单地理解为:让所有元素先减去同一个数,然后再除以另一个数,在数轴上的效果就是:先将数据集整体平移到有某个位置,然后按比例…

【C进阶】动态内存管理

家人们欢迎来到小姜的世界&#xff0c;<<点此>>传送门 这里有详细的关于C/C/Linux等的解析课程&#xff0c;家人们赶紧冲鸭&#xff01;&#xff01;&#xff01; 客官&#xff0c;码字不易&#xff0c;来个三连支持一下吧&#xff01;&#xff01;&#xff01;关注…

Spring 中最常用的 11 个扩展点

目录 1.自定义拦截器 2.获取Spring容器对象 2.1 BeanFactoryAware接口 2.2 ApplicationContextAware接口 3.全局异常处理 4.类型转换器 5.导入配置 5.1 普通类 5.2 配置类 5.3 ImportSelector 5.4 ImportBeanDefinitionRegistrar 6.项目启动时 7.修改BeanDefiniti…

MySQL高级【MVCC原理分析】

1&#xff1a;MVCC1.1&#xff1a;基本概念1). 当前读 读取的是记录的最新版本&#xff0c;读取时还要保证其他并发事务不能修改当前记录&#xff0c;会对读取的记录进行加 锁。对于我们日常的操作&#xff0c;如&#xff1a;select ... lock in share mode(共享锁)&#xff0c…

技术人员和非技术人员如何写出优质博客?-涵子的个人想法

大家好&#xff0c;我是涵子。今天&#xff0c;我们来沉重的聊聊一个大家都很关心的一个问题&#xff1a;技术人员和非技术人员如何写出优质博客&#xff1f; 目录 前言 初写博客&#xff0c;仰望大师 中段时期&#xff0c;无粉无赞 优质博客&#xff0c;涨粉涨赞 优质内容…