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

二分查找性能对比:自定义比较器法与long列表法测试分析

我手头有大约200万条有序的DataPoint类型记录,需要找出与指定时间戳最接近的单条数据。数据定义如下:

public class DataPoint
{
    public long OpenTimeTs;
}

我实现了三种逻辑一致的查找方法,但对它们的性能表现产生了疑惑,下面逐一说明:

方法1:基于long类型列表的二分查找
private DataPoint BinaryFindClosest(List<DataPoint> candles, List<long> times, long dateToFindMs)
{
    int index = times.BinarySearch(dateToFindMs);

    if (index >= 0)
        return candles[index];

    // If not found, List.BinarySearch returns the complement 
    // of the index where the element should have been.
    index = ~index;

    // This date search for is larger than any
    if (index == times.Count)
        return candles[index - 1];

    // The date searched is smaller than any in the list.
    if (index == 0)
        return candles[0];

    if (Math.Abs(dateToFindMs - times[index - 1]) < Math.Abs(dateToFindMs - times[index]))
        return candles[index - 1];
    else
        return candles[index];
}
方法2:使用自定义对象比较器的二分查找

逻辑和方法1基本一致,但直接对DataPoint列表进行二分查找,通过自定义比较器来对比时间戳:

private DataPoint BinaryFindClosest2(List<DataPoint> candles, DataPoint toFind)
{
    var comparer = Comparer<DataPoint>.Create((x, y) => x.OpenTimeTs > y.OpenTimeTs ? 1 : x.OpenTimeTs < y.OpenTimeTs ? -1 : 0);

    int index = candles.BinarySearch(toFind, comparer);

    if (index >= 0)
        return candles[index];

    // If not found, List.BinarySearch returns the complement 
    // of the index where the element should have been.
    index = ~index;

    // This date search for is larger than any
    if (index == candles.Count)
        return candles[index - 1];

    // The date searched is smaller than any in the list.
    if (index == 0)
        return candles[0];

    if (Math.Abs(toFind.OpenTimeTs - candles[index - 1].OpenTimeTs) < Math.Abs(toFind.OpenTimeTs - candles[index].OpenTimeTs))
        return candles[index - 1];
    else
        return candles[index];
}
方法3:遍历查找法

这是我之前一直在用的遍历查找方式:

private DataPoint FindClosest(List<DataPoint> candles, DataPoint toFind)
{
    long timeToFind = toFind.OpenTimeTs;

    int smallestDistanceIdx = -1;
    long smallestDistance = long.MaxValue;

    for (int i = 0; i < candles.Count(); i++)
    {
        var candle = candles[i];
        var distance = Math.Abs(candle.OpenTimeTs - timeToFind);
        if (distance <= smallestDistance)
        {
            smallestDistance = distance;
            smallestDistanceIdx = i;
        }
        else
        {
            break;
        }
    }

    return candles[smallestDistanceIdx];
}
问题与测试过程

最开始我手动写测试代码验证时,发现方法2的性能居然是最优的——这和我“long列表二分查找应该更快”的直觉完全不符,当时特别疑惑。

初始手动测试代码(Release模式)

var candles = AppState.GetLoadSymbolData();
var times = candles.Select(s => s.OpenTimeTs).ToList();

var dateToFindMs = candles[candles.Count / 2].OpenTimeTs;
var candleToFind = new DataPoint() { OpenTimeTs = dateToFindMs };

var numberOfFinds = 100_000;

var sw = Stopwatch.StartNew();
for (int i = 0; i < numberOfFinds; i++)
{
    var foundCandle = BinaryFindClosest(candles, times, dateToFindMs);
}
sw.Stop();
var elapsed1 = sw.ElapsedMilliseconds;

sw.Restart();
for (int i = 0; i < numberOfFinds; i++)
{
    var foundCandle = BinaryFindClosest2(candles, candleToFind);
}
sw.Stop();
var elapsed2 = sw.ElapsedMilliseconds;

sw.Restart();
for (int i = 0; i < numberOfFinds; i++)
{
    var foundCandle = FindClosest(candles, candleToFind);
}
sw.Stop();
var elapsed3 = sw.ElapsedMilliseconds;

Console.WriteLine($"Elapsed 1: {elapsed1} ms");
Console.WriteLine($"Elapsed 2: {elapsed2} ms");
Console.WriteLine($"Elapsed 3: {elapsed3} ms");

初始测试结果

  • Elapsed 1: 19 ms
  • Elapsed 2: 1 ms
  • Elapsed 3: 60678 ms

这个结果显然有问题,于是我改用专业性能测试工具BenchmarkDotNet重新测试,终于得到了准确的结果:

MethodNMeanErrorStdDevGen0Allocated
BinaryFindClosest1000028.31 ns0.409 ns0.362 ns--
BinaryFindClosest21000075.85 ns0.865 ns0.722 ns0.001424 B
FindClosest100003,363,223.68 ns63,300.072 ns52,858.427 ns-2 B

结论

原来初始测试的顺序影响了结果(比如JIT编译、缓存等因素),实际性能排序是:方法1 > 方法2 > 方法3,遍历法始终是最慢的,就算优化后性能依旧垫底。

这里最大的教训就是:性能测试一定要用专业工具,手动写Stopwatch很容易因为各种因素得到错误结论。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 17:45:38