golang 甚至更快版本的http://benchmarksgame.alioth.debian.org/u64q/program.php?test=binarytrees&lang=go&

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了golang 甚至更快版本的http://benchmarksgame.alioth.debian.org/u64q/program.php?test=binarytrees&lang=go&相关的知识,希望对你有一定的参考价值。

package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"runtime/pprof"
	"strconv"
	"sync"
	"time"
)

var minDepth = 4
var n = 0

var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")

var (
	nodeBuf    []Node
	nodeBufIdx int32
)

type Allocator struct {
	nodes []Node
	idx   int
}

func calcNodeCount(depth int) int {
	res := 1 << (uint(depth + 1))
	return res - 1
}

func NewAllocatorWithCount(cap int) *Allocator {
	return &Allocator{
		nodes: make([]Node, cap, cap),
		idx:   0,
	}
}

func (a *Allocator) PrintUnused() {
	left := cap(a.nodes) - a.idx
	if left > 0 {
		fmt.Printf("left: %d out of %d\n", left, cap(a.nodes))
	}
}

func NewAllocatorWithDepth(depth int) *Allocator {
	cap := calcNodeCount(depth)
	return NewAllocatorWithCount(cap)
}

func (a *Allocator) Reset() {
	a.idx = 0
}

func (a *Allocator) allocNode(item int, left, right *Node) *Node {
	res := &a.nodes[a.idx]
	a.idx++
	res.item = item
	res.left = left
	res.right = right
	return res
}

func main() {
	timeStart := time.Now()

	flag.Parse()
	if flag.NArg() > 0 {
		n, _ = strconv.Atoi(flag.Arg(0))
	}

	if *cpuprofile != "" {
		f, err := os.Create(*cpuprofile)
		if err != nil {
			log.Fatal(err)
		}
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}

	maxDepth := n
	if minDepth+2 > n {
		maxDepth = minDepth + 2
	}
	stretchDepth := maxDepth + 1

	a := NewAllocatorWithDepth(stretchDepth)
	check_l := bottomUpTree(a, 0, stretchDepth).ItemCheck()
	fmt.Printf("stretch tree of depth %d\t check: %d\n", stretchDepth, check_l)
	a.PrintUnused()

	a = NewAllocatorWithDepth(maxDepth)
	longLivedTree := bottomUpTree(a, 0, maxDepth)
	a.PrintUnused()

	result_trees := make([]int, maxDepth+1)
	result_check := make([]int, maxDepth+1)

	var wg sync.WaitGroup
	for depth_l := minDepth; depth_l <= maxDepth; depth_l += 2 {
		wg.Add(1)
		go func(depth int) {
			iterations := 1 << uint(maxDepth-depth+minDepth)
			check := 0
			//n := calcNodeCount(depth) * 2 * iterations
			//fmt.Printf("depth: %d, iterations: %d, nodes: %d\n", depth, iterations, n)
			a := NewAllocatorWithDepth(depth)
			for i := 1; i <= iterations; i++ {
				a.Reset()
				check += bottomUpTree(a, i, depth).ItemCheck()
				a.Reset()
				check += bottomUpTree(a, -i, depth).ItemCheck()
			}
			result_trees[depth] = iterations * 2
			result_check[depth] = check
			a.PrintUnused()
			wg.Done()
		}(depth_l)
	}
	wg.Wait()

	for depth := minDepth; depth <= maxDepth; depth += 2 {
		fmt.Printf("%d\t trees of depth %d\t check: %d\n",
			result_trees[depth], depth, result_check[depth],
		)
	}
	fmt.Printf("long lived tree of depth %d\t check: %d\n",
		maxDepth, longLivedTree.ItemCheck(),
	)
	fmt.Printf("dur: %s\n", time.Since(timeStart))
}

func bottomUpTree(a *Allocator, item, depth int) *Node {
	if depth <= 0 {
		return a.allocNode(item, nil, nil)
	}
	left := bottomUpTree(a, 2*item-1, depth-1)
	right := bottomUpTree(a, 2*item, depth-1)
	return a.allocNode(item, left, right)
}

