You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go语言Partial Sort性能优化方案求助

Go语言高性能Partial Sort实现的性能瓶颈求解

我正在将Spotify的Annoy库移植到Go语言,项目名为goannoy,目前在排序环节(尤其是对标C++ std::partial_sort的部分排序功能)遭遇性能瓶颈。

我自行实现了两种部分排序方案:一种基于选择排序结合标准排序,另一种基于二叉堆,但在处理百万级规模的大数组时性能表现很差,推测核心原因是GC压力过大。现寻求**高性能、低内存分配(或零额外分配)**的Go语言partial sort实现方向或成熟库。

以下是我的实现代码:

排序用Pair结构体及优先队列实现

type Pair[T constraints.Ordered, S constraints.Ordered] struct {
    First  T
    Second S
}

func (p *Pair[T, S]) Less(other *Pair[T, S]) bool {
    return p.First < other.First ||
        (p.First == other.First && p.Second < other.Second)
}

type Pairs[T constraints.Ordered, S constraints.Ordered] []*Pair[T, S]

func (pq Pairs[_, _]) Len() int {
    return len(pq)
}

func (pq Pairs[_, _]) Less(i, j int) bool {
    return pq[i].First < pq[j].First ||
        (pq[i].First == pq[j].First && pq[i].Second < pq[j].Second)
}

func (pq Pairs[_, _]) Swap(i, j int) {
    pq[i], pq[j] = pq[j], pq[i]
}

func (pq *Pairs[TP, TV]) Push(x interface{}) {
    *pq = append(*pq, x.(*Pair[TP, TV]))
}

func (pq *Pairs[_, _]) Pop() interface{} {
    old := *pq
    n := len(old)
    item := old[n-1]
    *pq = old[:n-1]
    return item
}

func (pq *Pairs[TP, TV]) Top() *Pair[TP, TV] {
    if len(*pq) == 0 {
        return nil
    }

    return (*pq)[0]
}

第一种部分排序实现

func PartialSortSlice[TV interfaces.VectorType, TIX interfaces.IndexTypes](
    s []*Pair[TV, TIX],
    begin, middle, end int,
) {
    if begin >= end || middle <= begin || middle > end {
        return
    }

    // Find the N smallest elements
    N := middle - begin

    if end-begin > 20 && end-begin < 5000000 {
        SortPairs(s)
        return
    }

    for i := 0; i < N; i++ {
        minIndex := begin + i

        // Find the index of the smallest element in the unsorted part
        for j := begin + i + 1; j < end; j++ {
            if s[j].Less(s[minIndex]) {
                minIndex = j
            }
        }

        // Swap elements
        if minIndex != begin+i {
            s[begin+i], s[minIndex] = s[minIndex], s[begin+i]
        }
    }

    // Sort sub-range [begin, middle)
    if N > 15 {
        SortPairs(s[begin:middle])
    } else {
        for i := begin + 1; i < middle; i++ {
            for j := i; j > begin && s[j].Less(s[j-1]); j-- {
                s[j], s[j-1] = s[j-1], s[j]
            }
        }
    }
}

SortPairs实现

func SortPairs[TV interfaces.VectorType, TIX interfaces.IndexTypes](
    pairs []*Pair[TV, TIX],
) {
    sort.Slice(pairs, func(i, j int) bool {
        return pairs[i].Less(pairs[j])
    })
}

第二种基于二叉堆的部分排序实现

func PartialSortSlice2[TV interfaces.VectorType, TIX interfaces.IndexTypes](
    s []*Pair[TV, TIX],
    begin, middle, end int,
) {
    if begin >= end || middle <= begin || middle > end {
        return
    }

    // Find the N smallest elements using a binary heap
    N := middle - begin
    h := pmnh[TV, TIX]{indices: make([]int, N), data: s}
    for i := 0; i < N; i++ {
        h.indices[i] = i + begin
    }
    heap.Init(&h)
    for i := N; i < end-begin; i++ {
        if s[begin+i].Less(s[h.indices[0]]) {
            h.indices[0] = i + begin
            heap.Fix(&h, 0)
        }
    }

    // Swap elements
    for i := 0; i < N; i++ {
        s[begin+i], s[h.indices[i]-begin] = s[h.indices[i]-begin], s[begin+i]
    }

    // Sort sub-range [begin, middle) in place
    for i := begin + 1; i < middle; i++ {
        for j := i; j > begin && s[j].Less(s[j-1]); j-- {
            s[j], s[j-1] = s[j-1], s[j]
        }
    }
}

性能问题复现命令

git clone https://github.com/mariotoffia/goannoy.git
cd goannoy
go run cmd/precision/main.go -file -items 1000000 -prec 1000 -cpu-profile
cat results.txt

(使用-mem-profile参数可查看内存分配情况)

内容的提问来源于stack exchange,提问作者Mario Toffia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 22:42:35