Golang如何编写更省力的代码

Golang如何编写更省力的代码 继续讨论 Go 与绿色技术 - 你如何使用 Go 来保护环境?

每次算法代码审查都会问“能否用更少的电力来完成”

我刚读到上面这段话,想知道这具体如何实现,我该如何测量代码的耗电量?有没有什么例子,展示一段高耗电的代码,然后如何重写它以消耗更少的能量?

7 回复

hyousef:

我很好奇这是如何实现的,如何测量我的代码的耗电量?有没有一个高耗电的代码示例,然后进行重写…

非常简单:每个CPU操作都会消耗能量。尽量减少CPU操作。同时,每一点内存的使用也会消耗能量,尽量减少内存使用。

更多关于Golang如何编写更省力的代码的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


节能与硬件的设计和制造方式直接相关。

先生,您说得对。但请看看您陈述的实用性:您有一台已经制造好的机器,并在其上运行您的代码。对于您的代码需求而言,那台机器是静态的,它不会改变。

只要您的代码不是为特定机器设计、没有针对其特定规格进行优化以利用其特性,那么拥有更高效的代码就是有益的,即使在您升级机器时也是如此。

每个CPU操作都会消耗能量。尝试最小化CPU操作。同时,每一比特内存都会消耗能量,尝试最小化内存使用。

在我看来,在代码层面,这不会有太大帮助。节能与硬件的设计和制造方式直接相关。

