False Sharing

CPU-Cache 结构图 多数的CPU缓冲结构(Intel) 缓存行大小 32/64/128(现在多数为64) 解释(下面假设所有的线程都在不同的核心上) 在 不同的线程上读写某段连续的内存不同索引的值, 可能导致高速缓冲miss, 导致重复拷贝 a = [1,1,1,1,1,1] # thread 1 for {a[0]++} # thread 2 for {a[1]++} # thread 3 for {a[2]++} # thread 3 for {a[4]++} 线程写入某个值之后, 会导致其他其他的线程的缓存失效 例如 thread 1 修改了a[0] 其它线程虽然没有使用a[0], 但缓存也会失效, 需要重新获取, 会产生额外的拷贝的开销 如果需要解决这个问题, 需要将各个元素分隔到不同的缓存行, 也就是上一个元素的开始地址到下一个元素的开始地址, 间隔32/64/128 如 [1,x,x,x,x,2,x,x,x,x] 的形式进行填充 代码 package false_sharing import ( "runtime" "sync" "testing" "unsafe" ) const numCpu = 12 type increase interface { Increase(idx int) Len() int } func BenchmarkNoPadIncrease(b *testing....

2021-05-23 · 2 分钟 · 252 字

Go特性记录

internal 包 [20210316] /go/src/xxx/package/internal internal 包中的内容,无法在package外被导入,否则编译不通过。

2021-03-16 · 1 分钟 · 6 字

字符串拼接性能测试

// splicing_test.go package splicing import ( "strconv" "strings" "testing" ) func Str(str []string) string { var rst string for _, s := range str { rst += s } return rst } func BuilderStr(str []string) string { var builder strings.Builder for _, s := range str { builder.WriteString(s) } return builder.String() } func BenchmarkStr(b *testing.B) { srcStr := make([]string, 0, 100000) b.Run("Append-10000", func(b *testing.B) { for i := 0; i < 10000; i++ { srcStr = append(srcStr, strconv....

2021-01-28 · 3 分钟 · 431 字

time.After() 导致内存暴涨

select + time.After 导致内存暴涨 func Function(notRun notRun) { for { select { case <-notRun.notRun: // 大多数情况都是有notRun的输入 case <-time.After(time.Minute): notRun.Close() continue case <-notRun.run: notRun.Close() return } } } notRun 发送消息频率过快,而每次select都会调用到time.After,而time.After又会NewTimer,而每次NewTimer都必须在1分钟后才能释放。 当notRun的频率很高时,会在内存中堆积非常多的无用的Timer。导致内存暴涨。 解决方法 func Function(notRun notRun) { afterTime := time.Minute after := time.NewTimer(afterTime) defer after.Stop() for { after.Reset(afterTime) select { case <-notRun.notRun: case <-after.C: notRun.Close() continue case <-notRun.run: notRun.Close() return } } } 自己 NewTimer 在每次 select 之前 reset,使 timer 重新计时,从而避免每次都 new timer。

2019-10-31 · 1 分钟 · 71 字

Golang 编译

编译 编译其他系统 CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build # 不能复制空格 set CGO_ENABLED=0 set GOOS=linux set GOARCH=amd64 go build 图标制作 第一步: Windows 下载MinGW 第二步: 新建一个.rc文件,加入文件名为 demo.rc 输入内容 IDI_ICON1 ICON "cefclient.ico" 其中 cefclient.ico 是你的ico的地址 第三步: MinGW 执行 windres -o demo.syso demo.rc 第四步: 将生成的demo.syso 放到项目目录下 go build 隐藏命令行 go build -i -ldflags="-H windowsgui"

2019-10-29 · 1 分钟 · 49 字

GORM 使用

MYSQL 悲观锁 tx := db.DB.Begin() if err := tx.Set("gorm:query_option", "FOR UPDATE"). Where("`xx` = ? and `xxx` = ?", xx, xxx). First(&xx).Error; err != nil { tx.Rollback() return } 在事务中使用 Set("gorm:query_option", "FOR UPDATE") + first 能将查询的这条记录锁住; 在事务 rollback 或 commit 后会 unlock。 创建复合主键(当主键涉及自增时) gorm:"primary_key;AUTO_INCREMENT:false" 连接数据库 步骤分析 引入mysql数据库驱动 引入gorm包 读取配置文件中的数据库信息 将读取的数据生成为Open()需要的字符串 代码示例 package base import ( _ "github.com/go-sql-driver/mysql" _ "github.com/jinzhu/gorm/dialects/mysql" "github.com/jinzhu/gorm" "fmt" "log" ) var ( DB *gorm.DB user = "user" password = "password" dbname = "dbname" address = "address" port = "port" ) func DBConnect() { dsn := fmt....

2019-08-10 · 1 分钟 · 115 字

Golang HTTP包传输文件

文件传输 Content-Type http://tool.oschina.net/commons?type=22013-05-17 multipart/form-data: 既可以提交普通键值对,也可以提交(多个)文件键值对。 application/octet-stream: 只能提交二进制,而且只能提交一个二进制,如果提交文件的话,只能提交一个文件,后台接收参数只能有一个,而且只能是流(或者字节数组) application/x-www-form-urlencoded 不属于http content-type规范,通常用于浏览器表单提交,数据组织格式:name1=value1&name2=value2,post时会放入http body,get时,显示在在地址栏。 // golang 实现 W.Header().Add("Content-Type","application/octet-stream") W.Header().Add("Content-Disposition",fmt.Sprintf("attachment; filename=\"%s\"", fileName))

2019-07-24 · 1 分钟 · 18 字