Golang插件编译时间过长问题探讨

Golang插件编译时间过长问题探讨 我在我的项目中使用了Golang的插件概念。(go build -buildmode=plugin) 将一个插件编译成.so文件平均需要15秒。 如果我想要编译三到四个插件,这几乎需要45秒。 有没有办法减少编译到.so文件的时间? 或者 有没有其他方法可以更快地编译? 或者 至少可以减少多个插件的编译时间(例如,我能否并行运行所有插件,使得总空闲时间始终是15秒)

这是我在一个循环中使用的命令,该命令遍历一个列表并执行相同的命令:

go build -buildmode=plugin -o "classes/"+List[i]+".so "+  "classes/" + List[i].action + ".go"

更多关于Golang插件编译时间过长问题探讨的实战教程也可以访问 https://www.itying.com/category-94-b0.html

5 回复

我正在借助一个Node.js进程来执行这个shell命令。

更多关于Golang插件编译时间过长问题探讨的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


你是如何在循环中运行那个命令的?是在 shell 脚本里,还是其他方式?

感谢您的建议。 Nodejs 是异步代码执行,因此这个循环几乎同时开始,也就是说,命令几乎是同时运行的。

我不熟悉Node,但我建议你寻找一种方法从那里并行启动命令。如果你是从Go本身运行编译,我可以帮助你,但就像我说的,我不了解Node。

对于Golang插件编译时间过长的问题,可以通过并行编译来显著减少总时间。以下是几种实现方式:

1. 使用Golang并发编译插件

package main

import (
    "fmt"
    "os/exec"
    "sync"
)

func compilePlugin(pluginName, actionFile string, wg *sync.WaitGroup) {
    defer wg.Done()
    
    cmd := exec.Command("go", "build", 
        "-buildmode=plugin",
        "-o", fmt.Sprintf("classes/%s.so", pluginName),
        fmt.Sprintf("classes/%s.go", actionFile))
    
    if err := cmd.Run(); err != nil {
        fmt.Printf("编译插件 %s 失败: %v\n", pluginName, err)
    } else {
        fmt.Printf("插件 %s 编译完成\n", pluginName)
    }
}

func main() {
    plugins := []struct {
        name   string
        action string
    }{
        {"plugin1", "action1"},
        {"plugin2", "action2"},
        {"plugin3", "action3"},
    }
    
    var wg sync.WaitGroup
    
    for _, p := range plugins {
        wg.Add(1)
        go compilePlugin(p.name, p.action, &wg)
    }
    
    wg.Wait()
    fmt.Println("所有插件编译完成")
}

2. 使用Goroutine池控制并发数

package main

import (
    "fmt"
    "os/exec"
    "sync"
)

func worker(id int, jobs <-chan string, results chan<- string, wg *sync.WaitGroup) {
    defer wg.Done()
    for plugin := range jobs {
        cmd := exec.Command("go", "build",
            "-buildmode=plugin",
            "-o", fmt.Sprintf("classes/%s.so", plugin),
            fmt.Sprintf("classes/%s.go", plugin))
        
        if err := cmd.Run(); err != nil {
            results <- fmt.Sprintf("Worker %d: 插件 %s 编译失败", id, plugin)
        } else {
            results <- fmt.Sprintf("Worker %d: 插件 %s 编译成功", id, plugin)
        }
    }
}

func main() {
    plugins := []string{"plugin1", "plugin2", "plugin3", "plugin4"}
    
    numWorkers := 4
    jobs := make(chan string, len(plugins))
    results := make(chan string, len(plugins))
    
    var wg sync.WaitGroup
    
    // 启动worker
    for w := 1; w <= numWorkers; w++ {
        wg.Add(1)
        go worker(w, jobs, results, &wg)
    }
    
    // 发送任务
    for _, plugin := range plugins {
        jobs <- plugin
    }
    close(jobs)
    
    // 收集结果
    go func() {
        wg.Wait()
        close(results)
    }()
    
    // 输出结果
    for result := range results {
        fmt.Println(result)
    }
}

3. 使用sync.ErrGroup(需要golang.org/x/sync)

package main

import (
    "context"
    "fmt"
    "os/exec"
    
    "golang.org/x/sync/errgroup"
)

func compilePlugin(ctx context.Context, pluginName string) error {
    cmd := exec.CommandContext(ctx, "go", "build",
        "-buildmode=plugin",
        "-o", fmt.Sprintf("classes/%s.so", pluginName),
        fmt.Sprintf("classes/%s.go", pluginName))
    
    return cmd.Run()
}

func main() {
    plugins := []string{"plugin1", "plugin2", "plugin3"}
    
    g, ctx := errgroup.WithContext(context.Background())
    
    for _, plugin := range plugins {
        plugin := plugin // 创建局部变量副本
        g.Go(func() error {
            fmt.Printf("开始编译插件: %s\n", plugin)
            err := compilePlugin(ctx, plugin)
            if err != nil {
                return fmt.Errorf("编译插件 %s 失败: %w", plugin, err)
            }
            fmt.Printf("插件 %s 编译完成\n", plugin)
            return nil
        })
    }
    
    if err := g.Wait(); err != nil {
        fmt.Printf("编译过程中出错: %v\n", err)
    } else {
        fmt.Println("所有插件编译成功")
    }
}

4. Shell脚本并行编译

#!/bin/bash

# 并行编译所有插件
compile_plugin() {
    local plugin=$1
    echo "开始编译: $plugin"
    go build -buildmode=plugin -o "classes/${plugin}.so" "classes/${plugin}.go"
    echo "完成编译: $plugin"
}

# 导出函数以便在子进程中使用
export -f compile_plugin

# 插件列表
plugins=("plugin1" "plugin2" "plugin3" "plugin4")

# 使用xargs并行执行
printf "%s\n" "${plugins[@]}" | xargs -P 4 -I {} bash -c 'compile_plugin "$@"' _ {}

echo "所有插件编译完成"

5. 使用Makefile并行编译

.PHONY: all plugins clean

PLUGINS := plugin1 plugin2 plugin3 plugin4
SO_FILES := $(addprefix classes/,$(addsuffix .so,$(PLUGINS)))

all: plugins

classes/%.so: classes/%.go
	@echo "编译插件: $*"
	@go build -buildmode=plugin -o $@ $<

plugins: $(SO_FILES)

# 并行编译(使用make -j选项)
# 运行: make -j4 plugins

clean:
	rm -f classes/*.so

使用并行编译后,4个插件的编译时间可以从60秒减少到约15秒(假设有足够的CPU核心)。编译时可以通过设置GOMAXPROCS环境变量或使用-j选项(在Makefile中)来控制并发数。

回到顶部