在消费者层面,你可以考虑将你的应用程序部署在基于ARM或RISC-V的端点上(例如:Vision5 - https://www.youtube.com/watch?v=ykKnc86UtXg)。然而,这本身也有其需要应对的挑战。

总而言之,通用的绿色规则适用:仅部署你所消耗的;尽可能遵循3R原则

hollowaykeanho:

充其量,我们只能优化算法,使其用更少的周期获得更好的性能,但效果是微乎其微的。

你为什么声称效果微乎其微?你是怎么知道的,你有什么证据?

hollowaykeanho:

在下次硬件更新时,考虑使用更环保的硬件,比如基于ARM或RISC-V的服务器和终端。

我强烈反对。你能使用的最环保的硬件就是你现有的硬件,这比制造一台新硬件要环保得多。同理,一辆二手车也比一辆新的电动汽车环保得多,仅仅是因为电动汽车需要先被制造出来。

3R 尽可能做到

在我看来,使用 Go 语言来减少、重用和回收代码有点困难。至少我在理解如何做到这一点上有些困难。例如,与其他驱动程序相比,sqlx(Postgresql)可以大大减少代码量,但它不被认为是生成静态类型代码的方式,因此不算是“地道的”Go 用法。

但有一种方法可以实现更好的代码(至少在 Web 开发中),那就是反复测量并修复问题。我自己经常使用 https://pagespeed.web.dev 来寻找提高速度的方法,从而减少整体影响。这不仅仅是电力消耗的问题。但 Web 开发还有其他一些挑战,因为它很复杂(例如托管、缓存、减少流量等)。

我在某处读到,只有大约 10% 的 Go 开发者使用 Go 进行 Web 开发。有人能证实这一点吗?

telo_tade:

只要你的代码不是为特定机器设计的,没有针对其特定规格进行优化,那么拥有更高效的代码总是有益的,即使在你升级机器之后。

代码仅仅是指令。我们最多只能优化算法,使其用更少的周期获得更好的性能,但效果是微乎其微的。用通俗的话来说:

  1. 我们需要进行食物配给(保持绿色环保)。
  2. 我可以用英语、普通话等语言工作(代码)。
  3. 我吃的食物是普通人的两倍(需要两倍的能量)。
  4. 最多,你可以用你的语言告诉我食物短缺,或者指着我的肚子让我开始减少消耗(告诉我用一倍的能量完成相同的输出)。

问题不在于说话或指肚子没有帮助;而是它产生的效果非常小(因为我仍然需要大约两倍的能量才能维持生存,否则我会饿死)。在这种情况下,我宁愿从其他角度来解决问题,比如:

  1. 雇佣另一个吃得比我少(少于两倍食物)但工作比我做得更好的人(硬件替换)。
  2. 研究自给自足的室内农业来源(使用可再生能源供电)。
  3. 优化我的工作安排,让一个只吃一倍食物的人处理日常任务,只在重大任务时叫我(调度优化,能效+性能协处理器)。

争论的焦点主要是从正确的角度来解决问题。


附注:不要灰心。我们(指活跃的维护者)无论如何都会为了可维护性而不断重构和优化我们的代码(例如性能优化等),所以你仍然会得到更好的代码。

telo_tade:

但看看你陈述的实用性:你已经有一台已经构建好的机器,你在上面运行你的代码。对于你的代码需求来说,那台机器是静态的,它不会改变。

这就是“只部署你消耗的资源”原则发挥作用的地方。如果你的机器还没有达到其投资回报时间线(通常是6年),请继续使用它。在那之后,你可以考虑使用虚拟机或容器(Docker等)进行优化,将你的计算服务整合到尽可能少的活跃硬件上。如果你能实现类似谷歌容器引擎的自动驾驶功能,那就更好了。

在下一次硬件更新时,考虑更环保的硬件,比如基于ARM或RISC-V的服务器和终端。

同时,别忘了回收旧硬件。

Sibert:

我在某处读到,只有大约10%的Go开发者将Go用于Web开发。有人能证实这一点吗?

据我所知并非如此。

img

来源:Go Developer Survey 2022 Q2 Results - The Go Programming Language,基于5752个数据源。

在Go中编写更节能的代码主要涉及优化算法效率、减少内存分配和利用并发。以下是一些具体示例:

1. 测量工具

可以使用runtime/pprofruntime包来测量CPU和内存使用:

import (
    "fmt"
    "runtime"
    "time"
)

func measurePowerUsage() {
    var m1, m2 runtime.MemStats
    start := time.Now()
    
    runtime.ReadMemStats(&m1)
    // 你的代码在这里
    runtime.ReadMemStats(&m2)
    
    elapsed := time.Since(start)
    memUsed := m2.TotalAlloc - m1.TotalAlloc
    
    fmt.Printf("执行时间: %v, 内存分配: %v bytes\n", elapsed, memUsed)
}

2. 高耗电 vs 节能代码示例

示例1:字符串拼接优化

高耗电版本:

func buildStringHighPower(data []string) string {
    var result string
    for _, s := range data {
        result += s  // 每次都会分配新内存
    }
    return result
}

节能版本:

func buildStringLowPower(data []string) string {
    var builder strings.Builder
    builder.Grow(len(data) * 10) // 预分配内存
    for _, s := range data {
        builder.WriteString(s)
    }
    return builder.String()
}

示例2:算法复杂度优化

高耗电版本(O(n²)):

func findDuplicatesHighPower(arr []int) []int {
    duplicates := []int{}
    for i := 0; i < len(arr); i++ {
        for j := i + 1; j < len(arr); j++ {
            if arr[i] == arr[j] {
                duplicates = append(duplicates, arr[i])
            }
        }
    }
    return duplicates
}

节能版本(O(n)):

func findDuplicatesLowPower(arr []int) []int {
    seen := make(map[int]bool)
    duplicates := make([]int, 0, len(arr)/2)
    
    for _, num := range arr {
        if seen[num] {
            duplicates = append(duplicates, num)
        } else {
            seen[num] = true
        }
    }
    return duplicates
}

示例3:并发处理优化

高耗电版本(顺序处理):

func processItemsHighPower(items []Item) []Result {
    results := make([]Result, len(items))
    for i, item := range items {
        results[i] = processItem(item) // 顺序处理
    }
    return results
}

节能版本(并发处理):

func processItemsLowPower(items []Item) []Result {
    results := make([]Result, len(items))
    var wg sync.WaitGroup
    sem := make(chan struct{}, runtime.NumCPU()) // 限制并发数
    
    for i, item := range items {
        wg.Add(1)
        sem <- struct{}{}
        
        go func(idx int, it Item) {
            defer wg.Done()
            results[idx] = processItem(it)
            <-sem
        }(i, item)
    }
    
    wg.Wait()
    return results
}

示例4:内存复用

高耗电版本:

func processBatchHighPower(batches [][]byte) {
    for _, batch := range batches {
        result := make([]byte, len(batch)) // 每次分配新内存
        // 处理逻辑
        _ = result
    }
}

节能版本:

func processBatchLowPower(batches [][]byte) {
    var buffer []byte
    for _, batch := range batches {
        if cap(buffer) < len(batch) {
            buffer = make([]byte, len(batch))
        } else {
            buffer = buffer[:len(batch)]
        }
        // 复用buffer处理
        _ = buffer
    }
}

3. 使用pprof进行性能分析

import (
    "os"
    "runtime/pprof"
)

func profileCode() {
    f, _ := os.Create("cpu.prof")
    pprof.StartCPUProfile(f)
    defer pprof.StopCPUProfile()
    
    // 你的代码在这里
}

4. 实际节能技巧

  • 使用sync.Pool重用对象
  • 避免不必要的内存分配
  • 使用适当的数据结构
  • 批量处理减少系统调用
  • 合理设置GOMAXPROCS

这些优化通过减少CPU计算时间和内存分配,直接降低了电力消耗。在实际环境中,可以通过监控系统的功率计或云服务商的能耗报告来验证效果。

回到顶部