makefile 学习(5)完整的makefile模板

news2025/7/17 3:12:18

参考自:

  • (1)深度学习部署笔记(二): g++, makefile语法,makefile自己的CUDA编程模板
  • (2)https://zhuanlan.zhihu.com/p/396448133
  • (3) 一个挺好的工程模板,(https://github.com/shouxieai/cpp-proj-template)

1. c++ 编译流程图

在这里插入图片描述

2. 使用makefile编译工程

  • Makefile是一个经典的构建工具,使用它可以根据一系列规则构建程序。在编写Makefile的过程中,需要了解一些底层的细节,包括链接器编译器等等。Makefile可以被配置成在所有的平台上都能够使用,非常适合轻量级项目和较小的项目
  • CMake则是一种构建工具的高级语言,可以自动生成适合各种平台和编译器的Makefile文件,同时也能生成Visual Studio等IDE所需要的工程文件。CMake使用的是一种高级的、面向目标的语言,可以实现更加复杂的构建任务。CMake的优点在于它可以跨平台地生成Makefile文件,同时具有很好的可读性和可维护性,非常适合大型的、复杂的项目。CMake的缺点在于它的学习曲线相对较陡,需要学习一些新的概念和语法。
  • 总之,Makefile和CMake各自有其优缺点,在实际使用中需要根据项目的规模和需求选择适合的构建工具。

2.1 demo

(1) 一个makefile的demo

  • 新建一个main.cpp文件和一个01kernel.cu文件
#include <iostream>
using namespace std;

int main()
{
  cout << "Hello CUDA" << endl;
  return 0;
}

  • 新建一个Makefile文件
cpp_srcs := $(shell find src -name "*.cpp")
cpp_objs := $(patsubst %.cpp,%.o,$(cpp_srcs))
cpp_objs := $(subst src/,objs/,$(cpp_objs))

debug :
	@echo $(cpp_objs)

  • 终端执行 make debug 如果有问题就是有bug

(2) 逐行解释这个小demo

  • 定义变量 cpp_srcs,它的值是通过调用find命令在 src 目录下查找所有以 .cpp 结尾的文件,并将它们的路径存入 cpp_srcs 变量中。
cpp_srcs := $(shell find src -name "*.cpp")
  • 定义变量 cpp_objs,它的值是将 cpp_srcs 变量中所有 .cpp 后缀的文件名都替换成以.o结尾的目标文件名,并将这些目标文件名保存到 cpp_objs 变量中。
cpp_objs := $(patsubst %.cpp,%.o,$(cpp_srcs))
  • cpp_objs 变量中的所有 src/ 替换为 objs/,并将替换后的值存储回 cpp_objs 变量
cpp_objs := $(subst src/,objs/,$(cpp_objs))
  • 创建一个名为 debug 的伪目标,它的命令是打印 cpp_objs 变量的值,@ 告诉 Make 不要显示命令行上的命令,只显示命令的输出。
debug :
    @echo $(cpp_objs)

2.2 makefile的一个简单模板

# 定义cpp源码路径,并转换为objs目录先的o文件
cpp_srcs := $(shell find src -name "*.cpp")    
cpp_objs := $(patsubst %.cpp,%.o,$(cpp_srcs))
cpp_objs := $(subst src/,objs/,$(cpp_objs))

# 定义cu源码路径,并转换为objs目录先的cuo文件
# 如果cpp文件和cu名字一样,把.o换成.cuo
cu_srcs := $(shell find src -name "*.cu")   # 全部src下的*.cu存入变量cu_srcs
cu_objs := $(patsubst %.cu,%.cuo,$(cu_srcs)) # cu_srcs中全部.cu换成.o
cu_objs := $(subst src/,objs/,$(cu_objs))    # cu_objs src/换成objs/


# 定义名称参数
workspace := workspace 
binary := pro

# makefile中定义cpp的编译方式
# $@:代表规则中的目标文件(生成项)
# $<:代表规则中的第一个依赖文件
# $^:代表规则中的所有依赖文件,以空格分隔
# $?:代表规则中所有比目标文件更新的依赖文件,以空格分隔


# 定义cpp文件的编译方式
# @echo Compile $<     输出正在编译的源文件的名称
objs/%.o : src/%.cpp
	@mkdir -p $(dir $@)
	@echo Compile $<        
	@g++ -c $^ -o $@ 

# 定义.cu文件的编译方式
objs/%.cuo : src/%.cu
	@mkdir -p $(dir $@)
	@echo Compile $< 
	@nvcc -c $^ -o $@

# 定义workspace/pro文件的编译
$(workspace)/$(binary) : $(cpp_objs) $(cu_objs)
	@mkdir -p $(dir $@)
	@echo Link $^
	@g++ $^ -o $@

# 定义pro快捷编译指令,这里只发生编译,不执行
# 快捷指令就是make pro
pro : $(workspace)/$(binary)

# 定义指令并且执行的指令,并且执行目录切换到workspace下
run : pro
	@cd $(workspace) && ./$(binary)
	
debug :
	@echo $(cpp_objs)
	@echo $(cu_objs)

这里报错了,因为缺少了cuda的库文件,下面是查看自己cuda版本和找到在哪里的指令,以我的CUDA11.7为例, 去到目录下看看有什么库文件,我们当前需要一个cudaruntime的头文件

nvcc --version
whereis cuda-11.7
cuda-11: /usr/local/cuda-11.7
cd /usr/local/cuda-11.7/lib64 

2.3 完整学习整个makefile文件

来看看最后要完成的工程目录

project/
  - src/
      - main.cpp
      - 01.kernel.cu
  - objs/
  - workspace/
  - Makefile

(1) 定义源码路径

  • 这里定义源码路径的作用是为了在后续的代码中可以方便地引用源码文件,并将其编译成目标文件。具体地,使用find命令查找src目录下的所有扩展名为.cpp.cu的文件,然后将它们分别存储到cpp_srcscu_srcs变量中。接着,使用patsubstsubst函数将源码文件路径中的src/替换成objs/,并将它们分别存储到cpp_objs和cu_objs变量中,这样就得到了在objs目录下存储的目标文件列表。在后面的编译和链接过程中,这些目标文件(*.o, *.cuo)会被用来生成最终的可执行文件。
    - objs 目录中放置的是编译生成的目标文件,对于这个 makefile 来说,所有的 .o.cuo文件都会被放到 objs 目录下。在链接生成可执行文件时,makefile 会从这些目标文件中找到需要的文件进行链接
# 定义cpp源码路径,并转换为objs目录先的o文件
cpp_srcs := $(shell find src -name "*.cpp")    
cpp_objs := $(patsubst %.cpp,%.o,$(cpp_srcs))
cpp_objs := $(subst src/,objs/,$(cpp_objs))

# 定义cu源码路径,并转换为objs目录先的cuo文件
# 如果cpp文件和cu名字一样,把.o换成.cuo
cu_srcs := $(shell find src -name "*.cu")    # 全部src下的*.cu存入变量cu_srcs
cu_objs := $(patsubst %.cu,%.cuo,$(cu_srcs)) # cu_srcs中全部.cu换成.o
cu_objs := $(subst src/,objs/,$(cu_objs))    # cu_objs src/换成objs/

(2) 定义可执行文件及存放位置

这里定义的名称参数 workspace 和 binary 是用来指定工作空间和生成的可执行文件名称的。在这个 Makefile 中,workspace 表示的是工作空间的目录名称,binary 表示生成的可执行文件的名称。这些参数可以在 Makefile 中的其它规则中使用,例如在链接时指定目标文件路径和生成的可执行文件名称等。

workspace := workspace
binary := pro

(3) 定义头文件,库文件和链接目标

  • 因为编译cuda环境需要用到libcudart.so这个库,cudart 是 CUDA runtime库,CUDA编程必须用到的
  • 先整个定义了,后面用foreach一次性加进来
  • 每个人的cuda的安装位置不一样,可以用whereis cuda-版本查找位置
  • 编译的过程中需要查找头文件库文件include_path, library_path告诉编译器去哪里找头文件,库文件
  • link_librarys是告诉链接器需要链接那些库文件
# 定义头文件库文件和链接目标,后面用foreach一次性增加
include_paths := /usr/local/cuda-11.7/include
library_paths := /usr/local/cuda-11.7/lib64
link_librarys := cudart

(4) 定义编译选项

# 定义编译选项
cpp_compile_flags := -m64 -fPIC -g -O0 -std=c++11
cu_compile_flags := -m64 -g -O0 -std=c++11
  • -m64:表示编译器生成的代码是 64 位的。
  • -fPIC:表示编译器要生成位置独立的代码。
  • -g:表示编译器会在生成的目标文件中加入调试信息,方便进行调试。
  • -O0:表示关闭优化。
  • -std=c++11:表示采用 C++11 标准进行编译。

(5)合并选项

  • -L 指定链接时查找的目录
  • -l 指定链接的目标名称,符合libname.so -lname 规则
  • -I 指定编译时头文件查找目录
  • run path 链接的时查找动态链接库文件的路径,让程序运行的时候,自动查找并加载动态链接库
rpath         := $(foreach item,$(link_librarys),-Wl,-rpath=$(item))
include_paths := $(foreach item,$(include_paths),-I$(item))
library_paths := $(foreach item,$(library_paths),-L$(item))
link_librarys := $(foreach item,$(link_librarys),-l$(item))

(6) 把合并后的选项给到编译器选项

  • cpp_compile_flags += $(include_paths): 将include_paths添加到cpp_compile_flags中,用于在编译C++源代码时指定头文件搜索路径。
  • cu_compile_flags += $(include_paths): 将include_paths添加到cu_compile_flags中,用于在编译CUDA源代码时指定头文件搜索路径。
  • link_flags := $(rpath) $(library_paths) $(link_librarys): 将rpath、library_pathslink_librarys合并成一个链接选项link_flags。rpath指定运行时库搜索路径,library_paths指定链接库搜索路径,link_librarys指定要链接的库文件名。
cpp_compile_flags += $(include_paths)
cu_compile_flags  += $(include_paths)
link_flags        := $(rpath) $(library_paths) $(link_librarys)

(7) 定义cpp cuda编译方式

  • .cpp .cu源文件 编译成目标文件 .o .cuo,放在objs里面
  • .cpp, .cu源文件是依赖项,生成目标文件.o .cuo
# 定义cpp文件的编译方式
# @echo Compile $<     输出正在编译的源文件的名称
objs/%.o : src/%.cpp
	@mkdir -p $(dir $@)
	@echo Compile $<        
	@g++ -c $^ -o $@ $(cpp_compile_flags)

# 定义.cu文件的编译方式
objs/%.cuo : src/%.cu
	@mkdir -p $(dir $@)
	@echo Compile $< 
	@nvcc -c $^ -o $@ $(cu_compile_flags)

(8) 在workspace下编译出可执行文件

  • 上面.cpp, .cu文件编译出来的结果在这里就是依赖项
  • -L./objs 表示告诉链接器在当前目录下寻找库文件,./objs 是指定的路径。实际上,./objs 是目标文件存储的路径,不是库文件存储的路径,这里写的是 -L./objs 只是为了指定链接时查找目录的路径。
# 定义workspace/pro文件的编译
$(workspace)/$(binary) : $(cpp_objs) $(cu_objs)
	@mkdir -p $(dir $@)
	@echo Link $^
	@g++ $^ -o $@ $(link_flags) -L./objs

(9)定义伪标签, 作为指令

  • 上面编译的可执行文件,是指令依赖项
  • 执行可执行文件
# 定义pro快捷编译指令,这里只发生编译,不执行
# 快捷指令就是make pro
pro : $(workspace)/$(binary)

# 定义指令并且执行的指令,并且执行目录切换到workspace下
run : pro
	@cd $(workspace) && ./$(binary)
	
debug :
	@echo $(cpp_objs)
	@echo $(cu_objs)

clean : 
	@rm -rf objs $(workspace)/$(binary)

# 指定伪标签,作为指令
.PHONY : clean debug run pro
  • (10) 完整的makefile文件
# 定义cpp源码路径,并转换为objs目录先的o文件
cpp_srcs := $(shell find src -name "*.cpp")    
cpp_objs := $(patsubst %.cpp,%.o,$(cpp_srcs))
cpp_objs := $(subst src/,objs/,$(cpp_objs))

# 定义cu源码路径,并转换为objs目录先的cuo文件
# 如果cpp文件和cu名字一样,把.o换成.cuo
cu_srcs := $(shell find src -name "*.cu")    # 全部src下的*.cu存入变量cu_srcs
cu_objs := $(patsubst %.cu,%.cuo,$(cu_srcs)) # cu_srcs中全部.cu换成.o
cu_objs := $(subst src/,objs/,$(cu_objs))    # cu_objs src/换成objs/


# 定义可执行文件存放目录以及可执行文件名
workspace := workspace
binary := pro

# 定义头文件库文件和链接目标,后面用foreach一次性增加
include_paths := /usr/local/cuda-11.7/include
library_paths := /usr/local/cuda-11.7/lib64
link_librarys := cudart

# 定义编译选项
cpp_compile_flags := -m64 -fPIC -g -O0 -std=c++11
cu_compile_flags := -m64 -g -O0 -std=c++11

# 对头文件, 库文件,目标统一增加 -I,-L-l
rpath         := $(foreach item,$(link_librarys),-Wl,-rpath=$(item))
include_paths := $(foreach item,$(include_paths),-I$(item))
library_paths := $(foreach item,$(library_paths),-L$(item))
link_librarys := $(foreach item,$(link_librarys),-l$(item))

# 合并选项
# 合并完选项后就可以给到编译方式里面去了
cpp_compile_flags += $(include_paths)
cu_compile_flags  += $(include_paths)
link_flags        := $(rpath) $(library_paths) $(link_librarys)


# makefile中定义cpp的编译方式
# $@:代表规则中的目标文件(生成项)
# $<:代表规则中的第一个依赖文件
# $^:代表规则中的所有依赖文件,以空格分隔
# $?:代表规则中所有比目标文件更新的依赖文件,以空格分隔


# 定义cpp文件的编译方式
# @echo Compile $<     输出正在编译的源文件的名称
objs/%.o : src/%.cpp
	@mkdir -p $(dir $@)
	@echo Compile $<        
	@g++ -c $^ -o $@ $(cpp_compile_flags)

# 定义.cu文件的编译方式
objs/%.cuo : src/%.cu
	@mkdir -p $(dir $@)
	@echo Compile $< 
	@nvcc -c $^ -o $@ $(cu_compile_flags)

# 定义workspace/pro文件的编译
$(workspace)/$(binary) : $(cpp_objs) $(cu_objs)
	@mkdir -p $(dir $@)
	@echo Link $^
	@g++ $^ -o $@ $(link_flags) -L./objs


# 定义pro快捷编译指令,这里只发生编译,不执行
# 快捷指令就是make pro
pro : $(workspace)/$(binary)

# 定义指令并且执行的指令,并且执行目录切换到workspace下
run : pro
	@cd $(workspace) && ./$(binary)
	
debug :
	@echo $(cpp_objs)
	@echo $(cu_objs)

clean : 
	@rm -rf objs $(workspace)/$(binary)

# 指定伪标签,作为指令
.PHONY : clean debug run pro

3. 推荐一个makefile工程

超全面和详细的一个makefil工程,值得学习借鉴

cc        := g++
name      := pro
workdir   := workspace
srcdir    := src
objdir    := objs
stdcpp    := c++11
cuda_home := /home/yuanwushui/anaconda3/lib/python3.9/site-packages/trtpy/trt8cuda112cudnn8
syslib    := /home/yuanwushui/anaconda3/lib/python3.9/site-packages/trtpy/lib
cpp_pkg   := /home/yuanwushui/anaconda3/lib/python3.9/site-packages/trtpy/cpp-packages  #opencv4.2
cuda_arch := 
nvcc      := $(cuda_home)/bin/nvcc -ccbin=$(cc)

# 定义cpp的路径查找和依赖项mk文件
cpp_srcs := $(shell find $(srcdir) -name "*.cpp")
cpp_objs := $(cpp_srcs:.cpp=.cpp.o)
cpp_objs := $(cpp_objs:$(srcdir)/%=$(objdir)/%)
cpp_mk   := $(cpp_objs:.cpp.o=.cpp.mk)

# 定义cu文件的路径查找和依赖项mk文件
cu_srcs := $(shell find $(srcdir) -name "*.cu")
cu_objs := $(cu_srcs:.cu=.cu.o)
cu_objs := $(cu_objs:$(srcdir)/%=$(objdir)/%)
cu_mk   := $(cu_objs:.cu.o=.cu.mk)

# 定义opencv和cuda需要用到的库文件
link_cuda      := cudart cudnn
link_trtpro    := 
link_tensorRT  := nvinfer nvinfer_plugin
link_opencv    := opencv_core opencv_imgproc opencv_imgcodecs opencv_videoio opencv_video opencv_highgui
link_sys       := stdc++ dl protobuf
link_librarys  := $(link_cuda) $(link_tensorRT) $(link_sys) $(link_opencv)

# 定义头文件路径,请注意斜杠后边不能有空格
# 只需要写路径,不需要写-I
include_paths := src              \
    $(cuda_home)/include/cuda     \
	$(cuda_home)/include/tensorRT \
	$(cpp_pkg)/opencv4.2/include  \
	$(cuda_home)/include/protobuf \
	include

# 定义库文件路径,只需要写路径,不需要写-L
library_paths := $(cuda_home)/lib64 $(syslib) $(cpp_pkg)/opencv4.2/lib

# 把library path给拼接为一个字符串,例如a b c => a:b:c
# 然后使得LD_LIBRARY_PATH=a:b:c
empty := 
library_path_export := $(subst $(empty) $(empty),:,$(library_paths))

# 把库路径和头文件路径拼接起来成一个,批量自动加-I、-L、-l
run_paths     := $(foreach item,$(library_paths),-Wl,-rpath=$(item))
include_paths := $(foreach item,$(include_paths),-I$(item))
library_paths := $(foreach item,$(library_paths),-L$(item))
link_librarys := $(foreach item,$(link_librarys),-l$(item))

# 如果是其他显卡,请修改-gencode=arch=compute_75,code=sm_75为对应显卡的能力
# 显卡对应的号码参考这里:https://developer.nvidia.com/zh-cn/cuda-gpus#compute
# 如果是 jetson nano,提示找不到-m64指令,请删掉 -m64选项。不影响结果
cpp_compile_flags := -std=$(stdcpp) -w -g -O0 -m64 -fPIC -fopenmp -pthread
cu_compile_flags  := -std=$(stdcpp) -w -g -O0 -m64 $(cuda_arch) -Xcompiler "$(cpp_compile_flags)"
link_flags        := -pthread -fopenmp -Wl,-rpath='$$ORIGIN'

cpp_compile_flags += $(include_paths)
cu_compile_flags  += $(include_paths)
link_flags        += $(library_paths) $(link_librarys) $(run_paths)

# 如果头文件修改了,这里的指令可以让他自动编译依赖的cpp或者cu文件
ifneq ($(MAKECMDGOALS), clean)
-include $(cpp_mk) $(cu_mk)
endif

$(name)   : $(workdir)/$(name)

all       : $(name)
run       : $(name)
	@cd $(workdir) && ./$(name) $(run_args)

$(workdir)/$(name) : $(cpp_objs) $(cu_objs)
	@echo Link $@
	@mkdir -p $(dir $@)
	@$(cc) $^ -o $@ $(link_flags)

$(objdir)/%.cpp.o : $(srcdir)/%.cpp
	@echo Compile CXX $<
	@mkdir -p $(dir $@)
	@$(cc) -c $< -o $@ $(cpp_compile_flags)

$(objdir)/%.cu.o : $(srcdir)/%.cu
	@echo Compile CUDA $<
	@mkdir -p $(dir $@)
	@$(nvcc) -c $< -o $@ $(cu_compile_flags)

# 编译cpp依赖项,生成mk文件
$(objdir)/%.cpp.mk : $(srcdir)/%.cpp
	@echo Compile depends C++ $<
	@mkdir -p $(dir $@)
	@$(cc) -M $< -MF $@ -MT $(@:.cpp.mk=.cpp.o) $(cpp_compile_flags)
    
# 编译cu文件的依赖项,生成cumk文件
$(objdir)/%.cu.mk : $(srcdir)/%.cu
	@echo Compile depends CUDA $<
	@mkdir -p $(dir $@)
	@$(nvcc) -M $< -MF $@ -MT $(@:.cu.mk=.cu.o) $(cu_compile_flags)

# 定义清理指令
clean :
	@rm -rf $(objdir) $(workdir)/$(name) $(workdir)/*.trtmodel $(workdir)/*.onnx 
	@rm -rf $(workdir)/image-draw.jpg $(workdir)/input-image.jpg $(workdir)/pytorch.jpg

# 防止符号被当做文件
.PHONY : clean run $(name)

# 导出依赖库路径,使得能够运行起来
export LD_LIBRARY_PATH:=$(library_path_export)

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

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

相关文章

linux嵌入式时区问题

目录 操作说明实验参考 最近有个针对时区的需求&#xff0c;研究了下。 查询网上的一些设置&#xff0c;发现基本都是系统中自带的一些文件&#xff0c;然后开机时解析&#xff0c;或者是有个修改的命令。 操作 但针对嵌入式常用到的 busybox 制作的最小系统&#xff0c;并没…

图论|知识图谱——详解自下而上构建知识图谱全过程

导读&#xff1a;知识图谱的构建技术主要有自顶向下和自底向上两种。其中自顶向下构建是指借助百科类网站等结构化数据源&#xff0c;从高质量数据中提取本体和模式信息&#xff0c;加入到知识库里。而自底向上构建&#xff0c;则是借助一定的技术手段&#xff0c;从公开采集的…

activiti流程回退与跳转

学习连接 【工作流Activiti7】3、Activiti7 回退与会签 【工作流Activiti7】4、Activiti7 结束/终止流程 Activiti-跳转到指定节点、回退 ativiti6.0 流程节点自由跳转实现、拒绝/不同意/返回上一节点、流程撤回、跳转、回退等操作&#xff08;通用实现&#xff0c;亲测可用…

基于python+TensorFlow+Django算法模型的车辆车型识别系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介简介技术栈主要模块1. 数据预处理2. 模型构建3. 模型训练4. 模型集成5. 用户界面 系统工作流程未来改进计划 二、功能三、系统四. 总结 一项目简介 # 车辆车…

【linux】基本指令(中篇)

echo指令 将引号内容打印到显示屏上 输出的重定向 追加的重定向 输出的重定向 我们学习c语言的时候当以写的方式创建一个文件&#xff0c;就会覆盖掉该文件之前的内容 当我们以追加的方式打开文件的时候&#xff0c;原文件内容不会被覆盖而是追加 more指令 10.more指令…

cephadm部署ceph quincy版本

环境说明 IP主机名角色 存储设备 192.168.2.100 master100 mon,mgr,osd,mds,rgw 大于5G的空设备192.168.2.101node101mon,mgr,osd,mds,rgw大于5G的空设备192.168.2.102node102mon,mgr,osd,mds,rgw大于5G的空设备 关闭防火墙 关闭并且禁用selinux 配置主机名/etc/hosts …

⑦【Redis GEO 】Redis常用数据类型:GEO [使用手册]

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ Redis GEO ⑦Redis GEO 基本操作命令1.geoadd …

原生JS实现计算器(内含源码)

前言 本文讲解了JavaScript如何在一小时内实现一个简易计算器&#xff0c;这里最大的亮点就在于&#xff0c;我在JS中只用了一个事件&#xff0c;就实现了计算器的效果和功能&#xff0c;那么好文本正式开始。 布局和样式流程 首先是HTMLCSS结构&#xff1a;这里主要用到的…

基于uQRCode封装的Vue3二维码生成插件

标题&#xff1a;基于uQRCode封装的Vue3二维码生成插件 摘要&#xff1a;本文介绍了一种基于uQRCode封装的Vue3二维码生成插件&#xff0c;可以在Javascript运行环境下生成二维码并返回图片地址。该插件适用于所有Javascript运行环境&#xff0c;并且支持微信小程序。本文将详…

在Python中matplotlib函数的plt.plot()函数的颜色参数设置,以及可以直接运行的程序代码!

文章目录 前言一、使用字符串颜色&#xff1a;二、使用十六进制颜色&#xff1a;三、使用RGB元组&#xff1a;四、使用颜色映射&#xff1a;总结 前言 在matplotlib中&#xff0c;plt.plot()函数可以接受颜色参数&#xff0c;可以设置为字符串颜色&#xff08;如red&#xff0…

笔记:pycharm当有多个plt.show()时候,只显示第一个plt.show()

import matplotlib.pyplot as plt import numpy as np# 创建数据 x np.linspace(0, 10, 100) y1 np.sin(x) y2 np.cos(x) y3 np.tan(x) y4 np.exp(x)# 创建一个2x2的子图网格 # fig plt.figure() fig,((ax1, ax2), (ax3, ax4)) plt.subplots(nrows2, ncols2, figsize(8,…

Redis:事务操作

目录 Redis事务定义相关命令事务的错误处事务冲突的问题Redis事务三特性 Redis事务定义 redis事务是一个单独的隔离操作&#xff0c;事务中的所有命令都会序列化、按顺序地执行&#xff0c;事务在执行的过程中&#xff0c;不会被其他客户端发送来的命令请求所打断。 redis事务…

车载电子电器架构 ——电子电气架构设计方案概述

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 注:本文1万多字,认证码字,认真看!!! 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证…

作为Java初学者,如何快速学好Java?

作为Java初学者&#xff0c;如何快速学好Java&#xff1f; 开始的一些话 对于初学者来说&#xff0c;编程的学习曲线可能相对陡峭。这是正常现象&#xff0c;不要感到沮丧。逐步学习&#xff0c;循序渐进。 编程是一门实践性的技能&#xff0c;多写代码是提高的唯一途径。尽量…

【一起来学kubernetes】7、k8s中的ingress详解

引言配置示例负载均衡的实现负载均衡策略实现模式实现方案Nginx类型Ingress实现Treafik类型Ingress实现HAProxy类型ingress实现Istio类型ingress实现APISIX类型ingress实现 更多 引言 Ingress是Kubernetes集群中的一种资源类型&#xff0c;用于实现用域名的方式访问Kubernetes…

[图片来源BZhan]最小生成树(Prim➕Kruskal)、最短路径(Dijkstra➕Floyd)

文章目录 0.基础知识0.1图的存储结构0.2算法复杂度1.BFS和DFS2.Prim和Kruskal 1.最小生成树1.1Prim算法1.算法思想2.Prim代码实现 1.2Kruskal算法1.算法思想2.Kruskal代码实现[demo] 2.最短路径2.1问题抽象:2.2两种常见的最短路径问题:1.Dijkstra: 单源最短路径O(N^2)2.Floyd: …

停车管理系统

1 用户信息管理 2 车位信息管理 3 车位费用设置 4 停泊车辆查询 5 车辆进出管理 6 用户个人中心 7 预定停车位 8 缴费信息 9 业务逻辑详解 1 用户停车&#xff1a;user用户登录&#xff0c;在预定停车位菜单&#xff0c;选择一个车位点击预定即可 2 车辆驶出&#xff1a;admin…

【数据结构实验】图(二)将邻接矩阵存储转换为邻接表存储

文章目录 1. 引言2. 邻接表表示图的原理2.1 有向权图2.2 无向权图2.3 无向非权图2.1 有向非权图 3. 实验内容3.1 实验题目&#xff08;一&#xff09;数据结构要求&#xff08;二&#xff09;输入要求&#xff08;三&#xff09;输出要求 3.2 算法实现 4. 实验结果 1. 引言 图是…

电磁场信息论及先进MIMO (黄大年茶思屋座谈) 笔记

天线阵的负载动态调控&#xff0c;动态阻抗匹配网络&#xff0c;实时跟着扫描角度的变化而变化&#xff0c;可能突破Hannan极限。 新的天线构架&#xff1a; 周期 —》非周期 每个单元不一样 动态可调&#xff0c;可重构 每个天线多端口或多模式 多层天线 非周期结构天线的增…

paho mqtt的keepAliveInterval

一、keepAliveInterval 所用的版本为1.3.12 实验一、 这个值设置的30&#xff0c;打开mqtt的trace&#xff0c;发现每隔33s发送一次pingreq note&#xff1a; 期间&#xff0c;client和server一直保持qos0的消息交互&#xff08;client->server&#xff09; 实验二、 …