Go-高质量编程与性能调优

news2024/11/17 1:27:12

高质量编程:

什么是高质量:

​ ——编写代码能达到正确可靠,简洁清晰的目标

各种边界条件是否考虑完备

异常情况处理,稳定性保证

易读易维护

编程原则
简单性

消除“多余的复杂性”,以简单清晰的逻辑编写代码

不理解的代码无法修复改进

可读性

代码是写给人看的,而不是机器

编写可维护代码的第一步是确保代码可读

生产力

团队整体工作效率非常重要

编写规范
  • 代码格式

    gofmt: go语言官方提供工具,自动格式化为官方统一风格

    goimports:go语言官方提供工具,实际等于gofmt加上依赖包管理,自动增删依赖包引用,对其排序分类等

  • 注释

    注释应该做的:

    应该解释代码作用

    应该解释代码如何做的

    应该解释代码实现的原因

    应该解释代码什么情况会出错

    应该解释公共符号(公共的变量,常量,函数,不应该取决与代码的长度来选择注释)

    代码是最好的注释,并且注释应该要提供代码未表达出的上下文信息

  • 命名规范

    简介胜于冗长

    for index := 0; index < len(s); index++{//bad
        //do something
    }
    
    for i := 0; i < len(s); i++{ //good
        //do something
    }
    

    缩略词全大写,但当其位于变量开头且不需要导出时,使用全小写

    例如:使用ServeHTTP而不是ServeHttp
    使用XMLHTTPRequest或者xmlHTTPRequest
    

    变量距离其被使用的地方越远,越需要携带更多的上下文信息

    函数名不携带包名的上下文信息,因为包名和函数总是成对出现的

    函数名尽量简短

    对于package来说,只由小写字母组成,不包含大写字母和下划线

    简短并包含一定上下文信息,例如schema,task

    不要与标准库同名

    不使用常用变量名作为包名,例如使用bufo而不是buf

    使用单数不使用复数,例如使用encoding而不是encodings

    谨慎使用缩写

  • 控制流程

    避免嵌套

    if foo{ // bad 
    	return x
    }else{
    	return nil
    }
    
    if foo{ // good
    	return x
    }
    return nil
    

    简单来说差不多少用else -f else的这样以后添加修改方便

    处理逻辑尽量走直线,避免复杂嵌套分支

  • 错误和异常处理

    简单错误:仅出现一次的错误,有限使用errors.New来创建匿名变量

    func ErrorsNew(size int) error {
    	if size > 10 {
    		return errors.New("size bigger , should be less then 10")
    	}
    	return nil
    }
    

    错误的Wrap和Unwrap

    在fmt.Errorf中使用%w关键字将一个错误关联到错误链中

    	list, _ , err := c.GetBytes(cache.Subkey(a.actionID , "srcfiles"))
    	if err != nil{
    		return fmt.Errorf("reading srcfiles list : %w" , err)
    	}
    

    错误判定

    判定一个错误是否为特定错误,使用errors.ls

    不同于使用==,使用该方法可判定错误链上的所有错误是否含有特定错误

    	data, err = lockedfile.Read( targ )
    	if errors.Is(err , fs.ErrNotExist){
    	// Treat non-existent as empty, to bootstrap 
    	//the "latest" filethe first time we connect to a given database.
    		return []byteP{} , nil
    	}
    	return data, err
    }
    

    在错误链上过去特定错误使用errors.AS

    	if _ ,err := os.Open("non-exit"); err != nil{
    		var pathError *fs.PathError
    		if errors.As(err , &pathError){
    			fmt.Println("Failed at path :" , pathError.Path)
    		}else{
    			fmt.Println(err)
    		}
    	}
    

    panic

    不建议在业务代码中使用panic

    调用函数不包含recover会造成程序崩溃

    若问题可以被屏蔽或解决,建议使用error代替panic

    recover

    recover只能被defer的函数中使用

    嵌套无法生效

    只在当前goroutine生效

    defer语句是后进先出

优化与性能测试:

