点击蓝字
关注我们
AI TIME欢迎每一位AI爱好者的加入!
图神经网络(GNN)是一类专门针对图结构数据的神经网络模型,在社交网络分析、知识图谱等领域中取得了不错的效果。近来,相关研究人员在GNN的可解释性、架构搜索、对比学习等方面做了很多探究。
本周精选了10篇GNN领域的优秀论文,来自剑桥、港大、华中科大等机构。
为了方便大家阅读,只列出了论文标题、作者、链接等信息,如果感兴趣可点击“论文详情页”,PC端数据同步(收藏即可在PC端查看),每日新论文也可登录小程序查看。
1. AirGNN: Graph Neural Network over the Air论文详情页
作者:Zhan Gao,Deniz Gunduz
AI华同学综述(大模型驱动):图神经网络是信息处理架构,这些模型从网络数据中表示,并允许通过局部通讯实现。现有的GNN架构有时假设理想的无线通信路线忽略了通道效应,如振动和噪音,导致在实际应用程序中性能恶化。本文提出了一种新的图神经网络AirGNN,是一种新的GNN架构,该架构将通信模型融入到架构中,包括对无线传输图的转换。空对数变换改变了随机交互图上的图变换操作,以考虑受邻居特征收集时频率变异和噪声,从而提高了桥梁的可扩展性。我们提出了一种基于随机梯度下降的方法来训练AirGNN,并表明培训过程收敛到了固定解上。
2. LightGCL: Simple Yet Effective Graph Contrastive Learning for Recommendation论文详情页
作者:Xuheng Cai,Chao Huang,Lianghao Xia,Xubin Ren
AI华同学综述(大模型驱动):在本文中,我们提出了一种简单有效的图对比学习范式LightGCL,该模型只使用单一的奇异值分解来进行特征对比增强。实验表明,该模型对数据稀疏性和流行倾向的影响非常鲁棒。此外,进行了大量的分析,以证明该模型优于基线系统,并且能够有效地抵消了噪声扰动。
3. Self-supervised Guided Hypergraph Feature Propagation for Semi-supervised Classification with Missing Node Features论文详情页
作者:Chengxiang Lei,Sichao Fu,Yuetian Wang,Wenhao Qiu,Yachen Hu,Qinmu Peng,Xinge You
AI华同学综述(大模型驱动):图神经网络中缺失的节点特征最近受到了越来越大的关注,这些缺失的节点特征严重损害了现有GNN的性能。一些最近的方法已经提出来重新构造缺失的节点特征。然而,如何有效地利用与节点的复杂数据相关性来重建缺失的节点特征仍然是一个很大的挑战。为了解决以上问题,我们提出了一种自我监督的引导高分辨率超图特征传播(SGHFP)。具体来说,根据缺失信息出现的节点特征,首先为每个节点生成特征hypergraph,然后将这些特征映射到两个层的双层GNN构建伪标签。在每一迭代中,通过交换形成的特征和先前迭代的复制特征有效地结合在一起,以更好地保留更高序列的数据相关性。
4. Robust Mid-Pass Filtering Graph Convolutional Networks论文详情页
作者:Jincheng Huang,Lun Du,Xu Chen,Qiang Fu,Shi Han,Dongmei Zhang
[toc]
1、String#split 可以根据给定的分隔符或正则表达式将一个字符串分割成多个部分 // 使用正则表达式 "(?<=\\G.{" + n + "})"来分割字符串,其中表达式中的 n 表示字符的长度。 public static List<String> usingSplitMethod(String text, int n) { String[] results = text.split("(?<=\\G.{" + n + "})"); return Arrays.asList(results); } 2、String#substring 一般情况我们都是用于截取字符串使用的,这里我们也是可以用来处理字符串的分割,只要循环就行 public static List<String> usingSubstringMethod(String text, int n) { List<String> results = new ArrayList<>(); int length = text.length(); for (int i = 0; i < length; i += n) { results.add(text.substring(i, Math.min(length, i + n))); } return results; } 3、Pattern类 Pattern 类通常来说,我们是用于处理正则表达式,做一些match使用,正如第一种 String#split 方法所见,正则表达式也可以用于分割字符串 // 我们使用 .
error C2001: 常量中有换行符 error乱码的本质原因vscode解决方法手动调整编码设置默认编码 vs2019解决方法安装UTF-8插件设置编码 error 我在vs2019中写一样的代码,显示乱码
原因就是输出的中文导致的了。。
乱码的本质原因 是因为中文主要有两套编码,一套是GBK,一套是UTF-8,代码编辑器一般默认用UTF-8,而在输出的终端中默认是GBK,所以在UTF-8下的中文输出到GBK的终端自然会乱码。
(VSCode打开文件后默认的编码格式是UTF-8,而cmd默认的编码格式是gbk,不同的编码格式就导致我们程序在运行时在cmd中出现中文乱码。)
cpp文件中的中文乱码控制台打印的中文乱码 因此:给编译器加一条指令,让编译器执行时把UTF-8转成GBK给终端,就能解决问题了!
vscode解决方法 按ctrl+shift+p打开控制面板,搜索C/C++:编辑配置(JSON),按enter进入,会打开一个叫c_cpp_properties的json配置文件,按图中操作即可。
添加如下代码(前面的逗号不要落)
"compilerArgs": [ "-fexec-charset=GBK" ] 手动调整编码 然后,点击右下角的编码,选择编码
通过编码保存–>GBK
成果解决
需要提醒的是,这种方法必须在你写代码之前就要改好编码格式,如果是用UTF-8写完了才发现没改格式的话,还是需要删除掉中文重新写的!
建议设置编码是在空文件(写代码前设置,可以省去很大不必要的麻烦)
过了一段时间,再次打开cpp文件
代码乱码了
选择gbk就行。
设置默认编码 文件,首选项,设置,编码
此时,新建立的cpp文件都是GBK编码了
查看控制台:(GBK编码)
vs2019解决方法 查看控制台(utf-8编码。。。)
安装UTF-8插件 (其实不用安装这个。。)
安装UTF-8插件。
打开vs2019
点击end tasks
禁用这个插件
如果中文编码,不用强制utf-8,不然会出问题。。。
设置编码 VS 2019隐藏了高级保存功能,导致没办法直接去设置代码编码
但我们可以通过设置,调出这个命令。
1点开工具选项卡中的自定义
2将菜单栏修改成文件,如图(这里是选择我们添加命令的位置)
3 点击添加命令
在文件中,找到高级保存选项,确定
现在,在导航栏的文件中,就可以找到这个命令了
点击高级保存选项:
点击此命令
完美解决
常用命令 1、目录操作 cd 切换目录
cd / 切换到根目录
cd ~ 回到个人用户的主目录
ls 查看当前目录下所有文件的详细信息 list的意思
ll 查看当前目录下所有文件的详细信息
pwd 显示当前目录的全路径 . 当前目录
.. 上级目录
2、文件操作 cp 复制
mv 重命名、剪切移动位置
rm 删除 加上 -rf 就不会出现提示直接删除
mkdir test 在当前目录下创建一个test文件夹
touch a.txt 在当前目录下创建一个文件a.txt
./文件名 执行文件(切换到那个目录然后执行)
3、文件编辑 vi a.txt 编辑,文件编辑器
进入后只能查看,按i进入编辑模式,可以移动光标进行文本编辑操作
按esc退出编辑模式
输入:wq 保存并退出(w代表保存,q代表退出)
输入/abc 回车 搜索文件中包含abc的内存
4、文件查看 cat a.txt 一次性读取并打印a.txt文件里的所有信息
more a.txt 读取a.txt文件,每次读取一屏幕,按空格键翻页
tail -200 a.txt 读取后面两百行(看日志排查错误的时候,后面是最新的)tail是尾巴的意思
tail -f a.txt 实时读取这个文件(监控日志用)
head -10 a.txt 从前往后读10行
以下步骤亲测有效无误!!!!!
一、 基础环境准备 1)软件清单
Centos7(64位)服务器3台,地址如下:
jdk1.8
Mysql5.7 (必须要5.7的版本!!)
mysql-connector-java-5.1.42-bin.jar
CDH包:https://ro-bucharest-repo.bigstepcloud.com/cloudera-repos/cdh5/parcels/5.15.2/
CM包:https://ro-bucharest-repo.bigstepcloud.com/cloudera-repos/cm5/redhat/7/x86_64/cm/5.15.2/RPMS/x86_64/
2)服务器地址
192.168.56.1 (此地址映射你自己的服务器地址)
192.168.56.2 (此地址映射你自己的服务器地址)
192.168.56.3 (此地址映射你自己的服务器地址)
3)修改hostname
vim /etc/hostname 修改主机名为cdh*后进行以下操作
service network restart 4)配置hosts
vim /etc/hosts 192.168.56.1 cdh1 192.168.56.2 cdh2 192.168.56.3 cdh3 5)免密访问
ssh-keygen -t rsa //三台服务器全都执行以下命令 ssh-copy-id root@cdh1 ssh-copy-id root@cdh2 ssh-copy-id root@cdh1 6)关闭防火墙
systemctl stop firewalld systemctl disable firewalld 7)关闭SELINUX
setenforce 0 vim /etc/sysconfig/selinux SELINUX=disabled 8)配置NTP
yum -y install ntp yum install nfs-utils rpcbind systemctl start rpcbind.
达梦数据库安装详解 首先创建一个专门的用户 完成后创建安装目录,修改权限 修改限制,按下图改成65536 挂载
mount -o loop /opt/dm8_20230104_x86_rh6_64.iso /mnt
切换用户(dmdba )安装(图形化安装)
一直下一步,目录选择刚刚的dm8
回到root用户,执行脚本 回到dmdba用户,点击确定、完成、初始化 一直下一步
创建示例库 也可以不装
根据提示在root用户下执行命令
修改下环境
刷新
8.然后就可以登录了
格式: disql 用户名/密码 用户名和密码在创建时候有的,我使用的都是默认的
/* 第1个参数为I2C操作句柄
第2个参数为从机设备地址
第3个参数为从机寄存器地址
第4个参数为从机寄存器地址长度
第5个参数为发送的数据的起始地址
第6个参数为传输数据的大小
第7个参数为操作超时时间 */
HAL_I2C_Mem_Write(&hi2c2,salve_add,0,0,PA_BUFF,sizeof(PA_BUFF),0x10);
HAL_I2C_Mem_Write_IT();
HAL_I2C_Mem_Read();
HAL_I2C_Mem_Read_IT();
HAL_I2C_Mem_Read_DMA();
HAL_I2C_Mem_Write_DMA();
HAL_I2C_Master_Receive();// STM32 主机接收,不需要用到寄存器地址
HAL_I2C_Master_Receive_IT();//中断IIC接收
HAL_I2C_Master_Receive_DMA();// DMA 方式的IIC接收
HAL_I2C_Master_Transmit_IT(); //中断IIC发送
HAL_I2C_Master_Transmit_DMA(); // DMA 方式的IIC发送
HAL_I2C_Master_Transmit(&hi2c2,salve_add,PA_BUFF,sizeof(PA_BUFF),0x10); //STM32 主机发送
HAL_I2C_Slave_Receive();// STM32 从机机接收,不需要用到寄存器地址
HAL_I2C_Slave_Transmit();// STM32 从机机发送,不需要用到寄存器地址
HAL_I2C_Slave_Receive_IT();
HAL_I2C_Slave_Receive_DMA();
HAL_I2C_Slave_Transmit_IT();
HAL_I2C_Slave_Transmit_DMA();
举个调用 HAL_I2C_Mem_Write()函数读取16个字节的使用例子
HAL_I2C_Mem_Read(&hi2c2,U9_Save_Read_Add,ADC_Result_Add,I2C_MEMADD_SIZE_8BIT,Read_buff,2,0xff);
再举一个 HAL_I2C_Mem_Read( ) 函数写16个字节的使用例子
uint8_t Configuration_config[2]={0x09,0xc0};
//设置U9的Configuration寄存器为 0x09 0xc0
HAL_I2C_Mem_Write(&hi2c2,U9_Save_Write_Add,ADC_Configuration_Add,I2C_MEMADD_SIZE_8BIT,Configuration_config,2,0xff);
void MX_USART1_UART_Init(void)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
}
二,配置串口中断
1.先写一个串口接收函数
typedef struct
{
bool UART2_Flag_Bit;
unsigned int RxCounter; //IDLE receive flag
uint8_t RxBuff[256]; //DMA receive buffer
}USART_2;
uint8_t UART2_TIME=0;
void USER2_UART_IRQHandler()//串口中段处理函数
{
if (__HAL_UART_GET_FLAG(&uartoline ,UART_FLAG_RXNE )!=RESET ){
__HAL_UART_ENABLE_IT(&uartoline,UART_IT_IDLE) ;
Off_Line.RxBuff[Off_Line.RxCounter++] =(uint8_t)(uartoline.Instance->DR&(uint8_t)0x00ff);
UART2_TIME=1;
WEB服务器的部署 打开虚拟机后查看已经开放的端口,可以看到没有TCP 80、TCP 443,说明HTTP服务端口没有打开
打开我的电脑—双击CD驱动器
选择安装可选的Windows组件
选择应用程序服务器—打开Internet信息服务—选择万维网服务和FTP服务
一路确定后,开始安装,会弹出如下窗口,因为我们要从镜像文件中安装组件,点击浏览查看我们的映像文件是哪个盘,由下图可知,映像文件是E盘,所以我们将文件复制来源改为E:\i386
第二个弹窗也是同样的操作
完成安装
打开IIS服务器
打开运行,输入netstat -an 命令,查看80端口是否开放,如图,可以看到80端口处于开放状态,证明HTTP服务已经打开(21端口是FTP服务)
在客户机中输入服务器的IP地址(10.1.1.1)进行访问,出现了如下的页面
在服务器中我们找到c:\Inetpub\wwwroot路径下的iisstart.htm文件,打开后出现了与客户机中一样的页面
安装成功
新建网站,右击网站—新建 —网站
输入网站描述
指定新网站的IP地址、端口设置和主机头。
主目录的路径就是已经写好的网页的路径,此处选择已经写好的html文件
由于我们创建的是静态网页,没有互动功能,所以选择读取权限即可
完成网站的创建
此时我们可以在另一台XP虚拟机中访问10.1.1.1,看到出现了如下的错误
因为我们没有将要打开的网页添加到默认文档中,所以无法看到,解决这一问题的方法,回到Windows 2003的IIS服务器中,右击属性—文档—添加—写入要打开的网站界面—确定
添加完成后,在Windows XP中刷新页面,可以看到已经打开了我们自己所写的页面
网站发布成功
实验原理
基于与VPN实例绑定的接口和路由协议等建立实例路由表并基于实例路由表转发数据,实现实例间隔离,本实验以生产网络和管理网络为例,生产网络和管理网络分别使用OSPF协议和IS-IS协议实现生产互通。
实验拓扑
实验步骤
1.各网络设备命名,其余设备同理。
<Huawei>system-view
[Huawei]sysname SW1
[SW1]
<Huawei>system-view [Huawei]sysname AR1
[AR1]
2.根据拓扑在SW1设备创建VLAN 10、VLAN 20,并且划分VLAN。
[SW1]vlan batch 10 20
[SW1]interface Ethernet0/0/1
[SW1-Ethernet0/0/1]port link-type trunk
[SW1-Ethernet0/0/1]port trunk allow-pass vlan 10 20
[SW1-Ethernet0/0/1]quit
[SW1]interface Ethernet 0/0/2
[SW1-Ethernet0/0/2]port link-type access
[SW1-Ethernet0/0/2]port default vlan 10
[SW1-Ethernet0/0/2]quit
[SW1]interface Ethernet0/0/3
[SW1-Ethernet0/0/3]port link-type access
[SW1-Ethernet0/0/3]port default vlan 20
[SW1-Ethernet0/0/3]quit
3.根据拓扑图配置设备接口IP地址,其余设备同理。注:终端IP地址配置<略>。
[AR1]interface GigabitEthernet 0/0/1
[AR1-GigabitEthernet0/0/1]ip address 100.1.1.1 24
[AR1-GigabitEthernet0/0/1]quit
[AR1]interface LoopBack 0
[AR1-LoopBack0]ip address 1.1.1.1 32
前言:MongoDB是前端开发人员普遍使用的数据库,因为MongoDB不需要图形界面,是一个基于分布式文件存储的开源数据库系统。MongoDB 将数据存储为一个文档,数据结构由键值对(key=>value)组成;MongoDB 文档类似于 JSON 对象。
安装成功标识:
1.打开服务器输入地址为:‘http://localhost:27017’;
2.页面显示信息为:‘It looks like you are trying to access MongoDB over HTTP on the native driver port.’
接下来开始安装:
1.下载MongoDB数据库地址:(会自动检测电脑版本, 下载合适的MongoDB 版本号)
https://www.mongodb.com/try/download/community
2.打开下载的文件,找到 msi 后缀双击,进入安装
3.Custom 可以指定想安装在D盘或其他盘中
3.默认 'Run service as Network Service user’
4.取消勾选左下角图形化工具(Install MongoDB Compass),要不然安装时间会很长很长...
5.点击next ,finish就结束安装了
6.安装完的文件夹目录,config文件是后期加上去的,稍后会介绍
现在开始配置MongoDB的数据库环境
1.右击桌面图标 “我的电脑” ,找到 环境变量 在 系统变量 里面找到 path,点击 编辑
2.添加MongoDB的bin地址(ps:注意自己电脑存放mongodb的文件夹路径)
最后一步运行MongoDB服务
1.创建数据库文件的存放位置
在data文件夹下创建 db 文件夹(启动 MongoDB 服务之前需要必须创建数据库文件的存放文件夹,否则命令不会自动创建,而且不能启动成功)
2.启动 MongoDB 服务(Win+R键),输入cmd
3.进入命令编辑模式,找到db文件,按如下方式输入
4.输入命令,来启动MongoDB 服务
其实很简单,几乎是开箱即用。接口地址是://996315.com/api/scan/
首先在a标签中链接指向它
<a href="//996315.com/api/scan/">扫码</a> 当链接被点击,它就会去调用扫一扫,然后会再跳转回来,此时网址已经多了个qrresult参数,扫码扫到的内容就在附在这个参数后面,去获取这个参数的内容即可。
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script> <script type="text/javascript"> var qr=GetQueryString("qrresult"); if(qr.length>0){ alert(qr);//取到的内容,可以放到你表单的input文本框中,或者提交到后台 } function GetQueryString(name){ var reg = new RegExp("\\b"+ name +"=([^&]*)"); var r = location.href.match(reg); if (r!=null) return decodeURIComponent(r[1]); } </script> 取到了扫码结果下一步你是决定放文本框还是提交到后台看你业务需要了
其实调用接口还有另外一种方式,即:
<a href="//996315.com/api/scan/?redirect_uri=你的url地址">扫码</a> 功能经过测试是一样的,这个和上面的区别就是明确指定了扫码后将结果发送到指定url。
如果是本页面回调的话就用默认的不带参数的,更省事。
如果需要把扫码结果发送到指定页面就得用上redirect_uri这个参数,告诉接口扫码后跳转到那个页面。
另外,如果你不是用a标签,那么可以在事件中用js代码window.open来打开这个接口地址,也是一样的效果。
ps:缺省redirect_uri参数可能会出现异常,目前在苹果ios系统15.4.1上发现无法获取来源地址的问题,根本原因是高版本的苹果操作系统升级了安全机制,使得referer只能获取域名,如果是ip方式的就只能获取ip地址,后面的目录及文件名还有参数都获取不到。所以建议还是加上redirect_uri参数。
{术语
芯片位宽:每个传输周期的数据量。
逻辑Bank:由行和列组成,只要指定行和列接可以找到存储单元。
RAS:Row Address Strobe 行地址选通脉冲。
CAS:Column Address Strobe 列地址选通脉冲。
DQ:Data Input/Output 数据I/O通道。
DQS:Data Strobe 与数据同步出现,数据在DQS的上升沿和下降沿出现。(与DQS_N配合使用)
DM: Input Data Mask 用于屏蔽无效的写入数据。低电平写入有效、高电平写入无效
BL:Burst Length 突发长度,
RCD:RAS to CAS Delay, 行选通到读写命令的延时,决定了行寻址和读写命令的间隔。
CL: CAS Latency, 读出命令到数据真正输出的时延。DDR的CL值一般为2、2.5和3. DDR2的CL值一般为2、3、4、5和6 DDR3的CL一般为5到16
CWL:CAS Write Latency 写入命令到数据总线输入的延时
AL: Additive Latency, 附加时延。DDR3标准中,AL可设置为0、CL-1、CL-2。
引入AL的目的在于提高总线的效率。RAS后,可以立即CAS读命令。但是需要保持AL个周期。
DDR2 SDRAM: Double Data Rate 2 SDRAM 它与上一代DDR SDRAM最大的不同就是,
虽然同是采用了在时钟的上升沿和下降沿同时进行数据传输的方式,DDR2拥有两倍于DDR的预读取能力(4bit预读取)。
DDR2每个时钟周期能以4倍外部总线的速度读写数据。
DDR3采用了8bit预读取设计,这样SDRAM内核的频率只有接口频率的1/8,例如DDR3-800的核心工作频率只有100MHz
工作电压从DDR2的1.8V降至1.5V
ACTIVE:激活指定Bank和行,在READ和WRITE命令前
PRECHARGE:读写完成后,对同一Bank中另一行操作前,需要关闭现在的工作行,才能打开新一行。该过程就是预充电,通过PRECHARGE命令控制。
REFRESH:刷新,SDRAM的存储体是电容,如果长时间没有对电容充电会导致数据丢失,通过定期刷新,对数据进行重写,保证数据不丢失。
效率
RP: 预充电的命令周期,即预充电命令经过RP时间后才允许对新行操作,决定了对同一Bank不同行之间的操作速度。
RFC:刷新时间间隔。
RAS:同一Bank中行激活到预充电(关闭行)命令的时间间隔
RRD:同一Bank中不同行激活的间隔。
WR: Write Recovery 写恢复时间,数据从数据总线DQ真正写到SDRAM中的时间。
批量修改数据库表字段长度 select concat('alter table ',OWNER,'.',TABLE_NAME,' MODIFY ',COLUMN_NAME,' VARCHAR2(1000)',';') from SYS.DBA_TAB_COLUMNS WHERE OWNER= '模式' and DATA_LENGTH = '字段大小,自行更改' and COLUMN_NAME <> '字段名,自行更改' 批量修改数据库表字段大小写 select CONCAT('alter table ',OWNER,'.',TABLE_NAME,' rename column ',CONCAT('"',COLUMN_NAME,'"'),' TO "',UPPER(COLUMN_NAME),'";') from SYS.DBA_TAB_COLUMNS where OWNER= '模式' and DATA_LENGTH = '字段大小,自行更改' and COLUMN_NAME <> '字段名,自行更改' and COLUMN_NAME <> UPPER(COLUMN_NAME) 执行以上语句需要SYSDBA权限,将生成的SQL语句导出到excel表,复制并粘贴到SQL查询窗口,一键执行即可。
一.什么是冒泡排序法 冒泡排序是一种 较简单的 排序算法 。 它重复地走访过要排序的元素列,依次比较两个相邻的 元素 ,如果顺序错误就把他们交换过来。
二.对若干整数进行冒泡排序 为了让大家对冒泡排序有一个深入的了解,我们在这里以使用十个整数,进行冒泡排序为例。
1.主函数 首先,我们把主函数写好。
#include<stdio.h> int main() { //定义一个数组用来存放十个整数 int arr[10] = { 1,2,6,4,7,8,5,9,11,-1 }; //计算出这个数组的元素个数 int sz = sizeof(arr) / sizeof(arr[0]); 调用bubble_sort函数,进行冒泡排序 bubble_sort(arr, sz); //将排序后的数组打印 int i = 0; for (i = 0; i < sz; i++) { printf("%d ", arr[i]); } return 0; } 补充:关于数组传参的小细节 这里我们要注意,bubble_sort(arr, sz)中,传参时,我们写的是arr,表示将arr数组的首元素的地址传过去,而不是arr数组的内容。
数组名一般都是表示数组首元素的地址,但是,有两种情况除外。
1.sizeof(数组名) 这里表示整个数组。
2.&数组名 , 这里也是表示整个数组。
2.bubble_sort函数 接下来我们开始写bubble_sort函数。bubble_sort函数接收数组首元素的地址,以及数组长度两个参数。因为我们对一系列的元素进行冒泡排序,我们肯定需要先找到这些元素在哪里,其次我们需要知道进行冒泡排序的范围,所以数组长度这个参数也是必要的。
//冒泡排序法 void bubble_sort(int arr[],int sz) { int i = 0; //for循环中的内容是一趟冒泡排序,一共要进行sz-1趟 for (i = 0; i < sz - 1; i++) { int j = 0; int temp = 0; for (j = 0; j < sz - 1 - i; j++) //j < sz -1 -i,这里 - i 的原因是,我们已经排序好的元素不需要再参与到冒泡排序中 { //进行一次比较排序,将相邻的两个数进行比较 //在第一次这里的for循环结束后,数组的最后一个元素就是数组中最大的数 if (arr[j] > arr[j + 1]) { temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } } 3.
Linxu MMC 驱动子系统 文章目录 Linxu MMC 驱动子系统硬件关联目录说明mmc子系统的逻辑架构设备-总线-驱动模型一、MMC驱动抽象模型二、SDIO驱动抽象模型三、MMC/SDIO总线1. 总线结构体定义2. 总线注册3. 驱动注册4. 设备注册 四、MMC设备控制器(mmc host)1. 控制器结构体定义 MMC驱动注册MMC设备注册注册过程(瑞芯微MMC驱动源码)mmc卡(mmc type card)协议相关操作DTS配置参考资料 SD/SDIO/MMC 驱动是一种基于 SDMMC 和 SD SPI 主机驱动的协议级驱动程序,目前已支持 SD 存储器、SDIO 卡和 eMMC 芯片。
因为linux内核mmc子系统里面已经实现了这些协议,我们以后并不需要重新实现这些,只需要对协议有个简单的了解。
mmc是比较老的存储卡了,sd是mmc的替代者,sdio是基于sd而额外开发出的一种io接口卡。
硬件关联 CPU、MMC controller、存储设备之间的关联如下图所示,主要包括了MMC controller、总线、存储卡等内容的连接,针对控制器与设备的总线连接,主要包括时钟、数据、命令三种类型的引脚,而这些引脚中的cd引脚主要用于卡的在位检测,当mmc controller检测到该位的变化后,则会进行mmc card的注册或注销操作。
目录说明 针对mmc子系统,在代码实现上主要包括mmc core、mmc block、 mmc host这三个模块
mmc card:衔接最上层应用,主要用于实现mmc block驱动以及mmc driver即mmc层驱动(实际上我研究的源代码并没有这个目录(5.15.0-52-generic),猜测是合并到了core目录下);而mmc core:实现mmc/sd/sdio协议,主要包括mmc 总线、sdio总线的实现、mmc device、mmc driver的注册接口、mmc host与mmc card的注册与注销接口等内容。mmc host:存放各个mmc/sd/sdio控制器的驱动代码,最终操作mmc/sd/sdio卡的部分; mmc子系统的逻辑架构 MMC子系统从上到下分为3层
块设备层(MMC card):与Linux的块设备子系统对接,实现块设备驱动以及完成请求,如sys_open调用;通过调用core接口函数(具体如host->ops->rquest),驱动MMC core抽象出来的虚拟的card设备,如mmc、sd、tf卡,实现读写数据。
核心层(MMC core):是不同协议和规范的实现,为MMC控制器层和块设备驱动层提供接口函数。
核心层封装了 MMC/SD 卡的命令(CMD),例如存储卡的识别、设置、读写、识别、设置等命令。
MMC核心层由三个部分组成:MMC,SD和SDIO,分别为三类设备驱动提供接口函数;
core.c 把 MMC 卡、 SD 卡的共性抽象出来,它们的差别由 sd.
一、下载工具类库
commons IO包下载地址
解压bin目录下的jar包放到idea新建lib目录中
进行加入库操作
二、编写下载器
用的是FileUtils.copyURLToFile
class WebDownLoader{ //下载方法 public void downloader(String url, String name){ try { FileUtils.copyURLToFile(new URL(url), new File(name)); } catch (IOException e) { e.printStackTrace(); System.out.println("IO异常,downloader出现异常"); } } 三、多线程的实现
线程是继承Thread类重写run方法(下载图片的执行体)
public class TestThread2 extends Thread{ private String url; //网络地址 private String name;//保存文件名 public TestThread2(String url, String name){ this.url = url; this.name = name; } @Override public void run() { super.run(); } } 四、总的代码
package com.kuang.demo1; import org.
目标:在做菜单权限的时候需要选择图标,如果既想要用elementUI自带的图标,还想要自定义的图标,这时就需要二者结合一下 如果用的是vue-admin-template,那svg组件和引入elementUI是不需要操作的,直接使用即可。步骤直接跳到最后就行
一、封装组件svg src/components/Svgicon/index.vue
<template> <div v-if="isExternal" :style="styleExternalIcon" class="svg-external-icon svg-icon" v-on="$listeners" /> <svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners"> <use :xlink:href="iconName" /> </svg> </template> <script> // doc: https://panjiachen.github.io/vue-element-admin-site/feature/component/svg-icon.html#usage import { isExternal } from '@/utils/validate' export default { name: 'SvgIcon', props: { iconClass: { type: String, required: true }, className: { type: String, default: '' } }, computed: { isExternal() { return isExternal(this.iconClass) }, iconName() { return `#icon-${this.iconClass}` }, svgClass() { if (this.
//输出100-999的水仙花数 #include <stdio.h> int main() { int i ; int a ,b,c; for(i= 100;i <= 999; i++ ) { a = i%10; //个位 b = i /10 %10; //十位 c = i/100; //百位 if (i == a*a*a + b*b*b + c*c*c) printf("i = %d\n",i); } return 0; }
1 存储引擎 1.1 MySQL体系结构 1.2 存储引擎简介 存储引擎就是存储数据、建立索引、更新/查询数据等技术的实现方式。存储引擎是基于表的,而不是基于库的,同一个库的多个表可以采用不同的存储引擎,所以存储引擎也经常称为表类型。创建表时可以指定存储引擎,如果不指定默认存储引擎是InnoDB。
查询建表语句:
show create table tb_brand;
CREATE TABLE `tb_brand` ( `id` int NOT NULL AUTO_INCREMENT, `brand_name` varchar(20) DEFAULT NULL, `company_name` varchar(20) DEFAULT NULL, `ordered` int DEFAULT NULL, `description` varchar(100) DEFAULT NULL, `status` int DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci //主键是自增长的,下一条记录ID是8 在创建表时指定存储引擎:
ENGINE=XXX,例如ENGINE=InnoDB
查看当前数据库支持的存储引擎:
show engines;
1.2.1 InnoDB innodb_file_per_table开关,控制着每张innodb表都创建一个表空间文件,还是所有的innodb表共用一个表空间文件。查看系统变量:
1.2.2 InnoDB的逻辑存储结构 Row(一行)中包含,最后一次操作事务的ID(Trx id)、指针、各个字段
page是磁盘操作的最小单元。一个Extent大小固定是1M,一个page大小固定是16K,一个Extent包含64个page
1.2.3 MyISAM和Memory Memory数据存放到内存当中,访问速度很快
Memory在磁盘上只有.sdi文件
Memory支持hash索引
文章目录 前言一、nextTick是什么?1:由来2:语法3:使用4:作用 二、MutationObserver1:由来2:作用3:使用 三、vue中nextTick的底层原理详解1:简单概括事件循环2:思考3:微任务(microtask)4:vue中nextTick源码(1)vue@2.2.5关于nextTick的源码(2)vue@2.6.14关于nextTick的源码(3)vue@3.2.13关于nextTick的源码(4)vue2和vue3 nextTick的区别 5:vue2中的补充(vue的降级策略) 总结 前言 如果你理解nextTick和MutationObserver的基本使用,为节约时间请直接跳到第三步浏览”vue中nextTick的底层原理详解“即可。
一、nextTick是什么? 1:由来 尽管vue所使用的MVVM框架并不推荐访问DOM,但实际开发中免不了要进行DOM操作。而nextTick就提供了一个桥梁,确保我们操作的是更新后的DOM。
2:语法 this.nextTick(()=>{}) 3:使用 import { nextTick } from 'vue' export default { data() { return { message: '张三' } }, methods: { async increment() { // DOM 还未更新 this.message = '李四' // DOM 此时已经更新 await nextTick(()=>{ // 更新后要进行的操作 )} } 4:作用 等待下一次 DOM 更新刷新的工具方法。(在数据更新后调用里面的回调函数)
二、MutationObserver 1:由来 MutationObserver是HTML5新增的属性。
2:作用 MutationObserver用于监听DOM修改事件,能够监听到节点的属性、文本内容、子节点等的改动。
3:使用 官网示例如下
// Select the node that will be observed for mutations const targetNode = document.
/ 今日科技快讯 /
近日,经历了一次停办,两次规模严重缩水之后,2023年世界移动通信大会(MWC)终于再现往日盛况:有来自200多个国家和地区的2000多家厂商参加,并在此次展会上发布了最新的产品与科技,仅中国就有100余家厂商参展。
每年的MWC都被称作“全球移动通信技术发展的风向标”,今年大会以“时不我待——明日科技,将至已至”为主题,具体又围绕着5G新动能、数字万物、开放网络、超越现实+ 、金融科技等五大热点主题展开。
/ 作者简介 /
本篇文章转自史大拿的博客,文章主要分享了对kotlin协程源码的分析,相信会对大家有所帮助!
原文地址:
https://juejin.cn/post/7204752219915288636
/ 前言 /
kotlin协程源码十分庞大,本篇只能把我理解的源码聊一聊,不会特别深入研究,只会浅浅的看看表层。本来计划协程系列是10篇左右,后续是flow热流冷流之类的,冷流操作符之类的应该不会在写了,flow当作Rxjava来用就可以,后续可能还会写一篇关于热流的文章。也可能没有:) 主要是不好写,文字写出来还是比较生硬。
/ launch 浅析 /
源码阅读从最简单的一个launch开始!
在launch的时候,会执行CoroutineScope.newCoroutineContext函数。这里会传入一个EmptyCoroutineContext。CoroutineScope.newCoroutineContext会走foldCopies , 这个函数是用来合并2个协程的。
先来看看 foldCopies的参数:
coroutineContext // 可以看出,此时coroutineContext为JobImpl我们稍后来看看它是在什么时候赋值的
context // 默认什么都没有传,是EmptyCoroutineContext
true // isNewCoroutine是否创建新的Coroutine
我们在创建CoroutineScope的时候,会对coroutineContext赋值。
我们在创建协程作用域的时候,会初始化一个Job,Job的默认实现为JobImpl。好了,再回到CoroutineScope.newCoroutineContext方法。
执行完foldCopies后,我们知道此时返回结果combined = jobImpl,最后当返回的时候if (combined !== Dispatchers.Default && combined[ContinuationInterceptor] == null)最终给他添加一个默认调度器Dispatchers.Default。
tips:ContinuationInterceptor是协程拦截器,下面会说,不要急。
再次回到主线流程。
此时newContext = [JobImpl,Dispatchers.Default],如果说有子协程的情况下,newCoroutineContext这个方法就会使用最新的coroutineContext。例如这样:
这都是foldCopies的功劳,foldCopies的细节就不看了,没意义。比较烧脑,不想看。再接着往下走:
在聊协程启动模式的时候说过,coroutineStart一共有4种模式:
DEFAULT
LAZY
UNDISPATCHED
ATOMIC // 实验中
此时会判断是否是懒加载模式,很明显不是懒加载,所以会走 StandaloneCoroutine。StandaloneCoroutine会通过 handleJobException 捕获一些异常。比如说在JVM中使用Main线程的时候,会提示Module with the Main dispatcher had failed to initialize.
为了配置本地git连接到gitlab,查了不少资料,很多资料都说的不清不楚的,今天我自己终于搞清楚了,把教程写下来给有需要的人。
要从零开始配置本地Git与GitLab的连接,请按照以下步骤进行操作:
安装Git:如果还没有安装Git,请在计算机上安装它。可以从Git官方网站(https://git-scm.com/downloads )下载Git安装程序,并按照安装指南进行操作。
在GitLab上创建一个账户:如果您还没有GitLab账户,请在GitLab官网(https://gitlab.com/users/sign_in)上创建一个账户。
创建一个新的Git仓库:登录到您的GitLab账户,然后在页面上创建一个新的Git仓库。请注意Git仓库的名称和位置,因为这些信息将用于配置本地Git。
在本地计算机上设置Git:在计算机上创建个文件夹,右键打开Git Bash终端,并输入以下命令以设置您的Git用户名和电子邮件地址:
git config --global user.name "Your Name" git config --global user.email "youremail@example.com" 生成SSH密钥:输入以下命令以生成SSH密钥:
ssh-keygen -t rsa -C "youremail@example.com" 您将被提示输入一个文件名和密码。您可以保留默认文件名和密码,也可以选择自己的文件名和密码。
添加SSH密钥到GitLab:使用以下命令将SSH密钥添加到GitLab:
cat ~/.ssh/id_rsa.pub 将在屏幕上看到SSH密钥。复制并粘贴到GitLab账户的SSH密钥部分。
克隆Git仓库:在本地计算机上打开Git Bash终端,并输入以下命令以克隆Git仓库:
git clone git@gitlab.com:<your_username>/<your_repository>.git 将<your_username>和<your_repository>替换为您在步骤3中创建的Git仓库的用户名和仓库名称。
进入Git仓库:使用以下命令进入Git仓库:
cd <your_repository> 将更改推送到Git仓库:在本地计算机上进行更改后,使用以下命令将更改推送到Git仓库:
git add . git commit -m "commit message" git push origin master 其中,“commit message”是您的提交消息。
现在您已经成功地将本地Git与GitLab连接起来,可以开始使用Git进行版本控制并将代码推送到GitLab仓库。
在执行第9步操作的时候,如果报错:
On branch master nothing to commit, working tree clean To git@gitlab.com:<your_username>/<your_repository>.git ! [rejected] master -> master (fetch first) error: failed to push some refs to 'git@gitlab.
前言
2021年8月5日,Vue正式发布3.2版本,同时,Vue的作者尤雨溪还在个人微博称:“ < script setup > + TS + Volar = 真香 ”;2022年1月22日,Vue官方宣布Vue3成为了新的默认版本。
如今的Vue3已经势不可挡,当然,搭建一个全新的Vue3项目也有了全新的方式,今天就带大家熟悉一下Vue3项目的全新搭建方式。
搭建步骤:
下载node 版本>16.0
建目录,如(vue3Study)
用IDE工具打开终端,输入命令
npm init vue@latest 接下来会让我们依次输入以下几个问题的答案,帮助创建项目:
Project name:项目名称,默认值:vue-project,可输入想要的项目名称,此处不建议中文。 Add TypeScript? 是否加入TypeScript组件?默认值:No。 Add JSX Support? 是否加入JSX支持?默认值:No。 Add Vue Router for Single Page Application development? 是否为单页应用程序开发添加Vue Router路由管理组件?默认值:No。 Add Pinia for state management? 是否添加Pinia组件来进行状态管理?默认值:No。 Add Vitest for Unit testing? 是否添加Vitest来进行单元测试?默认值:No。 Add an End-to-End Testing Solution?默认值No, Cypress , Playwright (Add Cypress for both Unit and End-to-End testing?
B-DMC(Binary-input discrete memoryless channel)
我们写W : X → Y来表示B-DMC具有输入字母X、输出字母Y和一般转移概率W (y|x),x ∈ X,y ∈ Y。输入字母表X将总是{0,1},输出字母表和转移概率可以是任意的。
当W是对称信道时,对称容量I(W)等于香农容量,即对于该信道,存在输出字母表Y的置换π,使得对于所有的有 且 。
二进制对称信道(BSC)和二进制擦除信道(BEC)是对称信道的例子。
BSC是B-DMC ,其中。
BEC是B-DMC ,对于每个,存在 或者 。
在后一种情况下,y被称为擦除符号。所有擦除符号y上的之和称为BEC的擦除概率。
主要关注两个参数:
对称容量:
巴氏参数:
这些参数分别用作速率和可靠性的度量。是使用频率相同的输入时,上可靠通信的最高速率。是当仅用于发送0或1一次时最大似然(ML)判决误差的概率的上限。取值都是0-1。
二者之间的关系:
模型降阶,简单讲就是在保证设定精度的前提下,简化模型,减少计算量。这种简单的思想很早就出现在工程领域的文献中,但其作为具有理论以及的较为系统的数学方法也就是近一二十年的事。
求解大规模线性方程组,自由度少则几十万,多则上千万,利用常规求解方法效率低下,而模型降阶的目的正是降低此类问题的计算复杂性,例如,在对大规模电路进行精确模型分析之前,用户只想对模型部分进行建模仿真,或只想了解大概数值范围和趋势,都可以通过模型降阶方法来减少模型计算时间。
模型降解的通常要求是:
1. 计算结果与原始模型相比,误差小或者在设定介绍范围内;
2. 保留原始模型的特点和特征;
3. 降阶后的模型计算要保证稳定性和有效性。
可以看出,广义上的模型降阶泛指简化减少模型计算。讨论重点是求解大规模线性方程组的降阶方法。
在模型降阶中,最基础和最常用的就是Krylov子空间方法。Krylov全名Aleksey Nikolaevich Krylov,1863年出生于俄国,俄罗斯海军工程师,一生著有300多篇论文和书籍,涵盖了流体力学,造船,天文,测量,数学等领域。1931年Krylov提出了Krylov子空间和方法的论文,Krylov方法通常采用所构造的标准列正交向量基底对系统进行模型降阶,使得降阶系统的传递函数对于原始线性系统的传递函数,在其指定的区域内有很好的近似。Kylov子空间方法在数学理论上相当完善,其主要优点是算法稳定,能保持原始传递函数的特点,有时还能保持系统的基本特性。
对于线性方程组 A*x = B
其中A为 N*N的矩阵,N为大数
常规方法为 x=A的逆矩阵*B
很显然,直接求逆矩阵比较困难,尤其A是稀疏矩阵时
我们将 A的逆矩阵*B = m1*B + m2*A*B + m3*A*A*B
其中m1,m2,m3为未知的标量系数,可以看出A,B都是已知量,只要求解 m1,m2,m3就可以了
Krylov已经证明,当m个数=N时,其结果为精确解。
实际工程中,可以根据具体情况选择m的数量。
一些常用的Krylov子空间方法包括:
Arnoldi降阶算法
共轭梯度系列相关算法
最小余量系列相关算法
C正交Arnoldi算法
块Arnoldi算法
对称和非对称Lanczos降阶算法
多重Krylove子空间降阶
在算法工具方面,Matlab 仍然是首选。两个基于Matlab的MOR算法工具:
MORLAB
https://www.mpi-magdeburg.mpg.de/projects/morlab
RBmatalb
https://www.morepas.org/software/
visual studio 2022 社区版 c# 环境搭建及安装使用【图文解析-小白版】 visual studio 安装 C# 环境安装流程创建c#窗体应用程序 visual studio 安装 C# 环境 首先,进入其官网下载对应的visual studio社区版本,官网链接: https://visualstudio.microsoft.com/zh-hans/ 安装流程 双击.exe文件进行安装:
大概20GB左右,需要耐心等待
选择安装的组件,c#和.net以及一些扩展
安装位置不建议更改,改了容易出现问题。
选择完毕后,点击安装
安装完毕,稍后其会自动启动
创建c#窗体应用程序 首先,打开visual studio 软件,第一次需要进行登录(使用本机微软账号即可)
选择配色方案
进入软件界面,开始创建,在visual studio 2022版本中创建方式稍有不同,选择**“C# windows窗体应用”**
选择代码存放路径,及设定项目名称,点击下一步
点击创建
好啦,c#的环境搭建完毕!
一、宏观上的区别:
1、mysql与oracle都是关系型数据库,应用于各种平台。mysql最开始是一个瑞典公司开发的,但后来被sun公司收购,后来sun又被oracle收购,所以现在可以说mysql属于甲骨文公司了,mysql开源免费的,而oracle则是收费的,并且价格非常高。
mysql默认端口:3306,默认用户:root
oracle默认端口:1521,默认用户:system
mysql的安装卸载很简单,oracle很麻烦,安装所用的空间差别也是很大的,mysql安装后差不多一两百兆,而oracle则有3G左右,且使用的时候oracle占用特别大的内存空间和其他机器性能。
mysql登录:mysql -hlocalhost -uroot -p密码(h:host、u:user、p:password)
oracle登录:sqlplus user_name/password@IP:port/instance_name;(其中可以把IP地址,端口号,实例名写在一个TNS文件中取一个别名,登陆的时候输入这个别名就行了)
初学阶段,图形化工具,mysql可以使用Navicat,Oracle一般用PLSQL,也可以用sqlyog等;
mysql的管理工具较少,在Linux下的管理工具的安装有时需要安装额外的包(phpmyadmin,etc),有一定复杂性。
oracle有多重成熟命令行、图形界面、web管理工具,还有很多第三方的管理工具,管理极其方便高效。
oracle支持大并发,大访问量,是OLTP最好的工具。
2、数据库的层次结构:
mysql:默认用户是root,用户下可以创建好多数据库,每个数据库下还有好多表,一般情况下都是使用默认用户,不会创建多个用户;
oracle:创建一个数据库,数据库下有好多用户:sys、system、scott等,不同用户下有好多表,一般情况下只创建一个数据库用。
二、操作区别:
1、数据库中表字段类型:
mysql:int、float、double等数值型,varchar、char字符型,date、datetime、time、year、timestamp等日期型。
oracle:number(数值型),varchar2、varchar、char(字符型),date(日期型)等…
其中char(2)这样定义,这个单位在oracle中2代表两个字节,mysql中代表两个字符。
其中varchar在mysql中,必须给长度例如varchar(10)不然插入的时候出错。
2、主键:
mysql一般使用自动增长类型,在创建表时只要指定表的主键auto increment,插入记录时,不需要再指定该记录的主键值,mysql将自动增长。
oracle没有自动增长类型,主键一般使用的序列,插入记录时将序列号的下一个值赋给该字段即可,只是ORM框架是只要是native主键生成策略即可。
3、单引号处理:mysql里可以用双引号包起字符串,oracle只可以用单引号包起字符串。
4、分页处理:
mysql是直接在SQL语句中使用limit就可以实现分页
oracle则是需要用到伪劣ROWNUM和嵌套查询
5、对事务提交:
mysql默认是自动提交,可以修改为手动提交
oracle默认不自动提交,需要手动提交,需要在写commit指令或点击commit按钮。
6、对事务的支持:
mysql在innodb存储引擎的夯机所的情况下才支持事务,而oracle则完全支持事务。
7、事务隔离级别:
mysql是repeatable read的隔离级别,而oracle是read commited的隔离级别;
同时二者都支持serializable串行化事务隔离级别,可以实现最高级别的读一致性。每个session提交后其它session才能看到提交的更改;
oracle通过在undo表空间中构造多版本数据块来实现读一致性,每个session查询时,如果对应的数据块发生变化,oracle会在undo空间中为这个session构造它查询时的旧的数据块;
mysql没有类似oracle的构造多版本数据的机制,只支持read commited的隔离级别,一个session读取数据时,其他session不能更改数据,但可以在表最后插入数据;session更新数据时,要加上排它锁,其他session无法访问数据。
8、并发性:
mysql以表级锁为主,对资源锁定的粒度很大,如果一个session对一个表加锁时间过长,会让其他session无法更新此表中的数据。虽然Innodb引擎表可以用行级锁,但这个行级锁的机制依赖于表的索引,如果表没有索引,或者sql语句没有使用索引,那么仍然使用表级锁;
oracle使用行级锁,对资源锁定的粒度要小很多,只是锁定sql需要的资源,并且加锁是在数据库中的数据行上,不依赖于索引,所以oracle对并发性的支持要好很多。
9、逻辑备份:
mysql逻辑备份时要锁定数据,才能保证备份的数据是一致的,影响业务正常的dml使用,oracle逻辑备份时不锁定数据,且备份的数据是一致的。
10、复制:
mysql:复制服务器配置很简单,但主库出问题时,从库可能丢失一定的数据,且需要手工切换从库到主库;
oracle:既有堆或拉式的传统数据复制,也有dataguard的双机或多机容灾机制,主库出问题时,可以自动切换备库到主库,但配置管理较复杂。
11、性能诊断:
mysql的诊断调优方法较少,主要有慢查询日志;
oracle有各种成熟的性能诊断调优工具,能实现很多自动分析、诊断功能。比如awr、addm、sqltrace、tkproof等。
12、保存数据的持久性:
mysql默认提交sql语句,但如果更新过程中出现db或主机重启的问题,也许会丢失数据;
oracle把提交的sql操作先写入了在线联机日志文件中,保持到了硬盘上,可以随时恢复。
13、热备份:
oracle有成熟的热备份工具rman,不影响用户使用数据库。即使备份的数据库不一致,也可以在恢复时通过归档日志和联机重做日志进行一致的回复。
mysql:
myisam引擎:用mysql自带的mysqlhostcopy热备时,需要给表加读锁,影响dml操作;
innodb引擎:它会备份innodb的表和索引,但是不会备份.frm文件,用ibbackup备份时,会有一个日志文件记录备份期间的数据变化,因此可以不用锁表,不影响其它用户使用数据库,但此工具是收费的。
innobackup是结合ibbackup使用的一个脚本,它会协助对.frm文件的备份。
14、日期转换:
mysql中日期转换用dateformat()函数;
oracle用to_date()与to_char()两个函数。
本节内容需要有些基础知识,如进程和线程,队列数据结构
一、setTimeout和setInterval 只要使用过JavaScript的朋友,对setTimeout和setInterval应该不会默生,如果光说怎样去使用这个API,并不难,无非就是隔多少毫秒再执行某个函数,把变化的内容封装在函数中,就可以制作出动画效果,这也是最初写JavaScript时的常见写法,多年以前的IE6时代我对这个函数的印象就是它是用来做网页动画特效的,由于IE6浏览器并未开放源码,那这个定时任务到底是怎样执行的呢?只能去官方通过文档了解。后来,Google浏览器Chrome V8引擎是开源,通过源码,可以了解到JavaScript的setTimeout是怎样实现的了,关键部分就是消息队列和事件循环, 把任务放到了队列中,隔一定时间再把它取出来执行,我们知道浏览器渲染进程中所有运行在主线程上的任务都需要先添加到消息队列,然后事件循环系统再按照顺序执行消息队列中的任务. 在Java NIO中的也是相类似的玩法,开一个线程,在线程中不断的轮询是否有TCP连接事件或是读写事件,如果有则进入循环并判断是哪种任务并给出相应的处理逻辑,在NIO出现前,用IO写服务器,同样会开一个线程,只是accept方法是要阻塞并等待
// 轮询,且返回时有就绪事件 while (selector.select() > 0){ // 获取就绪事件集合 Set<SelectionKey> keys = selector.selectedKeys(); ...... } ServerSocket serverSocket = new ServerSocket(9999); while(true) { Socket socket = serverSocket.accept() .... } //一个阻塞队列 Queue<T> q = new LinkedBlockingQueue<>(); ... while(true){ T t = q.take(); ...... } 我们知道,在JavaScript中一旦有异步执行的回调函数就会将它添加到消息队列中,为什么要这样做,还是因为JavaScript是单线程的,这里并不是说浏览器是单线程。
且看Chrome源码:base/task/sequence_manager/task_queue_impl.h, 这里定义了一个队列结构,包含出队入队等方法
struct DelayedIncomingQueue { public: DelayedIncomingQueue(); DelayedIncomingQueue(const DelayedIncomingQueue&) = delete; DelayedIncomingQueue& operator=(const DelayedIncomingQueue&) = delete; ~DelayedIncomingQueue(); void push(Task task); void remove(HeapHandle heap_handle); Task take_top(); bool empty() const { return queue_.
MongoDB的聚合操作 一、MongoDB聚合二、单一作用聚合三、聚合管道3.1 什么是MongoDB聚合框架3.2 管道(Pipeline)和阶段(Stage)3.3 常用的管道聚合阶段3.3.1 聚合表达式3.3.2 $project3.3.2 $match3.3.2 $count3.3.3 $group3.3.4 $unwind3.3.5 $limit3.3.6 $skip3.3.7 $sort3.3.8 $lookup 3.4 聚合操作案例13.5 聚合操作案例23.5.1 返回人口超过500万的州3.5.2 返回各州平均城市人口 四、MapReduce操作 一、MongoDB聚合 MongoDB 中聚合(aggregate)主要用于处理数据(诸如统计平均值,求和等),并返回计算后的数据结果。 类似 S Q L 语句中的 c o u n t ( ∗ ) 。 \color{red}{类似 SQL 语句中的 count(*)。} 类似SQL语句中的count(∗)。
聚合操作处理数据记录并返回计算结果 \color{red}{聚合操作处理数据记录并返回计算结果} 聚合操作处理数据记录并返回计算结果。聚合操作组值来自多个文档,可以对分组数据执行各种操作以返回单个结果聚合操作包含三类: 单一作用聚合、聚合管道、 M a p R e d u c e 。 \color{red}{单一作用聚合、聚合管道、MapReduce。} 单一作用聚合、聚合管道、MapReduce。 单一作用聚合:提供了对常见聚合过程的简单访问, 操作单个集合聚合文档 \color{red}{操作单个集合聚合文档} 操作单个集合聚合文档 聚合管道是一个数据聚合的框架,模型基于数据处理流水线的概念 \color{red}{聚合管道是一个数据聚合的框架,模型基于数据处理流水线的概念} 聚合管道是一个数据聚合的框架,模型基于数据处理流水线的概念。文档进入多级管道,将文档转换为聚合结果MapReduce操作具有两个阶段: 处理每个文档并向每个输入文档发射一个或多个对象的 m a p 阶段 \color{red}{处理每个文档并向每个输入文档发射一个或多个对象的map阶段} 处理每个文档并向每个输入文档发射一个或多个对象的map阶段, 以及 r e d u c e 组合 m a p 操作的输出阶段。 \color{red}{以及reduce组合map操作的输出阶段。} 以及reduce组合map操作的输出阶段。 语法
机器学习与目标检测(数组相加:形状需要满足哪些条件) 机器学习与目标检测(数组相加:形状需要满足哪些条件)一、形状相同1.1、形状相同示例程序 二、符合广播机制2.1、符合广播机制的描述2.2、符合广播机制的示例程序 机器学习与目标检测(数组相加:形状需要满足哪些条件) 数组相加-形状需要满足的条件如下所示
一、形状相同 1.1、形状相同示例程序 形状相同示例程序如下所示
arr_a = np.array([1, 2, 3, 4]) arr_b = np.array([6, 7, 8, 9]) arr_add = np.add(arr_a, arr_b) print('1 数组形状相同') print('arr_a = ', arr_a) print('arr_b = ', arr_b) print('np.add(arr_a, arr_b) = ', arr_add, '\n') 形状相同示例程序运行如下所示
1 数组形状相同 arr_a = [1 2 3 4] arr_b = [6 7 8 9] np.add(arr_a, arr_b) = [ 7 9 11 13] 二、符合广播机制 2.1、符合广播机制的描述 符合广播机制的描述如下所示
(1):如果两个数组的维度数不相同,那么小维度数组的形状将会在最左边补 1。(2): 如果两个数组的形状在任何一个维度上都不匹配,那么数组的形状会沿着维度 为 1 的维度扩展以匹配另外一个数组的形状。
直接点 某8网 https://*****.b*b.h*****y*8*.com/ 具体网址格式就是这样的但是为了安全起见,我就这样打码了.
抛出问题 我们看到这个号码是在页面上正常显示的 F12 又是这样就比较麻烦,不能直接获取.用requests库也是获取不到正常想要的 源码的,因为字体加密了. 查看页面源代码又是这样的.所以就是我们想怎么解密呢. 解决步骤 获取到真正的源码找到对应的字体库进行解析操作. 获取到真正的源码 为什么用webdriver,因为requests拿不到真正的源码.
from selenium import webdriver # --- 进行chrome的配置 options = webdriver.ChromeOptions() prefs = {"profile.managed_default_content_settings.images": 2} # 设置无图模式 options.add_experimental_option("prefs", prefs) options.add_argument("service_args = ['–ignore-ssl-errors = true', '–ssl-protocol = TLSv1']") options.binary_location = r'C:\Program Files\Google\Chrome\Application\chrome.exe' # ---- chrome进行端口接管调用 options.add_argument('-incognito') driver = webdriver.Chrome(options=options) driver.set_page_load_timeout(5) # --- 设置宽和高位置 driver.maximize_window() # --- 拦截webdriver检测代码 driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {"source": """ Object.defineProperty(navigator, 'webdriver', { get: () => undefined }) "
1、UBI文件系统介绍 (1)UBI文件系统是基于MTD子系统的,内核必须要先支持MTD子系统;
(2)UBI文件系统适用于Nand flash,是可读写的文件系统;
2、内核中使能UBI 2.1、使能UBI设备驱动 Location: │ │ -> Device Drivers │ │ -> Memory Technology Device (MTD) support (MTD [=y]) │ │ -> Enable UBI - Unsorted block images 2.1.1、UBI wear-leveling threshold UBI 系统记录每个擦除块发生擦除操作的次数。此选项表示所有擦除操作次数中,最小值和最大值之间允许的最大间隔。此值默认为4096,对于寿命比较短的 MLC器件,此值应该配置相对小一点,比如256。
2.1.2、MTD devices emulation driver (gluebi) 模拟MTD驱动,选择此选项,当创建一个卷时, UBI 将同时模拟一个 MTD设备。这个功能提供了一个接口,供其它文件系统使用UBI。
2.2、使能UBIFS文件系统 必须先使能UBI设备驱动,才能找到UBIFS文件系统选项
3、mount 一个空 UBIFS 文件系统 3.1、 查看当前的mtd分区情况 # cat /proc/mtd dev: size erasesize name mtd0: 01000000 00020000 "boot" mtd1: 00400000 00020000 "kernel" mtd2: 02000000 00020000 "
在Python中,zip()函数是一种常用的内置函数,可以将两个或多个序列中的元素按位置打包成元组,然后返回这些元组组成的可迭代对象。zip()函数常常与for循环一起使用,用于同时遍历多个序列中的元素。在字典中,zip()函数也是一种常用的操作方式,可以用它来将两个列表或元组打包成一个字典。
下面是一个使用zip()函数创建字典的例子:
keys = ['a', 'b', 'c'] values = [1, 2, 3] my_dict = dict(zip(keys, values)) print(my_dict) # 输出 {'a': 1, 'b': 2, 'c': 3} 在这个例子中,zip()函数将keys和values两个列表打包成了一个可迭代对象,然后dict()函数将这个可迭代对象转换成了一个字典。
如果两个序列的长度不相等,则zip()函数只会返回长度相等的部分。
keys = ['a', 'b', 'c'] values = [1, 2] my_dict = dict(zip(keys, values)) print(my_dict) # 输出 {'a': 1, 'b': 2} 在这个例子中,values列表只包含了两个元素,因此zip()函数只返回了长度为2的元组,而'c'对应的值被忽略了。
本文的宗旨就是一文实现基于pytorch的单机多卡的分布式训练,多机多卡的暂时先不记录。没有pytorch分布式训练的原理等内容,目的是通过几个步骤能够直接快速的使用多GPU,包括分布式模型的save和load。之前的文章有简单的记录,但是有点问题,不够详细。
pytorch实现单机多卡有DataParallel和DistributedDataParallel,也就是DP和DDP这两种形式,
DP:
DDP:
前者DP比较简单,两行代码就行,但非真正的分布式,后者能够实现不同的GPU 占用基本相同的显存。这里只说后者。
1.训练代码与启动 from torch.utils.data import Dataset, DataLoader import torch.distributed as dist from torch.nn.parallel import DistributedDataParallel as DDP #step1:定义通信方式和device,这里device一般用命令行的的方式 #在使用torch.distributed.launch启动时,会自动给入local_rank参数 parser = argparse.ArgumentParser() parser.add_argument("--local_rank", type=int,default=-1) FLAGS = parser.parse_args() local_rank = FLAGS.local_rank torch.cuda.set_device(local_rank) dist.init_process_group(backend='nccl') # nccl的后端通信方式 device = torch.device("cuda", local_rank) #step2:分发数据,很重要的一步 train_sampler = torch.utils.data.distributed.DistributedSampler(train_dataset) val_sampler = torch.utils.data.distributed.DistributedSampler(val_dataset) train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=False, sampler=train_sampler,num_workers=2) val_loader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False, sampler=val_sampler,num_workers=2) #此处shuffle需要为False,可以自行在此之前先进行shuffle操作。 #setep3:初始化训练模型,使用DDP的方式 model = MyModel().to(device)#自己的模型 #model = torch.
了解数据链路层的基本概念和功能的基础上,重点掌握滑动窗口机制、三种可靠传输协议、各种MAC协议、HDLC协议和PPP协议,特别是CSMA/CD协议和以太网帧格式,以及局域网的争用期和最小帧长的概念、二进制指数退避算法。中继器、网卡、集线器、网桥和局域网交换机的原理及区别要重点掌握。
3.4流量控制与可靠传输机制 3.4.1流量控制、可靠传输与滑动窗口机制 流量控制涉及对链路上的帧的发送速率的控制,以使接收方有足够的缓冲空间来接收每个帧。例如在面向帧的自动重传请求系统中,当待确认帧的数量增加时,有可能超出缓冲存储空间而造成过载。流量控制的基本方法是由接收方控制发送方发送数据的速率,常见的方式有如下两种:
1.停止-等待流量控制基本原理 发送方每发送一帧,都要等待接收方的应答信号,才能发送下一帧;接收方每接收一帧,都要反馈一个应答信号,表示可接收下一帧。每次只发送一帧就要等待确认,传输效率很低。
2.滑动窗口流量控制基本原理 任意时刻,发送方都维持一组连续的允许发送的帧的序号,称发送窗口;同时接收方也维持一组连续的允许接收帧的序号,称接收窗口。发送窗口用来对发送方进行流量控制,而发送窗口的大小 WT代表在还未收到对方确认信息的情况下发送方最多还可以发送多少个数据帧。同理,接收端的接收窗口控制接收哪些数据帧。接收方收到的数据帧的序号落入接收窗口内时,才能接收,否则丢弃。
发送端每收到一个确认帧,发送窗口就向前滑动一个帧的位置,当发送窗口内没有可以发送的帧(即窗口内的帧全部是已发送但未收到确认的帧)时,发送方就会停止发送,直到收到接收方发送的确认帧使窗口移动,窗口内有可以发送的帧后,继续发送。
接收端收到数据帧后,将窗口向前移动一个位置,并发回确认帧,若收到的数据帧落在接收窗口之外,则一律丢弃。
滑动窗口有以下重要特性:
只有接收窗口向前滑动时,发送窗口才有可能向前滑动。从发送窗口的概念看,其他协议只在发送窗口和接收窗口上有差别:停止-等待协议是发送和接收窗口大小都为1;后退N帧协议是发送窗口大于1,接收窗口大小为1;选择重传协议是发送和接收窗口的大小都大于1.接收窗口的大小为1时,可保证帧的有序接收。数据链路层的滑动窗口协议中,窗口的大小在传输过程中是固定的。 3.可靠传输机制 数据链路层的可靠传输通常使用确认和超时传输两种机制来完成。确认是一种无数据的控制帧,这种控制帧使得接收方可以让发送方知道哪些内容被正确接收。有时为提高传输效率,将确认捎带在一个回复帧中,称为捎带确认。超时重传是指发送方在发送某个数据帧后就开启一个计时器,在一定时间内如果没有得到发送的数据帧的确认帧,那么就重新发送该数据帧,直到发送成功为止。
自动重传请求(Auto Repeat reQuest,ARQ)通过接收方请求发送方重传出错的数据帧来恢复出错的帧,是通信中用于处理信道所带来差错的方法之一。传统自动重传请求分为三种,即停止-等待(Stop-and-Wait)ARQ、后退N帧(Go-Back-N)ARQ和选择性重传(Selective Repeat)ARQ。后两种协议是滑动窗口技术与请求重发技术的结合,由于窗口尺寸开到足够大时,帧在线路上可以连续地流动,因此又称其为连续ARQ协议。在数据链路层中流量控制机制和可靠传输机制是交织在一起的。现有的实际有线网络的数据链路层很少采用可靠传输。
3.4.2单帧滑动窗口与停止-等待协议 在停止-等待协议中,源站发送单个帧后必须等待确认,在目的站的回答到达源站之前,源站不能发送其他的数据帧。从滑动窗口机制的角度看,停止-等待协议相当于发送窗口和接收窗口大小均为1的滑动窗口协议。
在停止-等待协议中,除数据帧丢失外,还可能出现以下两种差错。
到达目的站的帧可能已遭破坏,接收站利用前面讨论过的差错检测技术检出后,简单地将该帧丢弃。为了对付这种情况,源站装备了计时器。在一个帧发送之后,源站等待确认,如果计时器计满时仍未收到确认,那么再次发送相同的帧。如此重复,直到该数据帧正确到达。
数据帧正确而确认帧被破坏,此时接收方已收到正确的数据帧,但发送方收不到确认帧,就会重传数据帧,接收方收到重复的数据帧后会丢弃,并重传一个该帧对应的确认帧。发送的帧交替地用0和1来标识,肯定确认分别用ACK0和ACK1来表示,收到的确认有误时,重传已发送的帧。对于停止-等待协议,由于每发送一个数据帧就停止并等待,因此用1bit来编号就足够。在停止-等待协议中,若连续出现相同发送序号的数据帧,表明发送端进行了超时重传。连续出现相同序号的确认帧时,表明接收端收到了重复帧。
为了超时重发和判定重复帧的需要,发送方和接收方都要设置一个帧缓冲区。发送端在发送完数据帧时,必须在其发送缓存中保留此数据帧的副本,才能在出错时进行重传。只有收到对方发来的确认帧ACK时,方可清除此副本。
停止-等待协议通信信道的利用率很低,为克服这一缺点,就产生了后退N帧协议和选择重传协议。
3.4.3多帧滑动窗口与后退N帧协议(GBN) 在后退N帧式ARQ中,发送方无须在收到上一个帧的ACK后才能开始发送下一帧,而是可以连续发送帧。当接收方检测出失序的信息帧后,要求发送方重发最后一个正确接收的信息帧之后的所有未被确认的帧。当发送方发送了N个帧后,若发现该N个帧的前一个帧在计时器超时后仍未返回其确认信息,则该帧被判为出错或丢失,此时发送方就不得不重传该出错帧及随后的N个帧。即接收方只允许按顺序接收帧。
源站向目的站发送数据帧。当源站发完0号帧后,可以继续发送后续的1、2号帧等。源站每发送完一帧就要为该帧设置超时计时器。由于连续发送了许多帧,所以确认帧必须要指明是对哪一帧进行确认。为了减少开销,GBN协议还规定接收端不一定每收到一个正确的数据帧就必须立即发回一个确认帧,而可以在连续收到好几个正确的数据帧后,才对最后一个数据帧发确认消息,或者可在自己有数据要发送时才将对以前正确收到的帧加以捎带确认。即对某一数据帧的确认就表明该数据帧和此前所有的数据帧均已正确无误地收到。ACKn表示对第n号帧的确认,表示接收方已正确收到第n号帧及以前的所有帧,下一次期望收到第n+1号帧(也可能是第0号帧)。接收端只按序接收数据帧。接收端对有差错帧后面的正确帧也要丢弃,而且重复发送已发送的最后一个确认帧ACK1,以防止它丢失了。
后退N帧协议的接收窗口为1,可以保证按序接收数据帧。若采用n比特对帧编号,则其发送窗口的尺寸WT应满足大于等于1且小于等于2的n次方-1。若发送窗口的尺寸大于2的n次方-1,则会造成接收方无法分辨新帧和旧帧。
后退N帧协议因连续发送数据帧而提高了信道的利用率,但在重传时又必须把原来已发送正确的数据帧重传,又使传送效率降低。若信道传输质量很差导致误码率较大时,它不一定优于停止-等待协议。
3.4.4多帧滑动窗口与选择重传协议(SR) 选择重传ARQ协议,为进一步提高信道的利用率,可设法只重传出现差错的数据帧或计时器超时的数据帧,但此时必须加大接收窗口,以便先收下发送序号不连续但仍处在接收窗口中的那些数据帧。等到所缺序号的数据帧收到后再一并送交主机。
在选择重传协议中,每个发送缓冲区对应一个计时器,当计时器超时时,缓冲区的帧就会重传。另外,该协议使用了比上述其他协议更有效的差错处理策略,即一旦接收方怀疑帧出错,就会发一个否定帧NAK给发送方,要求发送方对NAK中指定的帧进行重传。
选择重传协议的接收窗口尺寸WR和发送窗口尺寸WT都大于1,一次可以发送或接收多个帧。若采用n比特对帧编号,为了保证接收方向前移动窗口后,新窗口序号与旧窗口序号没有重叠部分,需要满足条件:接收窗口WR + 发送窗口WT 小于等于2^n。假定仍然采用累计确认的方法,并且接收窗口要小于发送窗口,否则无意义,即接收窗口尺寸不应超过序号范围的一半。一般情况下在SR协议中,接收窗口和发送窗口的大小是相同的。
选择重传协议可避免重复发送那些本已正确到达接收端的数据帧,但在接收端要设置具有相当容量的缓冲区来暂存那些未按序正确收到的帧。接收端不能接收窗口下界以下或窗口上界以上的序号的帧,因此所需缓冲区的数目等于窗口的大小,而不是序号数目。
信道效率(信道利用率),从时间角度定义,信道效率是对发送方而言的,是指发送方在一个发送周期的时间内,有效地发送数据所需要的时间占整个发送周期的比率。
发送方从开始发送数据到收到第一个确认帧为止,称为一个发送周期T,发送方在这个周期内共发送L比特的数据,发送方的数据传输速率为C,则发送方用于发送有效数据的时间是L/C,信道利用率为(L/C)/T
信道吞吐率=信道利用率x发送方的发送速率
流量控制的三种滑动窗口协议的信道利用率是一个关键知识点。
嘿,这篇文章是不是发布很快,没想到吧。
承接上文,本节文章对代码进行分析。
先对Tensorrt的流程进行一些简要的介绍,后续如果有必要会单开一节进行详细说明。
Tensorrt的加速步骤可分为三步:
1、tensorrt api定义一个网络或者pytorch–>onnx导出模型
2、制定一个容器builder用于生成engine。builder负责指定engine所需内存大小等。
3、使用builder生成engine。engine可用于tensorrt的推理模式。
注:无论是tensorrt直接编写model还是onnx转换的model都需要构建一个builder,然后从builder中生成.engine文件。仔细观察.onnx—>.engine这部分转换代码其实就是一个读取onnx的参数,构建builder,并生成engine。
备:.onnx—>.engine有多种方式,例如NVIDIA官方提供的trtexec脚本,或者使用代码编写相关步骤,如https://github.com/noahmr/yolov5-tensorrt/tree/main/examples/builder noahmr大佬所写代码,大家有兴趣可以去看一看。(有没有哪位同学想了解下,俺也可以写篇文章分析下onnx—>builder—>engine中的步骤及各个参数的意义。嘿嘿)
builder用于生成engine的步骤被称为序列化(serialize),engine可用于tensorrt的推理模式时的操作被称为反序列化(deserialize),序列化的目的主要起到转换模型使其适用于Tensorrt的框架,并可以将转换后engine保存,用于后续推理。(序列化常常会花费一段时间大约几分钟,所以在真正使用时我们常常先生成engine保存文件,以后在推理时可以直接调用。不同的机器通常需要在本地生成相应的engine文件,毕竟调用的硬件或者库文件位置可能不同,相关优化也就不一样)
首先我们先对结构进行分析,建立一个YoloLayerPlugin类。直接两大件先上手。
class API YoloLayerPlugin : public IPluginV2IOExt { public: YoloLayerPlugin(int classCount, int netWidth, int netHeight, int maxOut, const std::vector<Yolo::YoloKernel>& vYoloKernel); //YoloLayerPlugin(const void* data, size_t length); ~YoloLayerPlugin(); /*...*/ } 然后一步步复写IPluginV2IOExt这类,我们需要对这个类中的所有virtual函数复写。从上到下
IPluginV2IOExt–>IPluginV2Ext–>IPluginV2一点点分析。下面是我们必须要复写的函数
getPluginType:获取Plugin的名字
getPluginVersion:获取Plugin的版本
getNbOutputs:获取layer的输出个数
getOutputDimensions:获取layer的输出维度
initialize:在执行时初始化layer,当engine生成时调用
terminate:释放初始化layer时的系统资源。当engine销毁时调用
getWorkspaceSize:获取layer所需空间大小。这个函数与getSerializationSize不同,它是指除数据和参数外分配给layer的额外空间。
enqueue:执行layer的一系列操作。返回值0,1代表执行是否成功。(layer数据操作都包含在这个函数内)
getSerializationSize:返回序列化反冲区的大小,即分配给model的空间大小,后续会有空间计算的详细说明与示例。
serialize:序列化一个layer
destroy:销毁plugin的资源。
clone:复制plugin或layer。在项目我们常常会多次使用一个plugin,例如conv等模块。
setPluginNamespace:设置plugin的命名空间。
getPluginNamespace:获取plugin的命名空间。
getOutputDataType:获得输出数据类型。
isOutputBroadcastAcrossBatch:如果输出张量在批次中广播,则返回 true。
canBroadcastInputAcrossBatch:如果plugin可以使用跨批次广播而无需复制的输入,则返回 true。
attachToContext:将plugin附加到执行上下文并授予plugin对某些上下文资源的访问权限。
detachFromContext:将插件对象从其执行上下文中分离出来。
configurePlugin:配置layer。传达输入和输出的数量、所有输入和输出的维度和数据类型、所有输入和输出的广播信息、选择的插件格式和最大批量大小。此时,插件设置其内部状态并为给定配置选择最合适的算法和数据结构。
supportsFormatCombination:判断plugin是否支持输出的数据类型。
class IPluginV2 virtual AsciiChar const* getPluginType() const noexcept = 0; virtual AsciiChar const* getPluginVersion() const noexcept = 0; virtual int32_t getNbOutputs() const noexcept = 0; virtual Dims getOutputDimensions(int32_t index, Dims const* inputs, int32_t nbInputDims) noexcept = 0; virtual bool supportsFormat(DataType type, PluginFormat format) const noexcept = 0; virtual int32_t initialize() noexcept = 0; virtual void terminate() noexcept = 0; virtual size_t getWorkspaceSize(int32_t maxBatchSize) const noexcept = 0; virtual int32_t enqueue(int32_t batchSize, void const* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) noexcept = 0; virtual size_t getSerializationSize() const noexcept = 0; virtual void serialize(void* buffer) const noexcept = 0; virtual void destroy() noexcept = 0; virtual IPluginV2* clone() const noexcept = 0; virtual void setPluginNamespace(AsciiChar const* pluginNamespace) noexcept = 0; virtual AsciiChar const* getPluginNamespace() const noexcept = 0; class IPluginV2Ext : public IPluginV2 virtual nvinfer1::DataType getOutputDataType( int32_t index, nvinfer1::DataType const* inputTypes, int32_t nbInputs) const noexcept = 0; virtual bool isOutputBroadcastAcrossBatch( int32_t outputIndex, bool const* inputIsBroadcasted, int32_t nbInputs) const noexcept = 0; virtual bool canBroadcastInputAcrossBatch(int32_t inputIndex) const noexcept = 0; virtual void attachToContext(cudnnContext* /*cudnn*/, cublasContext* /*cublas*/, IGpuAllocator* /*allocator*/) noexcept {} virtual void detachFromContext() noexcept {} class IPluginV2IOExt : public IPluginV2Ext virtual void configurePlugin( PluginTensorDesc const* in, int32_t nbInput, PluginTensorDesc const* out, int32_t nbOutput) noexcept = 0; virtual bool supportsFormatCombination( int32_t pos, PluginTensorDesc const* inOut, int32_t nbInputs, int32_t nbOutputs) const noexcept = 0; ok,也不是很多嘛(/(ㄒoㄒ)/~~),其中有些类函数只是起到一些查询功能,在现阶段,我们可以直接最简化一个括弧即可。
环境搭建 Ubuntu 20.04开启外设摄像头截图命令:
ffmpeg -f video4linux2 -s 640x480 -i /dev/video0 -ss 0:0:2 -frames 1 /data/ffmpeg-4.2.7/exe_cmd/tmp/out2.jpg 参数解析 int main(int argc, char **argv) { .................................; #if CONFIG_AVDEVICE avdevice_register_all(); #endif avformat_network_init(); show_banner(argc, argv, options); //1. 重点看下参数解析 ffmpeg_parse_options(argc, argv); //2. 这里面进行具体的数据处理 if (transcode() < 0) exit_program(1); } .................................; } int ffmpeg_parse_options(int argc, char **argv) { OptionParseContext octx; uint8_t error[128]; int ret; memset(&octx, 0, sizeof(octx)); /* split the commandline into an internal representation */ split_commandline(&octx, argc, argv, options, groups, FF_ARRAY_ELEMS(groups)); /* apply global options */ parse_optgroup(NULL, &octx.
一、TC原理介绍 Linux操作系统中的流量控制器TC(Traffic Control)用于Linux内核的流量控制,主要是通过在输出端口处建立一个队列来实现流量控制。
Linux流量控制的基本原理如下图所示。
接收包从输入接口(Input Interface)进来后,经过流量限制(Ingress Policing)丢弃不符合规定的数据包,由输入多路分配器(Input De-Multiplexing)进行判断选择:如果接收包的目的是本主机,那么将该包送给上层处理;否则需要进行转发,将接收包交到转发块(Forwarding Block)处理。转发块同时也接收本主机上层(TCP、UDP等)产生的包。转发块通过查看路由表,决定所处理包的下一跳。然后,对包进行排列以便将它们传送到输出接口(Output Interface)。一般我们只能限制网卡发送的数据包,不能限制网卡接收的数据包,所以我们可以通过改变发送次序来控制传输速率。Linux流量控制主要是在输出接口排列时进行处理和实现的。
Linux的网络流控,控发不控收 , 所以只能对产生瓶颈网卡处的发包速率进行控制.
二、TC规则 1、流量控制方式
流量控制包括以下几种方式:
SHAPING(限制)
当流量被限制,它的传输速率就被控制在某个值以下。限制值可以大大小于有效带宽,这样可以平滑突发数据流量,使网络更为稳定。shaping(限制)只适用于向外的流量。
SCHEDULING(调度)
通过调度数据包的传输,可以在带宽范围内,按照优先级分配带宽。SCHEDULING(调度)也只适于向外的流量。
POLICING(策略)
SHAPING用于处理向外的流量,而POLICIING(策略)用于处理接收到的数据。
DROPPING(丢弃)
如果流量超过某个设定的带宽,就丢弃数据包,不管是向内还是向外。
2、流量控制处理对象
流量的处理由三种对象控制,它们是:qdisc(排队规则)、class(类别)和filter(过滤器)。
QDISC(排队规则)
QDisc(排队规则)是queueing discipline的简写,它是理解流量控制(traffic control)的基础。无论何时,内核如果需要通过某个网络接口发送数据包,它都需要按照为这个接口配置的qdisc(排队规则)把数据包加入队列。然后,内核会尽可能多地从qdisc里面取出数据包,把它们交给网络适配器驱动模块。最简单的QDisc是pfifo它不对进入的数据包做任何的处理,数据包采用先入先出的方式通过队列。不过,它会保存网络接口一时无法处理的数据包。
CLASS(类)
某些QDisc(排队规则)可以包含一些类别,不同的类别中可以包含更深入的QDisc(排队规则),通过这些细分的QDisc还可以为进入的队列的数据包排队。通过设置各种类别数据包的离队次序,QDisc可以为设置网络数据流量的优先级。
FILTER(过滤器)
Filter(过滤器)用于为数据包分类,决定它们按照何种QDisc进入队列。无论何时数据包进入一个划分子类的类别中,都需要进行分类。分类的方法可以有多种,使用fileter(过滤器)就是其中之一。使用filter(过滤器)分类时,内核会调用附属于这个类(class)的所有过滤器,直到返回一个判决。如果没有判决返回,就作进一步的处理,而处理方式和QDISC有关。需要注意的是,filter(过滤器)是在QDisc内部,它们不能作为主体。
3、操作原理
类(Class)组成一个树,每个类都只有一个父类,而一个类可以有多个子类。某些QDisc(例如:CBQ和HTB)允许在运行时动态添加类,而其它的QDisc(例如:PRIO)不允许动态建立类。允许动态添加类的QDisc可以有零个或者多个子类,由它们为数据包排队。此外,每个类都有一个叶子QDisc,默认情况下,这个叶子QDisc使用pfifo的方式排队,我们也可以使用其它类型的QDisc代替这个默认的QDisc。而且,这个叶子叶子QDisc有可以分类,不过每个子类只能有一个叶子QDisc。
当一个数据包进入一个分类QDisc,它会被归入某个子类。我们可以使用以下三种方式为数据包归类,不过不是所有的QDisc都能够使用这三种方式。
tc过滤器(tc filter)
如果过滤器附属于一个类,相关的指令就会对它们进行查询。过滤器能够匹配数据包头所有的域,也可以匹配由ipchains或者iptables做的标记。
服务类型(Type of Service)
某些QDisc有基于服务类型(Type of Service,ToS)的内置的规则为数据包分类。
skb->priority
用户空间的应用程序可以使用SO_PRIORITY选项在skb->priority域设置一个类的ID。
树的每个节点都可以有自己的过滤器,但是高层的过滤器也可以直接用于其子类。
如果数据包没有被成功归类,就会被排到这个类的叶子QDisc的队中。相关细节在各个QDisc的手册页中。
4、命名规则
所有的QDisc、类和过滤器都有ID。ID可以手工设置,也可以有内核自动分配。ID由一个主序列号和一个从序列号组成,两个数字用一个冒号分开。
QDISC
一个QDisc会被分配一个主序列号,叫做句柄(handle),然后把从序列号作为类的命名空间。句柄采用象10:一样的表达方式。习惯上,需要为有子类的QDisc显式地分配一个句柄。
类(CLASS)
在同一个QDisc里面的类分享这个QDisc的主序列号,但是每个类都有自己的从序列号,叫做类识别符(classid)。类识别符只与父QDisc有关,和父类无关。类的命名习惯和QDisc的相同。
过滤器(FILTER)
过滤器的ID有三部分,只有在对过滤器进行散列组织才会用到。
5、单位
tc命令的所有参数都可以使用浮点数,可能会涉及到以下计数单位。
1》带宽或者流速单位:
kbps 千字节/秒
mbps 兆字节/秒
kbit KBits/秒
mbit MBits/秒
🧑🎓个人介绍:大二软件生,现学JAVA、Linux、MySQL、算法 💻博客主页:渡过晚枫渡过晚枫
👓系列专栏:[编程神域 C语言],[java/初学者],[蓝桥杯]
📖阶段目标:备战2023蓝桥杯java个人赛
👻不能逃避,不能逃避,不能逃避,去成为更好的自己!👻
目录
第一阶段:安装jdk并配置环境变量
第一步:安装jdk
第二步:配置环境变量
第二阶段:打开eclipse导入并打开项目
第一阶段:安装jdk并配置环境变量 第一步:安装jdk 在非系统盘新建一个叫“java”的文件夹,并在其中新建名为“jdk”和"jre"的子文件夹。
在资料文件夹中找到这个jdk1.8的文件,双击打开:
将安装路径 更改至刚才创建的“jdk目录”下,并点击下一步。
在选择目标文件夹时,将路径更改至刚才创建的“jre目录”下,并点击下一步,即可完成安装。
第二步:配置环境变量 接下来开始配置java运行环境,在搜索栏中输入“编辑系统环境变量”,点击进入。
在【高级】目录的下方点击右下角的【环境变量】。
点击【系统变量(S)】下方的新建,
【变量名】为:JAVA_HOME,【变量值】则是jdk的安装路径,可通过左下角的浏览目录进行寻找,或者直接复制路径。
双击【系统变量(S)】中的【path】,在【编辑环境变量】的菜单中选定右侧菜单栏下方的【编辑文本】,将以下值粘贴到变量值的最后(注:是添加至最后)。
;%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin
依照前方的方法,创再次建一个系统变量。
【变量名】为CLASSPATH,【变量值】为
.;%JAVA_HOME%\lib;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar
Win+R,打开【运行】,输入cmd,打开【dos系统】。
在其中输入javac,显示如下内容,则说明java可以正常运行了。
在其中输入javac -version,可以显示当前jdk的【版本号】,至此,第一阶段正式结束。
第二阶段:打开eclipse导入并打开项目 在资料文件夹中找到eclipse的压缩包并将其解压到除C盘外的任意一个硬盘中。
双击打开其下的eclipse.exe文件。
在选择工作区目录(Select a directory as workspace)时,点击右侧的【Browse】,选择之前创建的"jdk"目录,并点击【Launch】。
等待片刻后,出现欢迎界面。
点击左上角的【file】选项,并选择【import】,导入我们需要的项目。
在选择导入向导(Select an import wizard)时,
点击【General】文件夹,并选中其下的【Existing Projects into Workspace】文件夹,下一步。
在选择根目录( Select root directory) 时,点击右侧的【Browse】选项,找到我们所需要导入的文件,完成后点击【finish】。
在导入完成后,点击左侧菜单栏中的第二个选项【Java EE】。
点击上方菜单栏中间那个绿色的【Run】选项,即可成功打开游戏。
在进入游戏后,点击左上角菜单中的开始选项,即可游玩游戏。
只有左右两个方向,空格发射导弹。
系统语言设置的问题,改为汉语即可
设计要求:
1、数字密码锁能够设置4位数字密码;
2、输入密码正确显示”ON”,输入错误显示”OFF”;
3、密码初始值为“8888”;
4、输入原密码正确可以修改密码。
key0-9按键 表示密码0-9
key16为输入密码复位按键
key15为输入新密码确定按键
部分代码如下: 全部代码私聊
#include <REGX52.H>
typedef unsigned int u16; //对系统默认数据类型进行重定义
typedef unsigned char u8;
#define KEY_MATRIX_PORT P3 //使用宏定义矩阵按键控制口 #define SMG_A_DP_PORT P0 //使用宏定义数码管段码口
//共阳极数码管显示0~9 N F 的段码数据
u8 nixie[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x37,0x71};
unsigned int Code = 0;//用于保存密码的变量
unsigned int TempCode = 0;//用于暂时保存密码的变量
unsigned int NewCode = 0;//用于保存新密码的变量
unsigned int LNew=0,New=0;//用于判断是否开始录入新密码
unsigned int Newlate=0;//用于确定是否新密码录入结束
void wei(unsigned char pizz);//用于显示第几个数码管的函数声明
unsigned int key=0;//保存按键按下的位数
unsigned int T=0;//密码输入正确判断
unsigned int TLL=0;//密码输入正确判断
一、2023年软件测试行业的现状 2020年开年,一不小心,【新冠】黑天鹅从头上飘过,持续影响全国乃至全球的经济,软件行业公司也迎来了不少的冲击,那么一个值得打算入行软件测试行业,或者已经在软件测试行业耕耘多年的老司机来说,2023年软件测试行业,还值得入行吗?这个行业将来又将如何发展呢?下面作为一个十年资深测试,我谈谈我个人的看法。
二、2023年软件测试行业的趋势 鉴于笔者从13年入行IT行业,经历了只有开发没有测试的阶段,经历了14年只要会基本的功能测试在一线就能薪资过万的阶段,经历了17年只要会一点自动化,会一点性能就能蒙骗过面试官的阶段,更经历了19年所有面试官对于求职者的要求逐步提高,开始思考这个求职者当前具备的技能是否值得培养,工作态度,技能背景是否能够快速满足岗位需求的阶段,那么2023年软件测试行业又将是一个怎样的趋势呢?
相信从10年的市场变化,不难猜出2023年技术薄弱的测试工程师会逐渐被淘汰出局. 一波测试工程师的失业潮是在所难免的
虽然笔者我也一直在呼吁身边的朋友赶紧脱离落后的测试技能, 赶紧走入前沿的技术领域,但是看到很多人还是在原地踏步,他们生活越来越艰难, 工作也是犹如鸡肋,行尸走肉,得过且过也是挺着急的。从主流的招聘网站也能看出来初级测试岗位在减少,但是高级测试工程师的需求反而越来越多,职位薪资也是越来越高, 由此看出测试行业在向正规化,国际化发展。测试行业的门槛在提高。
互联网唯一不变的就是无时无刻不在变化
以往的深刻教训难道还不够多吗?诺基压是如何退出舞台的,苹果,头条,某多多崛起的速度用了几年?仔细算算貌似仅仅3年。摩拜女神仅仅用了三年的时间收获10个忆功成身退,连中石化都变成全国连锁买菜了。互联网还有什么是做不到的?只有你想不到没有做到。软件测试行业更是在飞速发展。
三、只会功能测试的测试工程师已经是新时代的测试文盲 随着测试工程师技能的提升,甚至很多的开发人员都开始转入测试岗位,那么对于还处在功能测试阶段的人来说,第一个是工作上已经没有太大的晋升空间,第二个是也很难跳槽. 最好的结果是凭借多年的经验转管理。我跟行业的很多测试经理交流过, 大部分工作超过6年的人, 在测试执行上会倦怠, 在测试技术的改进上已经无法入门, 还不如招实习生。
所以未来测试人员肯定是功能测试,自动化测试,性能测试等为一体的全能型人才。
四、 测试行业的薪资在提高 测试行业经过自身的净化洗涤会有新生. 典型的变化就是薪资从以前的3k-10k的范围, 整体提升到1w-3w之间。甚至在BAT等一线资深大佬已经年过50-100万。市场对于测试人员技术含量的要求, 责任的要求必然会带来整体的回报. 现在只要技术好,工作3年拿个两三万的月薪已经不再是遥不可及,甚至已经很正常。
五、独行踽近,众行致远!只有抱团才能取暖,欢迎加入软件测试技术群:695458161 共同进步,共同成长!
六、那么2023年作为一名软件测试工程师需要具备哪些技能呢? 初级测试工程师:月薪6-15K之间。必须掌握的技能:
1.软件测试理论、测试流程、需求分析、测试用例的设计、编写、评审,bug工具的使用等。
2.版本控制工具SVN,Git的使用。
3.掌握Linux操作系统的基本使用
4.掌握数据库Mysql或者Oracle的使用,包括增删改查,多表查询,子查询,联合查询等。
5.掌握接口测试工具的使用:如postman,jmeter等,以及抓包工具Fiddler的使用
6.掌握基本的python和自动化脚本的编写
7.掌握基本的性能测试。
8.掌握基本的App专项测试。
如果上面这些核心技术你都掌握了!那么恭喜你可以去寻找一份功能测试的工作了!那么这时候你就要思考第二个问题:如何升职?如何获取更高的薪资?
中级测试工程师:月薪15-30K之间。必须掌握的技能:
这个阶段你必须搞定自动化测试或者性能测试其中一项,能够独当一面!
对于性能测试来说,业界很多都是在“耍流氓”,性能测试普遍存在的现象如下:
只测不调,无法给出研发和运维人员执行建议
无法定位问题,缺乏清晰的逻辑和数据证明价值
性能测试工具≠性能测试
性能测试技术体系落后,急需拥抱开源软件
这些问题的出现,其实就是反映出了目前在做性能测试的现状:所以在这里笔者建议先从自动化发现发展更有利。
那么自动化需要具备的技能如下:
UI自动化:python或java熟悉一门语言,selenium,unittest,htmltestrunner,ddt数据驱动,PO模式,UI自动化框架等。
接口自动化:requests,pytest,allure,jenkins持续集成,接口自动化框架,日志处理,错误截图等
App自动化:appium,uiautomatar,各种定位,sdk,adb命令等
高级测试工程师:年薪50-100万之间。必须掌握的技能:
1、精通专项测试:自动化测试方向、性能测试方向、安全测试方向、测试架构建设等。
2、具备一定的管理能力:测试计划、进度跟进、任务分配、沟通、协调能力;
3、具备优化测试流程的能力:可以根据不同公司不同部门不同系统的特点,进行测试流程规划、设计和优化的能力;
4、业务能力:对所在行业的业务知识有深刻的认识和理解;
5、认知水平:了解行业发展趋势和前沿动态,并可以先进的理念、技术成功落地能力;
七、总结 如果你觉得此文对你有帮助,如果你对软件测试、接口测试、自动化测试、面试经验交流感兴趣点击下面小卡片:
使用php来连接sqlite数据库,Windows 用户必须启用 php_sqlite3.dll 才能使用该扩展。关于如何启用 php_sqlite3.dll,可以参考未检测到您服务器环境的sqlite3数据库扩展。
连接数据库 以下代码显示了如何连接到一个现有的数据库。
<?php class MyDB extends SQLite3 { function __construct() { $this->open('test.db'); } } $db = new MyDB(); if(!$db){ echo $db->lastErrorMsg(); } else { echo "Opened database successfully\n"; } ?> 我们将这个文件命名为test.php,并将test.php和test.db放入到phpstudy的www目录下,在浏览器下输入localhost/test.php,会得到如下消息:
Opened database successfully
INSERT操作 class MyDB extends SQLite3 { function __construct() { $this->open('tests.db'); //打开tests.db数据库 } } $db = new MyDB(); // 创建db对象, if(!$db){ echo $db->lastErrorMsg(); //若db对象为空,则连接失败 } else { echo "Opened database successfully\n"; } $sql =<<<EOF INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) VALUES (9, 'Mark', 25, 'Rich-Mond ', 65000.
博主电脑配置是AMD R5 3600,Nvidia RTX3060 12G,16G 3200MHz内存,训练数据集是自建数据集,大约1200张图片,3个检测目标。
训练YOLOv5-5.0版本的模型参数设置,模型是yolov5s,epoch 150(如果想要更好的mAP@0.5:0.95指标可以设置的更大,博主这个收敛的太快了就没设太多),batchsize 32或者64,imgsize 640,640,其他均为默认。
在最初训练模型的时候训练一次epoch大约需要3分钟,而且不管怎么设置参数,训练时间都不会改变太多,都在3分钟左右,博主期初是因为自己没用GPU跑,但是显存是实实在在的吃满了,但利率用始终在0%和4%之间波动,大多数还是在0%,博主百思不得其解,百度了许多方法都没有用,看了大部分博主的文章,然后看了看运行时候的内存,CPU和GPU,发现CPU使用率不高,但是内存和显存占用很高,那么也就排除了在用CPU跑模型的情况,思来想去大概是出在数据读取上。下面来介绍一些训练模型速度慢可能的原因和解决方法:
1.GPU太差 博主之前用的自己的GTX960m的确跑不起来,如果自己显卡太老,显存太低,要么降低batchsize和worker,还有imgsize,要么就换一张显卡。三个参数的设置在train.py的设置代码如图
2.没用到GPU,完全是CPU在跑 这种情况也很好发现,只需要看看显存是否被占用或者有没有安装pytorch和cuda,可以用如下代码来查看是否装了pytorch和cuda,怎么安装很多博主已经介绍过了,随便一查一大堆。
import torch print(torch.__version__) torch.cuda.is_available() 上面代码结果如下图,说明正确安装CUDA和pytorch(记得要版本对应,不然会报错),最好把cudnn也装上。
如果是CPU在训练,可以设置下面的代码来更换GPU训练模型,default设置成你GPU的编号就行,单显卡就是0。
3.训练瓶颈 3.1 CPU瓶颈 CPU瓶颈很好判断,那就是CPU占用率很高,同时GPU也在使用,这说明电脑CPU太差了。设置参数,尽量不让CPU占用率超过85%。
3.2 内存瓶颈
简单来说就是内存爆了,基本上不存在内存频率跟不上的,同样设置参数,内存占用也尽量不要超过85%-90%。
3.3 IO瓶颈
这种情况会复杂很多,这个IO可能是硬盘读到内存导致的,也有可能是从内存到GPU导致的,也有可能是读写记录的时候导致的,反正杂七杂八的,最难看出哪里出了问题。
对于以上三种瓶颈可以通过瓶颈测试工具来测试出来,具体方法下面的博客说的很清楚。
(24条消息) PyTorch消除训练瓶颈 提速技巧_*pprp*的博客-CSDN博客
对于前两种瓶颈,都可以通过设置batchsize,workers和imgsize来尝试解决,或者更换yolov5的模型,使用最小的yolov5s来训练,如果以上方法解决不了,建议更换硬件或者租用云服务器。对于第三种情况基本只能通过预读取数据和更换硬件来实现。
4.性能过剩 对,你没有看错,性能过剩也会导致Yolov5训练速度过慢,原因也很让人无语,在train.py中有这样一行代码,这行代码旨在将数据读到内存中进行缓存,这样就可以更快的存取数据。
这样读取数据就可以让正在工作的GPU计算完数据后不会因为硬盘读写太慢而等待数据读入,所以硬盘的读取很重要,训练过程中,数据首先从硬盘读入到内存,然后再从内存读取到CPU或者GPU,所以一块好的硬盘可以有效的加速模型的训练,特别是笔记本硬盘,笔记本硬盘读取速度很慢,建议把数据放入SSD中。那么问题来了,如果GPU计算速度超过了从硬盘读入内存,再从内存读入GPU的速度,并且硬盘的读取速度够快,会发生什么,这也是导致博主训练速度太慢的原因。
对于小批量数据(很多自建数据都是小批量数据),一两千张,检测目标数不多,模型不复杂的情况下,对于高端显卡或者专业图形卡都是小case,基本都用不到多少算力,GPU很快就计算完了一个batch的数据,这时候就要等待数据读入,如果硬盘读写够快,数据从硬盘读入内存,GPU再从内存读出,相较于直接从硬盘读入GPU,本来用作缓存的内存反而成了额外的开销,数据绕了远路,IO读取时间反而增大了。其实说白了还是IO问题造成的,只不过原因不是硬件太差。当然,以上内容是博主的猜测,仅供参考。
所以,对于拥有一个好的SSD,好显卡,内存和CPU性能好的计算机而言,在训练数据量小,模型不大的情况下,建议将cacheimages关掉,直接从硬盘读入GPU,将action='store_true'替换为action='store_false'即可,关掉之后博主一次epoch从3分钟变成了20s,提升巨大,而且训练结果没有改变。
总结 大多数情况来说都是性能不足,存在瓶颈的情况,从而导致模型训练时间长,这也是深度学习中常有的事,但是在自己硬件条件好,数据量不大的情况下,模型训练仍然慢,就该考虑下是不是因为性能过剩,在数据存取上花了过多的时间造成的,这表现为内存和显存使用很大,但是CPU和GPU却不怎么占用。经过博主一些测试,博主发现不管有没有用缓存,内存和显存使用都很大,这应该是pytorch底层逻辑的关系,在用GPU跑模型的时候pytorch会自动把数据缓存到显存里,内存使用大小仅仅和batch_size有关。
值得一提的是,虽然训练速度大大增加,但是依然没有解决GPU占用率低的问题,虽然GPU占用率很低,但是不会存在一段时间为0的情况,博主推测,可能是因为数据量太小,所需要检测目标数不多造成计算量太小的原因。博主之前在用VOC2012数据集进行训练时,GPU占用率就不低,但是仍然只占用了30%-60%,不知道使用这种方法有没有效,因为学业问题只能先跑自己数据集,如果有时间博主会更新相关结论。
最后,博主计算机功底不算扎实,如果有问题,希望各位读者指正。
本文为计算机网络系列第三章笔记,陆续会更新余下内容。文章参考:计算机网络微课堂
系列文章:
计算机网络「一」计算机网络概述 ❤ 确定不进来看看?❤
计算机网络「二」物理层 ❤ 多图详解 ❤
需要说明:文章中图片多来自于网络和网课,本文只用作后期学习时参考
一、数据链路层概述 数据链路层在网络体系结构中的地位
主机 H1 给主机 H2 发送数据,中间要经过 3 个路由器和电话网、局域网以及广域网等多种网络。从五层协议原理体系结构角度来看:
为了专注数据链路层内容,这里我们只考虑数据链路层,而不考虑其他层
主机 H1 到 H2 的通信可以看作在 4 个不同链路上组成
链路(LINK)就是从一个结点到相邻结点的一段物理线路,而中间没有任何其他的交换结点 要在链路上传输数据,仅有链路还不够,还需要一些通信协议来控制数据的传输
数据链路(Data Link)是指把实现通信协议的硬件和软件加到链路上,就构成了数据链路 在数据链路层上传输的数据包又称为 帧
数据链路层以 帧 为单位传输和处理数据 数据链路层的三个重要问题
这里先简单了解一下三个问题,后续内容详细介绍
封装成帧
差错检测
可靠传输
说明:这三个问题针对于点对点信道的数据链路层,使用广播信道的数据链路层除了这三个问题外,还有一些其他问题
二、封装成帧 封装成帧 是指数据链路层给上层交付的协议数据单元添加 帧头 和 帧尾 的过程。
帧头和帧尾中包含有重要的控制信息
帧头和帧尾的作用之一就是 帧定界
图中标志的作用就是帧定界 发送方的数据链路层将上层交付下来的协议数据单元封装成帧后,还要通过物理层将构成帧的各比特转换成电信号发送到传输媒体。
那么,接收方的数据链路层如何从物理层交付的比特流中提取一个个的帧呢?
帧定界
如下图,红色字段为帧定界标志
说明:并不是每一种数据链路层协议的帧都包含帧定界标志。例如,以太网 V2 的 MAC 帧:
前导码中的前7个字节为前同步码,作用是使接收方的时钟同步。之后1字节为帧开始定界符,表明后面紧跟的为MAC帧
此外,以太网定义了帧间间隔为 96 比特的发送时间,因此,MAC 帧不需要帧结束定界符
透明传输 透明传输是指数据链路层对上层交付的传输数据没有任何限制,就好像数据链路层不存在一样。
DNS服务器的部署 打开虚拟机后查看已经开放的端口,可以看到没有TCP53、UDP53,说明DNS服务端口没有打开
打开我的电脑—双击CD驱动器—
选择安装可选的Windows组件
选择网络服务—域名系统(DNS)—
点击下一步后会弹出如下弹窗,因为我们要从镜像文件中安装组件,点击浏览查看我们的映像文件是哪个盘,由下图可知,映像文件是E盘,所以我们将文件复制来源改为E:\i386
弹出的两个弹窗都改为E:\i386
完成组件安装
完成后回到桌面,打开运行,输入netstat -an,查看打开的窗口,可以看到TCP53和UDP53两个端口都处于开放状态
下一步,打开我们刚刚安装好的DNS服务器
新建正向查找区域
选择主要区域
设置区域名称
直接点击下一步
选择下一步
点击完成
双击打开正向查找区域的baidu.com区域文件,接下来百度要解析服务器时,都会在右侧空白处新建一条条的解析记录
与父文件夹相同就是没有主机名,A记录就是正向解析记录
我们可以右击空白处,新建一条主机记录,将主机名设置为www
此时可以打开另一台Windows XP虚拟机,打开运行,输入nslookup www.baidu.com,出现如下结果。发现DNS服务的地址不是我们刚刚所设置的1.1.1.1,是因为在TCP/IP属性中,我们选择的是自动获得DNS服务器地址
此时只需要手动配置DNS服务器地址即可。此处的10.1.1.1是第一台虚拟机的IP地址
再次使用nslookup www.baidu.com命令进行域名解析
部署成功
Verdi的使用技巧总结 1,加载信号2,放大缩小3,波形文件保存.rc4,移动波形窗口信号位置5,对信号的操作6,显示信号全路径7,显示状态机及其名称8,改变信号和波形颜色9,参考信号数值10,新建组与对组重命名11,mark标记功能12,重新加载波形或设计文件13,测量信号的两种方式 1,加载信号 选中波形窗口,使用快捷键 g 意为加载信号 Get signals。在弹出的窗口选中需要添加的信号,点击滚轮选择需要添加信号的位置
也可在代码中选中信号 Ctrl+W 将信号加载在波形中
2,放大缩小 Z:Zoom In
z:Zoom Out
f:Zoom All
3,波形文件保存.rc 调出波形,为方便下次使用,按键shift+S可以将波形保存为xxx.rc格式文件,使用波形文件时,按快捷键r打开界面,选择波形。可以避免下次打开重新添加信号
4,移动波形窗口信号位置 使用滚轮在波形窗口选中信号,然后拖到代码窗口即可查看选中波形的代码逻辑
5,对信号的操作 点击波形窗口,选中信号,使用Delete删除信号。
选中信号,使用Ctrl+p复制选中的信号,然后使用insert按键插入,粘贴的位置由黄线所在位置决定。
6,显示信号全路径 快捷键 h
按H显示波形信号全路径,再按H撤销
7,显示状态机及其名称 选中状态机状态的波形,在nWave窗口选中Tools>Bus Contention>打开状态机,(关闭),返回查看波形已经变为状态的名称。
补充:选择具有状态机的模块选中,然后在主工具栏选择Tools>Extract interative FSM
点击OK,然后点击下面的状态机,即可看到状态的跳转。
8,改变信号和波形颜色 按C或者T修改信号或者波形颜色,方便查看
9,参考信号数值 选中Source>Active Annotation即可看到每一个信号,参数的数据显示
10,新建组与对组重命名 将黄线放到最后一行,添加信号即可新建一个信号组。然后鼠标选中右键即可进行相关操作,也可以选中一个模块,直接Ctrl+4即可将接口信号加入波形窗口
11,mark标记功能 为方便波形定位,按键shift+M使用mark功能标记一下,可以自定义名称和颜色,方便查找。
12,重新加载波形或设计文件 选中波形或设计文件 L(shift + i) 重新加载波形或设计文件,这个很方便,在新一次仿真完成之后Roload即可
13,测量信号的两种方式 方法1:使用鼠标调出一个标线黄色的,使用滚轮调出一个mark点白色的。选择你需要测的信号,然后再nwave窗口中选择view-Signal Event Report即可查看选择的信号在两个标线中的沿的个数。方法2:使用鼠标调出一个标线黄色的,使用滚轮调出一个mark点白色的。选择你需要测的信号,使用x即可固定两个标线之间的距离,再次使用x即可解除。
我们平时经常做的是上传文件,上传文件夹与上传文件类似,但也有一些不同之处,这次做了上传文件夹就记录下以备后用。
这次项目的需求:
支持大文件的上传和续传,要求续传支持所有浏览器,包括ie6,ie7,ie8,ie9,Chrome,Firefox,360安全浏览器,并且刷新浏览器后仍然能够续传,重启浏览器(关闭浏览器后再打开)仍然能够继续上传,重启电脑后仍然能够上传
支持文件夹的上传,要求服务端能够保留层级结构,并且能够续传。需要支持10万个以上的文件夹上传。
支持低版本的系统和浏览器,因为这个项目的最终运行环境在政府,政府的配置都一般,职员都是办公用,内存都不大,基本上以Windows XP的系统为主。
1、介绍enctype
enctype 属性规定发送到服务器之前应该如何对表单数据进行编码。
enctype作用是告知服务器请求正文的MIME类型(请求消息头content-type的作用一样)
1、1 enctype的取值有三种
值
描述
application/x-www-form-urlencoded
在发送前编码所有字符(默认)
multipart/form-data
不对字符编码。每一个表单项分割为一个部件
text/plain
空格转换为 “+” 加号,但不对特殊字符编码。
1. 当enctype=’application/x-www-form-urlencoded’
2.当enctype=’multipart/form-data’
通过观察发现这个的请求体就发生了变化。这种请求体被称之为多部件请求体。
什么是多部件请求体:就是把每一个表单项分割为一个部件。
以请求头的content-type的boundary后面的一串随机字符串作为分割标识
普通表单项:
//name的意思是文本框里面name的属性值,而admin是我们输入的文本值
Content-Disposition: form-data; name="username"
admin
文件表单项
//filename的意思是:我们上传的文件名称,content-Type的意思是:MIME类型,asdasdas的意思是:文件里面的内容
Content-Disposition: form-data; name="upload"; filename="a.txt"
Content-Type: text/plain
asdasdas
3. 当enctype=’text/plain’
w3c称:空格会变成”+”加号,但是我这里没有发现,只有当get请求的时候,空格会变成”+”号
进入正题
完成上传需要满足3个必要的条件
提供form表单,method必须是post,因为get请求的传输数据一般为2kb,不同浏览器不一样。
form表单属性enctype的必须是multipart/form-data
提供input type=”file”类的上传输入域
大致实现原理:当enctype的值是multipart/form-data时,浏览器会把每个表单项进行分割,分割成不同的部件,以boundary的值为分割标识,这个标识的字符串是随机生成的,最后一个表单项的分割标识字符串末尾会多两个”- -“,代表结束。服务端用request.getHeader(“content-type”)获取分割字符串,然后进行解析。
代码实现
一、开发环境搭建
准备两个第三方jar包
commons-io包
commons-upload包
所有依赖包
代码实现
<%@ page language="java" import="up6.DBFile" pageEncoding="UTF-8"%>
<%@ page contentType="text/html;charset=UTF-8"%>
一、MOV指令
1、将十六进制0x1234数值,赋值给eax寄存器
mov eax,0x1234 2、将十六进制0x123数值,赋值给内存地址为ebx
mov dword [ebx],0x123 3、将edx的高八位赋值给eax的低八位
ax,eax的低16位,al,eax的低8位,ah,eax的高8位,通力bx,bl,bh为ebx的相关数值
mov al, dh 4、将十六进制0x11数值,复制给内存地址为ebx的第一个字节
mov byte [ebx],0x11 二、MOVSX指令,有符号扩展
当我们执行,mov eax,bl的时候,会提示错误,bl是8位,eax是32位,所以需要扩展,这时需要movsx
movsx eax,bl 将ebx置为0x12345680,再执行movsx eax,bl,会发现eax变成了FFFFFF80
因为有符号4字节最大表示的正数是0x7F,0x80就是表示负数了,所以这里执行的结果不一样。
三、MOVZX指令,无符号扩展
假如用MOVZX就不会出现上面的错误,这里全部表示的是正数
movzx eax, bl 四、LEA指令,赋址操作
1、mov取的内存的值,而lea的指令取的是址,lea后面必须跟的是寄存器,后面是一个内存
lea eax, ds:[ebx+0x5] 对比 mov eax, ds:[ebx+0x5] 五、XCHG指令,交换数值
将ebx的值和eax的值进行交换
xchg ebx, eax