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);