gpt4 book ai didi

c# - 为什么性能指标有所不同?

转载 作者:太空狗 更新时间:2023-10-29 21:41:31 27 4
gpt4 key购买 nike

我有一个简单的方法,可以将数组从一种类型转换为另一种类型。我想找出哪种方法最快。但是到目前为止,我得到了不同的结果,从中我无法得出结论,哪种方法实际上要快得多。

由于转换仅涉及分配内存,读取数组和转换值,因此我感到惊讶的是,这些值并不更稳定。我想知道如何进行准确的测量,这些测量是有意义的,并且一天到一天都不会改变。
一天之间的差异约为20%。

当然,.NET 3.5和4.0的JITer,调试和 Release模式,不在调试器下运行可执行文件(禁用JIT优化直到禁用它),在DEBUG和RELEASE之间生成C#编译器的代码(主要是nop)之间存在差异。 IL代码中的操作和更多临时变量)。

using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace PerfTest
{
class Program
{
const int RUNS = 10 * 1000 * 1000;


static void Main(string[] args)
{
int[] array = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43 };

var s2 = Stopwatch.StartNew();
for (int i = 0; i < RUNS; i++)
{
float[] arr = Cast(array);
}
s2.Stop();
GC.Collect();

var s3 = Stopwatch.StartNew();
for (int i = 0; i < RUNS; i++)
{
float[] arr = Cast2(array);
}
s3.Stop();
GC.Collect();

var s4 = Stopwatch.StartNew();
for (int i = 0; i < RUNS; i++)
{
var arr = CastSafe(array);
}
s4.Stop();


Console.WriteLine("Times: {0} {1} {2}", s2.ElapsedMilliseconds, s3.ElapsedMilliseconds, s4.ElapsedMilliseconds);
}

// Referece cast implementation to check performance
public static unsafe float[] Cast(int[] input)
{
int N = input.Length;
float[] output = new float[N];

fixed (int* pIStart = &input[0])
{
int* pI = pIStart;
fixed (float* pOStart = &output[0])
{
float* pO = pOStart;

for (int i = 0; i < N; i++)
{
*pO = (float)*pI;
pI++;
pO++;
}
}
}

return output;
}

// Referece cast implementation to check performance
public static unsafe float[] Cast2(int[] input)
{
int N = input.Length;
float[] output = new float[N];
fixed (int* pIStart = &input[0])
{
int* pI = pIStart;
fixed (float* pOStart = &output[0])
{
float* pO = pOStart;

for (int i = 0; i < N; i++)
{
pO[i] = (float) pI[i];
}
}
}

return output;
}
public static float[] CastSafe(int[] input)
{
int N = input.Length;
float[] output = new float[N];

for (int i = 0; i < input.Length; i++)
{
output[i] = (float)input[i];
}

return output;
}
}
}