性能优化的前提是满足正确可靠,简洁清晰等质量因素

性能优化是综合评估,有时候时间效率和空间效率可能对立

Benchmark

go语言提供支持基准性能测试的工具

func BenchmarkFib10(b *testing.B) {
	for i := 0; i < 10; i++{
		Fib(10)
	}
}

func Fib(n int)  int {
	if n < 2{
		return n
	}
	return Fib(n - 1) + Fib(n - 2)
}
go test -bench=. -benchmem // 执行命令

在这里插入图片描述

上面第一行-16差不多是cpu核数了

10000… 是执行次数

0.0005517 是每次花费时间

0B 是每次申请内存

0allocs是每次申请几次内存

性能优化建议Slice

预分配

尽可能在使用make初始化切片时提供容量信息

func BenchmarkNoPreAlloc(){
	data := make([]int , 0)
	for k := 0; k < n; k++{
		data = append(data, k)
	}
}

func BenchmarkPreAlloc(){

	data := make([]int , n)
	for k := 0; k < n; k++{
		data = append(data,  k )
	}
}

在这里插入图片描述

大内存未释放

在已有的切片基础上创建切片,不会创建新的底层数组,如下

func Copy(origin []int) []int{
	return origin[3:len(origin)]
}

上述就是想创建个新切片,但是其实底层数组用的是同一个,如果说传入参数在之后不用的,但是因为有个这个返回值只用一段数据却占用这之前的一大块数据,内存就浪费了

另一个问题就是由于工用一个数组,你改变一个切片的值,另一个也就改变了

func main() {
	a := []int{1, 2, 3, 4, 5}
	b := a
	fmt.Println(a) //[1 2 3 4 5]
	fmt.Println(b) //[1 2 3 4 5]
	b[0] = 1000
	fmt.Println(a) //[1000 2 3 4 5]
	fmt.Println(b) //[1000 2 3 4 5]
}

由于切片是引用类型,所以a和b其实都指向了同一块内存地址。修改b的同时a的值也会发生变化。

Go语言内建的copy()函数可以迅速地将一个切片的数据复制到另外一个切片空间中,copy()函数的使用格式如下:

copy(destSlice, srcSlice []T)

其中:

  • srcSlice: 数据来源切片
  • destSlice: 目标切片

举个例子:

func main() {
	// copy()复制切片
	a := []int{1, 2, 3, 4, 5}
	c := make([]int, 5, 5)
	copy(c, a)     //使用copy()函数将切片a中的元素复制到切片c
	fmt.Println(a) //[1 2 3 4 5]
	fmt.Println(c) //[1 2 3 4 5]
	c[0] = 1000
	fmt.Println(a) //[1 2 3 4 5]
	fmt.Println(c) //[1000 2 3 4 5]
}
性能优化建议-Map
const n = 10000000

func BenchmarkNoPreAlloc(b *testing.B) {
	data := make(map[int]int , 0)
	for k := 0; k < n; k++ {
		data[k] = 1
	}
}

在这里插入图片描述

func BenchmarkPreAlloc(b *testing.B) {

	data := make(map[int]int , n)
	for k := 0; k < n; k++ {
		data[k] = 1
	}
}

在这里插入图片描述
可以看出,结果和切片差不多

分析:

​ 不断向map中添加元素操作会触发map扩容

​ 提前分配好空间可以减少内存拷贝和ReHash的消耗

性能优化建议-字符串处理

常见的字符串拼接方式

func plus(n int , str string) string{
	s := ""
	for i := 0; i < n; i++{
		s += str
	}
	return s
}

func StrBulider(n int  , str string)string{
	var builder strings.Builder
	for i := 0; i < n; i++{
		builder.WriteString(str)
	}
	return builder.String()
}

func ByteBuffer(n int , str string)string{
	buf := new(bytes.Buffer)
	for i := 0; i < n; i++{
		buf.WriteString(str)
	}
	return buf.String()
}

使用 + 拼接性能最差,后面俩个差不多,strings.Buffer更快

分析:

​ 字符串在Go语言中是不可变类型,占用内存大小固定

