Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/282.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C#性能分析-如何计算CPU周期?_C#_Performance_Casting - Fatal编程技术网

C#性能分析-如何计算CPU周期?

C#性能分析-如何计算CPU周期?,c#,performance,casting,C#,Performance,Casting,这是进行性能分析的有效方法吗?我想获得纳秒精度并确定类型转换的性能: class PerformanceTest { static double last = 0.0; static List<object> numericGenericData = new List<object>(); static List<double> numericTypedData = new List<double>(); stat

这是进行性能分析的有效方法吗?我想获得纳秒精度并确定类型转换的性能:

class PerformanceTest
{
    static double last = 0.0;
    static List<object> numericGenericData = new List<object>();
    static List<double> numericTypedData = new List<double>();

    static void Main(string[] args)
    {
        double totalWithCasting = 0.0;
        double totalWithoutCasting = 0.0;
        for (double d = 0.0; d < 1000000.0; ++d)
        {
            numericGenericData.Add(d);
            numericTypedData.Add(d);
        }
        Stopwatch stopwatch = new Stopwatch();
        for (int i = 0; i < 10; ++i)
        {

            stopwatch.Start();
            testWithTypecasting();
            stopwatch.Stop();
            totalWithCasting += stopwatch.ElapsedTicks;

            stopwatch.Start();
            testWithoutTypeCasting();
            stopwatch.Stop();
            totalWithoutCasting += stopwatch.ElapsedTicks;
        }

        Console.WriteLine("Avg with typecasting = {0}", (totalWithCasting/10));
        Console.WriteLine("Avg without typecasting = {0}", (totalWithoutCasting/10));
        Console.ReadKey();
    }

    static void testWithTypecasting()
    {
        foreach (object o in numericGenericData)
        {
            last = ((double)o*(double)o)/200;
        }
    }

    static void testWithoutTypeCasting()
    {
        foreach (double d in numericTypedData)
        {
            last = (d * d)/200;
        }
    }
}
我有点怀疑。。。看起来对性能几乎没有影响。铸造真的那么便宜吗

更新:

class PerformanceTest
{
    static double last = 0.0;
    static object[] numericGenericData = new object[100000];
    static double[] numericTypedData = new double[100000];

    static Stopwatch stopwatch = new Stopwatch();
    static double totalWithCasting = 0.0;
    static double totalWithoutCasting = 0.0;
    static void Main(string[] args)
    {
        for (int i = 0; i < 100000; ++i)
        {
            numericGenericData[i] = (double)i;
            numericTypedData[i] = (double)i;
        }

        for (int i = 0; i < 10; ++i)
        {
            stopwatch.Start();
            testWithTypecasting();
            stopwatch.Stop();
            totalWithCasting += stopwatch.ElapsedTicks;
            stopwatch.Reset();

            stopwatch.Start();
            testWithoutTypeCasting();
            stopwatch.Stop();
            totalWithoutCasting += stopwatch.ElapsedTicks;
            stopwatch.Reset();
        }

        Console.WriteLine("Avg with typecasting = {0}", (totalWithCasting/(10.0)));
        Console.WriteLine("Avg without typecasting = {0}", (totalWithoutCasting / (10.0)));
        Console.ReadKey();
    }

    static void testWithTypecasting()
    {
        foreach (object o in numericGenericData)
        {
            last = ((double)o * (double)o) / 200;
        }
    }

    static void testWithoutTypeCasting()
    {
        foreach (double d in numericTypedData)
        {
            last = (d * d) / 200;
        }
    }
}
1) 是的,铸造通常(非常)便宜

2) 在托管语言中,您不会获得纳秒级的精确度。或者在大多数操作系统下使用非托管语言

考虑

  • 其他过程
  • 垃圾收集
  • 不同的紧张
  • 不同的CPU

而且,您的测量包括foreach循环,在我看来是50%或更多。可能是90%。

请注意,您测量的不是类型转换,而是拆箱。这些值一直是双倍的,没有进行类型转换

您忘记在两次测试之间重置秒表,因此您在一次又一次地添加以前所有测试的累积时间。如果您将滴答声转换为实际时间,您会看到它的总和远远超过运行测试所花费的时间

如果添加一个
stopwatch.Reset()在每个
秒表开始()之前,则会得到更合理的结果,如:

Avg with typecasting = 41027,1
Avg without typecasting = 20594,3
取消绑定值并不昂贵,它只需检查对象中的数据类型是否正确,然后获取值。不过,这比已知类型时要做的工作多得多。请记住,您也在测量结果的循环、计算和分配,这两个测试都是相同的


装箱一个值比取消装箱要昂贵,因为这会在堆上分配一个对象。

当您调用Stopwatch.Start时,它会让计时器从停止的地方继续运行。您需要调用Stopwatch.Reset()将计时器设置回零,然后再重新启动。就我个人而言,每当我想启动计时器时,我都会使用stopwatch=stopwatch.StartNew(),以避免这种混乱

此外,您可能希望在开始“计时循环”之前调用这两种测试方法,以便它们有公平的机会“预热”这段代码,并确保JIT有机会运行到公平的环境中

当我在我的机器上这样做的时候,我看到testWithTypecasting运行的时间大约是testWithTypecasting运行时间的一半