type Node struct {
	item        int
	left, right *Node
}

func (self *Node) ItemCheck() int {
	if self.left == nil {
		return self.item
	}
	return self.item + self.left.ItemCheck() - self.right.ItemCheck()
}

_mm_add_epi32的Golang汇编实现

【中文标题】_mm_add_epi32的Golang汇编实现【英文标题】:Golang assembly implement of _mm_add_epi32 【发布时间】:2020-08-04 08:25:40 【问题描述】:

我正在尝试在 golang 程序集中实现 _mm_add_epi32,可以选择在 avo 的帮助下实现。但我对组装知之甚少,甚至不知道如何开始。你能给我一些代码提示吗?谢谢大家。

这是等效的较慢的 golang 版本:

func add(x, y []uint32) []uint32 
    if len(x) != len(y) 
        return nil
    

    result := make([]uint32, len(x))
    for i := 0; i < len(x); i++ 
        result[i] = x[i] + y[i]
    
    return result


我知道paddq xmm, xmm这个结构是我们需要的,但不知道如何将[]byte的切片转换成256位寄存器YMM

【问题讨论】:

您拥有的函数与_mm_add_epi32 有点不同,因为它需要任意长度的切片。这是故意的吗? 感谢您的回复,使用[]uint32[8]uint32都可以,第一个是为了与函数调用者保持数据结构的一致性。 嗯,支持任意长度的切片会使代码变得相当复杂,所以最好选择两者中的一个,这样我就可以写一个答案。 [8]uint32 已经足够好了,提前谢谢。 【参考方案1】:

下面是这样一个加法函数的例子:

    // func add(x, y [8]int32) [8]int32
    // q = x + y
TEXT ·add(SB),0,$0
    VMOVDQU x+0(FP), Y0
    VPADDD  Y+32(FP), Y0, Y0
    VMOVDQU Y0, q+64(FP)
    VZEROUPPER
    RET

在阅读此代码之前,请先熟悉this document。不幸的是,Go 风格的程序集(又名 Plan 9 风格的程序集)的文档记录很差。

数组按值传递到堆栈上。返回值作为调用者读回的额外最右边的参数传递。使用我链接到访问函数参数的文档中记录的(FP)

除此之外,它非常简单。语法类似于(但不等于)AT&T 语法。请注意,寄存器名称不同,必须提供大小后缀。

如您所见,为单个操作编写汇编函数是毫无意义的。采用您需要的算法并完全用汇编语言编写它可能会更好。

【讨论】:

非常感谢,我会尽快提供反馈。 谢谢@fuz,它有效!!! > 如您所见,为单个操作编写汇编函数是毫无意义的。你是对的,实际上我已经优化了我的应用程序的其他部分,这看起来像是最后可以优化的部分,所以我尝试了这个。非常感谢。 @WeiShen 如果您发布更多上下文,我也许可以提供更好的优化建议。随意提出一个新问题并将其链接到此处,以便我找到它。还可以考虑使用go tool pprof 来查找应用程序的哪些部分实际上与性能相关。 是的,我经常使用 pprof。这是 [pprof 结果](gist.github.com/shenwei356/…) @WeiShen 不幸的是,在 cmets 中不允许换行,因此您的代码部分非常乱码。如果你能告诉我你想使用这个函数的上下文,我可能会提供更好的优化建议。正如我之前所说,最好为此提出一个新问题。

以上是关于golang 甚至更快版本的http://benchmarksgame.alioth.debian.org/u64q/program.php?test=binarytrees&lang=go&的主要内容,如果未能解决你的问题,请参考以下文章

node可以用nvm快速切换版本,golang如何快速切换版本?用gvm就行。

如何在 Golang 中更快地进行 api 调用?

Golang 1.9 的新特性

[译]golang 1.8工具链改进

Go:缓冲通道总和更快?

GoLang -- Gin框架