​ 使用 + 每次都会重新分配内存

​ 后面的底层都是 []byte数组,内存扩容策略,不需要每次拼接重新分配内存

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

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

相关文章

《Vue3+Typescript》一个简单的日历组件实现

这是一个没有套路的前端博主&#xff0c;热衷各种前端向的骚操作&#xff0c;经常想到哪就写到哪&#xff0c;如果有感兴趣的技术和前端效果可以留言&#xff5e;博主看到后会去代替大家踩坑的&#xff5e; 主页: oliver尹的主页 格言: 跌倒了爬起来就好&#xff5e; 目录 一、…

带宽管理:知识点概述及防火墙带宽管理实验

目录 一、带宽管理技术介绍 1.1 基本概念&#xff1a; 1.2 带宽管理原理介绍&#xff1a; 1.3 接口带宽原理&#xff1a; 1.4 带宽策略原理&#xff1a; 1.5 带宽通道原理 1.6 带宽复用 二、带宽管理实验 一、带宽管理技术介绍 1.1 基本概念&#xff1a; 带宽管理对通…

hMailServer-5.3.3-B1879.exe

hMailServer-5.3.3-B1879.exe

【转】数据治理简介

原文链接&#xff1a;数据治理怎么做&#xff1f;这篇万字长文终于讲清楚了&#xff01; - 知乎 引言&#xff1a; 股份制改革对我国银行业来说只是一个开始&#xff0c;企业在风险管理、创造价值等方面还有很长的路要走。 风险管理要求提供精准的数据模型、创造价值要求充分…

动态线程池问题的解决

项目中需要将线程池也监控管理起来。 于是决定引入了hippo4j&#xff0c;这个引入很简单&#xff0c;官方的例子也很简单&#xff0c;拿过来直接跑。 出现问题了&#xff0c;用的和例子一模一样的&#xff0c;也没什么错&#xff0c;但是就是在服务器的管理控制台上没有找到动态…

深入浅出指南:Netty开发【NIO核心组件】

目录 ​Netty开发【NIO核心组件】 1.NIO基础概念 2.NIO核心组件 2.1.Channel&&Buffer简介 2.2.Selector 服务器的多线程版本 服务器的线程池版本 服务器的selector版本 2.3.Buffer 0.ByteBuffer的正确使用流程 1.ByteBuffer类型简介 2.ByteBuffer核心属性说…

【Docker】Docker应用部署之Docker容器安装Redis

目录 一、搜索Redis镜像 二、拉取Redis镜像 三、创建容器 四、测试使用 一、搜索Redis镜像 docker search redis 二、拉取Redis镜像 docker pull redis:版本号 # 拉取对应版本的redis镜像 eg: docker pull redis:5.0 三、创建容器 docker run -id --nameredis -p 6379:637…

23款奔驰GLS450加装原厂香氛负离子系统,清香宜人,久闻不腻

奔驰原厂香氛合理性可通过车内空气调节组件营造芳香四溢的怡人氛围。通过更换手套箱内香氛喷雾发生器所用的香水瓶&#xff0c;可轻松选择其他香氛。香氛的浓度和持续时间可调。淡雅的香氛缓缓喷出&#xff0c;并且在关闭后能够立刻散去。车内气味不会永久改变&#xff0c;香氛…

【HDFS】Block、BlockInfo、BlockInfoContiguous、BlockInfoStriped的分析记录

本文主要介绍如下内容: 关于几个Block类之间的继承、实现关系;针对文章标题中的每个类,细化到每个成员去注释分析列出、并详细分析BlockInfo抽象类提供的抽象方法、非抽象方法的功能针对几个跟块组织结构的方法再进行分析。moveBlockToHead、listInsert、listRemove等。一、…

基于STM32设计的门禁照相机

一、项目介绍 当前文章介绍基于STM32设计的门禁照相机&#xff0c;本项目提供了一种更加智能、安全、便捷的门禁解决方案。门禁照相机采用STM32F103ZET6 MCU作为主控芯片&#xff0c;配合2.8寸LCD显示屏、OV7725数字摄像头、SD卡和模拟门铃按键等外设模块&#xff0c;实现了摄…