尽管如此,演员本身不太可能成为表现惩罚中最重要的部分。testWithTypecasting方法在装箱双精度列表上运行,这意味着除了增加内存消耗总量之外,还需要额外的间接级别来检索每个值(在内存中其他地方引用该值)。这会增加内存访问所花费的时间,可能比“在强制转换”本身所花费的CPU时间影响更大。

查看系统中的性能计数器。Diagnostics命名空间在创建新计数器时,首先创建一个类别,然后指定一个或多个计数器放置在其中

    // Create a collection of type CounterCreationDataCollection.
System.Diagnostics.CounterCreationDataCollection CounterDatas = 
   new System.Diagnostics.CounterCreationDataCollection();
// Create the counters and set their properties.
System.Diagnostics.CounterCreationData cdCounter1 = 
   new System.Diagnostics.CounterCreationData();
System.Diagnostics.CounterCreationData cdCounter2 = 
   new System.Diagnostics.CounterCreationData();
cdCounter1.CounterName = "Counter1";
cdCounter1.CounterHelp = "help string1";
cdCounter1.CounterType = System.Diagnostics.PerformanceCounterType.NumberOfItems64;
cdCounter2.CounterName = "Counter2";
cdCounter2.CounterHelp = "help string 2";
cdCounter2.CounterType = System.Diagnostics.PerformanceCounterType.NumberOfItems64;
// Add both counters to the collection.
CounterDatas.Add(cdCounter1);
CounterDatas.Add(cdCounter2);
// Create the category and pass the collection to it.
System.Diagnostics.PerformanceCounterCategory.Create(
   "Multi Counter Category", "Category help", CounterDatas);

请参见

只是一个想法,但有时相同的机器代码可能需要不同数量的周期来执行,这取决于其在内存中的对齐方式,因此您可能需要添加一个或多个控件。

不要自己“做”C#,但对于x86-32和更高版本,在C中通常可以使用rdtsc指令,这比操作系统时钟更精确。通过搜索stackoverflow可以找到关于rdtsc的更多信息。在C语言下,它通常作为一个内在函数或内置函数提供,并返回自计算机通电以来的时钟周期数(8字节长/_int64无符号整数)。因此,如果CPU的时钟速度为3 Ghz,则底层计数器每秒递增30亿次。除了一些早期的AMD处理器外,所有多核CPU的计数器都将同步

如果C没有它,你可以考虑写一个很短的C函数来从C中访问它。如果通过函数或内联访问指令,则会产生大量开销。对函数的两个背对背调用之间的差异将是基本的度量开销。如果您正在考虑对应用程序进行计量,则必须确定几个更复杂的开销值


<>你可以考虑关闭CPU节能模式(并重新启动PC),因为它降低了在低活动周期期间被馈送到CPU的时钟频率。这是因为它会导致不同内核的时间戳计数器不同步。

请注意。也许不需要说明,但是当您进行此类测试时,请确保您是在发布模式下编译的。你无法想象调试过程中幕后发生的事情。就像我说的,也许不需要说明,但可能值得一提。@Dested我在发布和调试模式下尝试了它,结果没有什么不同。对我来说,类型转换看起来快了近3倍。我遗漏了什么吗?在输出中对齐“=”符号会有帮助。@Lirik:不,打字速度不快。时间的测量是不正确的。看到我下面的答案。你是对的,当我在循环中移动秒表时,我看到了一个显著的性能差异:带类型转换的平均值=129570.35537不带类型转换的平均值=378055.40987类型转换几乎慢4倍!除此之外,当Sw位于环路内时,您正在测量秒表伪影。你的结果可重复吗>@Henk是的,我多次运行这个应用程序(发布和调试模式),结果都是一样的。@Henk-Doh!我以为上面说的是一百二十万。。。现在我又看了一遍,我注意到我的眼睛在捉弄我。。。它更快!!可能吗?!?!我应该通过打字来提高性能吗?请注意,循环中没有强制转换,只有取消绑定。
Avg with typecasting = 41027,1
Avg without typecasting = 20594,3
    // Create a collection of type CounterCreationDataCollection.
System.Diagnostics.CounterCreationDataCollection CounterDatas = 
   new System.Diagnostics.CounterCreationDataCollection();
// Create the counters and set their properties.
System.Diagnostics.CounterCreationData cdCounter1 = 
   new System.Diagnostics.CounterCreationData();
System.Diagnostics.CounterCreationData cdCounter2 = 
   new System.Diagnostics.CounterCreationData();
cdCounter1.CounterName = "Counter1";
cdCounter1.CounterHelp = "help string1";
cdCounter1.CounterType = System.Diagnostics.PerformanceCounterType.NumberOfItems64;
cdCounter2.CounterName = "Counter2";
cdCounter2.CounterHelp = "help string 2";
cdCounter2.CounterType = System.Diagnostics.PerformanceCounterType.NumberOfItems64;
// Add both counters to the collection.
CounterDatas.Add(cdCounter1);
CounterDatas.Add(cdCounter2);
// Create the category and pass the collection to it.
System.Diagnostics.PerformanceCounterCategory.Create(
   "Multi Counter Category", "Category help", CounterDatas);