Golang性能优化:这两个简单函数还能更快吗?
Golang性能优化:这两个简单函数还能更快吗? 我对自己的代码进行了基准测试,结果出乎意料,两个简单的函数竟然占用了相当长的计算时间。好吧,它们在一个while循环中被调用了很多很多次,但我没想到会这样。 这两个函数相当简单:
- 给定一个二维切片(NxN矩阵,其中N的范围是2-50)和一个矩阵列索引,返回该列的值作为一个切片。
func view(mm [][]int, columnIndex int) []int {
column := make([]int, len(mm))
for i, row := range mm {
column[i] = row[columnIndex]
}
return column
}
- 给定一个二维切片(NxN矩阵,其中N的范围是2-30),返回矩阵的列和作为一个切片。
func colSums(mm [][]int) []int {
sum := make([]int, len(mm[0]))
for i := range mm[0] {
psum := 0
for j := range mm {
psum = psum + mm[j][i]
}
sum[i] = psum
}
return sum
}
有没有办法让它们变得更好/更快?
更多关于Golang性能优化:这两个简单函数还能更快吗?的实战教程也可以访问 https://www.itying.com/category-94-b0.html
3 回复
如果你对矩阵进行转置,即让外层的切片代表列而非行,那么你将获得更好的引用局部性,并且能更稳定地命中缓存。
另一种选择是,在一个连续的切片中分配整个矩阵,然后为行或列构建子切片。
更多关于Golang性能优化:这两个简单函数还能更快吗?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
你对代码进行过基准测试吗? 你能将其与以下代码进行比较吗?
- 给定一个二维切片(NxN 矩阵,其中 N 在 2-50 范围内)和一个矩阵列索引,将该列的值作为切片返回
func view(mm [][]int, columnIndex int) []int {
column := make([]int, len(mm))
for i := range mm {
column[i] = mm[i][columnIndex]
}
return column
}
- 给定一个二维切片(NxN 矩阵,其中 N 在 2-30 范围内),将矩阵的列和作为切片返回
func colSums(mm [][]int) []int {
sum := make([]int, len(mm[0]))
for i := range mm[0] {
for j := range mm {
sum[i] += mm[j][i]
}
}
return sum
}
这两个函数确实有优化空间,主要问题在于内存分配和访问模式。以下是优化方案:
1. 列视图函数优化
原函数每次调用都分配新切片,可以通过预分配复用内存:
func viewFast(mm [][]int, columnIndex int, buf []int) []int {
for i := 0; i < len(mm); i++ {
buf[i] = mm[i][columnIndex]
}
return buf[:len(mm)]
}
// 使用示例
func benchmark() {
matrix := make([][]int, 50)
// 初始化矩阵...
buf := make([]int, 50) // 预分配最大可能大小
for i := 0; i < 1000000; i++ {
col := viewFast(matrix, 2, buf)
_ = col // 使用列数据
}
}
更彻底的优化是直接操作,避免复制:
func viewDirect(mm [][]int, columnIndex int, fn func(int)) {
for i := 0; i < len(mm); i++ {
fn(mm[i][columnIndex])
}
}
// 使用示例
viewDirect(matrix, 2, func(val int) {
// 直接处理每个值,避免切片分配
process(val)
})
2. 列求和函数优化
原函数访问模式不佳(列优先),改为行优先访问:
func colSumsFast(mm [][]int) []int {
if len(mm) == 0 {
return nil
}
n := len(mm[0])
sum := make([]int, n)
for _, row := range mm {
for j := 0; j < n; j++ {
sum[j] += row[j]
}
}
return sum
}
使用循环展开进一步优化:
func colSumsUnrolled(mm [][]int) []int {
if len(mm) == 0 {
return nil
}
n := len(mm[0])
sum := make([]int, n)
for _, row := range mm {
j := 0
// 每次处理4个元素
for ; j+3 < n; j += 4 {
sum[j] += row[j]
sum[j+1] += row[j+1]
sum[j+2] += row[j+2]
sum[j+3] += row[j+3]
}
// 处理剩余元素
for ; j < n; j++ {
sum[j] += row[j]
}
}
return sum
}
3. 组合优化方案
如果两个函数在同一个热点循环中,可以合并计算:
func computeColumnStats(mm [][]int, targetCol int) ([]int, []int) {
if len(mm) == 0 {
return nil, nil
}
n := len(mm[0])
sums := make([]int, n)
target := make([]int, len(mm))
for i, row := range mm {
target[i] = row[targetCol]
for j := 0; j < n; j++ {
sums[j] += row[j]
}
}
return target, sums
}
基准测试对比
func BenchmarkOriginalView(b *testing.B) {
matrix := make([][]int, 50)
for i := range matrix {
matrix[i] = make([]int, 50)
for j := range matrix[i] {
matrix[i][j] = i + j
}
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = view(matrix, 2)
}
}
func BenchmarkOptimizedView(b *testing.B) {
matrix := make([][]int, 50)
for i := range matrix {
matrix[i] = make([]int, 50)
for j := range matrix[i] {
matrix[i][j] = i + j
}
}
buf := make([]int, 50)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = viewFast(matrix, 2, buf)
}
}
优化后的版本应该能获得2-5倍的性能提升,具体取决于矩阵大小和调用频率。关键优化点:
- 减少内存分配(预分配和复用)
- 改善缓存局部性(行优先访问)
- 循环展开减少循环开销

