Last active
December 28, 2022 14:52
-
-
Save BruceChen7/675b64f260068d14ae8598c3dff42008 to your computer and use it in GitHub Desktop.
#golang#effective#practice#go
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// [Go语言高性能编程手册(万字长文)](https://mp.weixin.qq.com/s/iSxWFsnNLGgilzKIsSDBgg) | |
// Bad | |
for i := 0; i < b.N; i++ { | |
s := fmt.Sprint(rand.Int()) | |
} | |
BenchmarkFmtSprint-4 143 ns/op 2 allocs/op | |
// Good | |
for i := 0; i < b.N; i++ { | |
s := strconv.Itoa(rand.Int()) | |
} | |
BenchmarkStrconv-4 64.2 ns/op 1 allocs/op | |
// 少量的重复不比反射差 | |
// DeleteSliceElms 从切片中过滤指定元素。注意:不修改原切片。 | |
func DeleteSliceElms(i interface{}, elms ...interface{}) interface{} { | |
// 构建 map set。 | |
m := make(map[interface{}]struct{}, len(elms)) | |
for _, v := range elms { | |
m[v] = struct{}{} | |
} | |
// 创建新切片,过滤掉指定元素。 | |
v := reflect.ValueOf(i) | |
t := reflect.MakeSlice(reflect.TypeOf(i), 0, v.Len()) | |
for i := 0; i < v.Len(); i++ { | |
if _, ok := m[v.Index(i).Interface()]; !ok { | |
t = reflect.Append(t, v.Index(i)) | |
} | |
} | |
return t.Interface() | |
} | |
// DeleteU64liceElms 从 []uint64 过滤指定元素。注意:不修改原切片。 | |
func DeleteU64liceElms(i []uint64, elms ...uint64) []uint64 { | |
// 构建 map set。 | |
m := make(map[uint64]struct{}, len(elms)) | |
for _, v := range elms { | |
m[v] = struct{}{} | |
} | |
// 创建新切片,过滤掉指定元素。 | |
t := make([]uint64, 0, len(i)) | |
for _, v := range i { | |
if _, ok := m[v]; !ok { | |
t = append(t, v) | |
} | |
} | |
return t | |
} | |
// 当使用反射时,请问一下自己,我真地需要它吗? | |
// 遍历 []struct{} 使用下标而不是 range | |
// 运算符 + 只能简单地完成字符串之间的拼接,非字符串类型的变量需要单独做类型转换。 | |
// 行内拼接字符串不会产生内存分配,也不涉及类型地动态转换,所以性能上优于fmt.Sprintf()。 | |
// Good | |
func BenchmarkJoinStrWithOperator(b *testing.B) { | |
s1, s2, s3 := "foo", "bar", "baz" | |
for i := 0; i < b.N; i++ { | |
_ = s1 + s2 + s3 | |
} | |
} | |
// Bad | |
func BenchmarkJoinStrWithSprintf(b *testing.B) { | |
s1, s2, s3 := "foo", "bar", "baz" | |
for i := 0; i < b.N; i++ { | |
_ = fmt.Sprintf("%s%s%s", s1, s2, s3) | |
} | |
} | |
// 非行内拼接字符串推荐使用 strings.Builder | |
func BenchmarkJoinStrWithStringsJoin(b *testing.B) { | |
s1, s2, s3 := "foo", "bar", "baz" | |
for i := 0; i < b.N; i++ { | |
_ = strings.Join([]string{s1, s2, s3}, "") | |
} | |
} | |
func BenchmarkJoinStrWithStringsBuilder(b *testing.B) { | |
s1, s2, s3 := "foo", "bar", "baz" | |
for i := 0; i < b.N; i++ { | |
var builder strings.Builder | |
_, _ = builder.WriteString(s1) | |
_, _ = builder.WriteString(s2) | |
_, _ = builder.WriteString(s3) | |
} | |
} | |
func BenchmarkJoinStrWithBytesBuffer(b *testing.B) { | |
s1, s2, s3 := "foo", "bar", "baz" | |
for i := 0; i < b.N; i++ { | |
var buffer bytes.Buffer | |
_, _ = buffer.WriteString(s1) | |
_, _ = buffer.WriteString(s2) | |
_, _ = buffer.WriteString(s3) | |
} | |
} | |
func BenchmarkJoinStrWithByteSlice(b *testing.B) { | |
s1, s2, s3 := "foo", "bar", "baz" | |
for i := 0; i < b.N; i++ { | |
var bys []byte | |
bys= append(bys, s1...) | |
bys= append(bys, s2...) | |
_ = append(bys, s3...) | |
} | |
} | |
func BenchmarkJoinStrWithByteSlicePreAlloc(b *testing.B) { | |
s1, s2, s3 := "foo", "bar", "baz" | |
for i := 0; i < b.N; i++ { | |
bys:= make([]byte, 0, 9) | |
bys= append(bys, s1...) | |
bys= append(bys, s2...) | |
_ = append(bys, s3...) | |
} | |
} | |
// 综合易用性和性能,一般推荐使用strings.Builder来拼接字符串。 | |
// string.Builder也提供了预分配内存的方式 Grow: | |
func BenchmarkJoinStrWithStringsBuilderPreAlloc(b *testing.B) { | |
s1, s2, s3 := "foo", "bar", "baz" | |
for i := 0; i < b.N; i++ { | |
var builder strings.Builder | |
builder.Grow(9) | |
_, _ = builder.WriteString(s1) | |
_, _ = builder.WriteString(s2) | |
_, _ = builder.WriteString(s3) | |
} | |
} | |
// 使用sync.Pool来减少gc | |
// | |
type Student struct { | |
Name string | |
Age int32 | |
Remark [1024]byte | |
} | |
var studentPool = sync.Pool{ | |
New: func() interface{} { | |
return new(Student) | |
}, | |
} | |
stu := studentPool.Get().(*Student) | |
json.Unmarshal(buf, stu) | |
studentPool.Put(stu) |
输出什么
func main() {
var i interface{}
if i == nil {
fmt.Println("nil")
return
}
fmt.Println("not nil")
}
当且仅当接口的动态值和动态类型都为 nil 时,接口类型值才为 nil,上面的代码输出nil。
func Foo(x interface{}) {
if x == nil {
fmt.Println("empty interface")
return
}
fmt.Println("non-empty interface")
}
func main() {
var x *int = nil
Foo(x)
}
interface 的内部结构,我们知道接口除了有静态类型,还有动态类型和动态值,当且仅当动态值和动态类型都为 nil 时,接口类型值才为 nil。这里的 x 的动态类型是 *int
,所以 x 不为 nil。
type ConfigOne struct {
Daemon string
}
func (c *ConfigOne) String() string {
return fmt.Sprintf("print: %v", c)
}
func main() {
c := &ConfigOne{}
c.String()
}
运行时错误
解析:
如果类型实现 String() 方法,当格式化输出时会自动使用 String() 方法。上面这段代码是在该类型的 String() 方法内使用格式化输出,导致递归调用,最后抛错。
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
资料来源
常见的问题
答:输出内容为:
解析: defer 的执行顺序是先进后出。出现panic语句的时候,会先按照 defer 的后进先出顺序执行,最后才会执行panic。
编译是否问题
解析:
在函数有多个返回值时,只要有一个返回值有命名,其他的也必须命名。如果有多个返回值必须加上括号();如果只有一个返回值且命名也需要加上括号()。这里的第一个返回值有命名sum,第二个没有命名,所以错误。
编译问题
下面这段代码能否通过编译,如果可以,输出什么?
答:不能通过
解析:
这道题的主要知识点是变量的简短模式,形如:x := 100 。但这种声明方式有限制:
必须使用显示初始化;
不能提供数据类型,编译器会自动推导;
只能在函数内部使用简短模式
编译问题
那有什么是可以比较的呢?
不能比较的有
编译问题
编译问题