Pandas进阶修炼120题-第三期(金融数据处理,51-80题)

目录 往期内容&#xff1a;第一期&#xff1a;Pandas基础&#xff08;1-20题&#xff09;第二期&#xff1a;Pandas数据处理&#xff08;21-50题&#xff09; 第三期 金融数据处理51.使用绝对路径读取本地Excel数据方法一&#xff1a;双反斜杠绝对路径方法二&#xff1a;r 拓展…

记录--你不知道的Js高级方法

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 前言 在Js中有一些比较冷门但是非常好用的方法&#xff0c;我在这里称之为高级方法&#xff0c;这些方法没有被广泛使用或多或少是因为存在一些兼容性的问题&#xff0c;不是所有的浏览器都读得懂的。…

Java特殊时间格式转化

平常开发过程当中&#xff0c;我们可能会见到有的日期格式是这样的。 1、2022-12-21T12:20:1608:00 2、2022-12-21T12:20:16.0000800 3、2022-12-21T12:20:16.00008:00下面来说一下这种时间格式怎么转换 第一种&#xff1a;2022-12-21T12:20:1608:00 代码如下&#xff1a; p…

Android ANR触发机制之Service ANR

一、前言 在Service组件StartService()方式启动流程分析文章中&#xff0c;针对Context#startService()启动Service流程分析了源码&#xff0c;其实关于Service启动还有一个比较重要的点是Service启动的ANR&#xff0c;因为因为线上出现了上百例的"executing service &quo…

FFmpeg aresample_swr_opts的解析

ffmpeg option的解析 aresample_swr_opts是AVFilterGraph中的option。 static const AVOption filtergraph_options[] {{ "thread_type", "Allowed thread types", OFFSET(thread_type), AV_OPT_TYPE_FLAGS,{ .i64 AVFILTER_THREAD_SLICE }, 0, INT_MA…

mybatisPlus高级篇

文章目录 主键生成策略介绍AUTO策略INPUT策略ASSIGN_ID策略ASSIGN_UUID策略NONE策略 MybatisPlus分页分页插件自定义分页插件 ActiveRecord模式SimpleQuery工具类SimpleQuery介绍listmapGroup 主键生成策略介绍 主键&#xff1a;在数据库中&#xff0c;主键通常用于快速查找和…

【MySQL】视图(十)

&#x1f697;MySQL学习第十站~ &#x1f6a9;本文已收录至专栏&#xff1a;MySQL通关路 ❤️文末附全文思维导图&#xff0c;感谢各位点赞收藏支持~ 一.引入 视图&#xff08;View&#xff09;是一种虚拟存在的表。视图中的数据并不在数据库中实际存在&#xff0c;行和列数据…

python json保留汉字原始形式,而不是Unicode编码(Unicode码)(加ensure_ascii=False参数)

文章目录 问题解决办法测试 问题 如图&#xff0c;保存汉字的时候变成unicode码了。。。 代码是这样的&#xff1a; 解决办法 在Python中&#xff0c;可以使用json模块的ensure_ascii参数来控制是否将汉字转换为类似\u5730\u9707的Unicode编码。默认情况下&#xff0c;ensure…

会议OA项目之权限管理个人中心(修改个人信息,选择本地图片进行头像修改)

&#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 接下来看看由辉辉所写的关于OA项目的相关操作吧 数据表及分析 表数据 表分析 所谓的权限管理就是不同的人管理不同的事&#xff0c;拥有着管理不同事情的不同权力。那么第一张表--权限表&…

网络知识整理

网络知识整理 网络拓扑网关默认网关 数据传输拓扑结构层面协议层面 网络拓扑 网关 连接两个不同的网络的设备都可以叫网关设备&#xff0c;网关的作用就是实现两个网络之间进行通讯与控制。 网关设备可以是交换机(三层及以上才能跨网络) 、路由器、启用了路由协议的服务器、代…