我明白了
  • 时间:1257 1388 1180
  • 时间:1331 1428 1267
  • 时间:1337 1435 1267
  • 时间:1208 1414 1145

  • 由此看来,傻瓜安全变体看起来比任何不安全变体都快,尽管边界检查消除了不安全方法应该使它至少和不快一样快。
    只是为了好玩,我还通过LCG(DynamicMethod)编译了相同的IL代码,该代码似乎比这些方法中的任何一个都要慢,尽管委托(delegate)调用的额外成本似乎在这里没有起到很大的作用。

    for循环确实执行此代码一千万次,这将产生稳定的结果。为什么我在这里看不到任何差异?使用实时作为进程优先级也没有帮助(psexec -realtime可执行文件)。如何获得可靠的数字?

    我的测试确实包括
  • 双四核计算机
  • Windows 7 32/64位版本
  • .NET Framework 3.5/4.0
  • 可执行文件的32/64位版本。

  • 如果使用探查器,我不确定他是否会进一步扭曲测量结果。由于他确实会不时中断我的应用程序以获取调用堆栈,因此他肯定会破坏任何可能有助于提高性能的缓存位置。如果有任何具有更好的(数据)缓存局部性的方法,我将无法通过探查器找到它。

    编辑1:
    考虑到我没有实时操作系统,我现在对我的测量进行采样。因为对于一个线程,我有一个15毫秒的时间窗口授予Windows计划程序,所以如果我测量的时间少于15毫秒,则可以将其排除在计划程序之外。如果我测量得太短,我最终将获得很小的滴答声计数,这不会告诉我太多。

    为了获得稳定的值,我需要足够长的时间来让操作系统定期执行其操作。经验测试表明,一次测量应该花费30秒钟以上的时间。

    然后将此时间跨度划分为远低于15ms的采样时间跨度。然后,我将获得每个样本的时序信息。从样本中,我可以提取最小/最大和平均值。这样,我还可以看到第一次的初始化效果。
    现在的代码看起来像这样
    class Program
    {
    const int RUNS = 100 * 1000 * 1000; // 100 million runs will take about 30s
    const int RunsPerSample = 100; // 100 runs for on sample is about 0,01ms << 15ms

    static void Main(string[] args)
    {
    int[] array = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43 };
    long[] sampleTimes = new long [RUNS/RunsPerSample];

    int sample = 0;
    for (int i = 0; i < RUNS; i+=RunsPerSample)
    {
    var sw = Stopwatch.StartNew();
    for (int j = i; j < i+RunsPerSample; j++)
    {
    float[] arr = Cast(array);
    }
    sw.Stop();
    sampleTimes[sample] = sw.ElapsedTicks;
    sample++;
    }
    Console.WriteLine("SampleSize: {0}, Min {1}, Max {2}, Average {3}",
    RunsPerSample, sampleTimes.Min(), sampleTimes.Max(), sampleTimes.Average());

    这些测试的值仍会发生变化(<10%),但是我认为,如果我创建一个我的值的直方图并丢弃可能由OS,GC引起的10%的最高值,...我真的可以我可以信任的稳定数字。

    样本大小:100,最小值25,最大值86400,平均值28,614631
  • SampleSize:100,最小值24,最大值86027,平均值28,762608
  • SampleSize:100,最小值25,最大值49523,平均值32,102037
  • SampleSize:100,最小值24,最大值48687,平均值32,030088

  • 编辑2:
    直方图显示测量值不是随机的。它们看起来像 Landau distribution,应该为我提供正确的近似算法稳定值。我希望在.NET中存在类似 ROOT的东西,在其中我可以交互地将正确的分布函数拟合到我的数据并获取结果。

    下面是使用 MSChart控件生成直方图的代码:
    using System.Collections.Generic;
    using System.Drawing;
    using System.Linq;
    using System.Windows.Forms;
    using System.Windows.Forms.DataVisualization.Charting;

    namespace ConsoleApplication4
    {
    public partial class Histogram : Form
    {
    public Histogram(long [] sampleTimes)
    {
    InitializeComponent();

    Series histogramSeries = cHistogram.Series.Add("Histogram");

    // Set new series chart type and other attributes
    histogramSeries.ChartType = SeriesChartType.Column;
    histogramSeries.BorderColor = Color.Black;
    histogramSeries.BorderWidth = 1;
    histogramSeries.BorderDashStyle = ChartDashStyle.Solid;

    var filtered = RemoveHighValues(sampleTimes, 40);
    KeyValuePair<long,int>[] histoData = GenerateHistogram(filtered);

    ChartArea chartArea = cHistogram.ChartAreas[histogramSeries.ChartArea];
    chartArea.AxisY.Title = "Frequency";

    chartArea.AxisX.Minimum = histoData.Min( x=>x.Key );
    chartArea.AxisX.Maximum = histoData.Max( x=>x.Key );

    foreach (var v in histoData)
    {
    histogramSeries.Points.Add(new DataPoint(v.Key, v.Value));
    }

    chartArea.AxisY.Minimum = 0;
    chartArea.AxisY.Maximum = histoData[0].Value + 100;
    }

    // Count the occurence of each value of input and return an array with the value as key and its count as value
    // as ordered list starting with the highest counts.
    KeyValuePair<long,int>[] GenerateHistogram(long [] input)
    {
    Dictionary<long, int> counts = new Dictionary<long, int>();
    foreach (var value in input)
    {
    int old = 0;
    if (!counts.TryGetValue(value, out old))
    {
    counts[value] = 0;
    }
    counts[value] = ++old;
    }

    var orderedCounts = (from x in counts
    orderby x.Value descending
    select x).ToArray();

    return orderedCounts;
    }

    long[] RemoveHighValues(long[] input, int maxDifference)
    {
    var min = input.Min();
    var max = input.Max();

    long[] filtered = input;

    while (max - min > maxDifference) // remove all values wich differ by more than maxDifference ticks
    {
    filtered = input.Where(x => x < max).ToArray();
    max = filtered.Max();
    }

    return filtered;

    }
    }
    }

    最佳答案

    您正在谈论的是每个方法调用的平均差异约为百分之一纳秒。 Windows并不是实时操作系统。这些测量结果将尽可能稳定。

    顺便说一句,the jitter will eliminate the bounds check inside your CastSafe method。如果您能找到比这更快的速度,我会感到非常惊讶。

    (如果瓶颈是CPU,那么您可以通过使用 Parallel.For 而不是普通的for循环来提高性能,但要确定您需要针对实际数据进行测试。例如,对于一个43整数的数组,而不是一个43,000,000整数的数组。)

    关于c# - 为什么性能指标有所不同?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6803662/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com