有趣的表演气味 [英] Interesting performance odities

查看:78
本文介绍了有趣的表演气味的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述




我最初在VS 2005中对C#的inlinine功能进行了基准测试,而我却在以下令人惊讶的结果中偶然发现了



请查看以下完整示例(您可以复制+粘贴

并自行编译)

Hi,

I was initially benchmarking inlinine capabilities of C# in VS 2005 while I
stumble on the following surprising results.

Please take a look at the following complete sample (you can just copy+paste
and compile it yourself)

>>>>>
>>>>>




使用系统;


公共接口I {void Do(); }


公共类A:我{public void Do(){}}

public struct B< T> :我{public void Do(){}}

公共类C:我{public virtual void Do(){}}


公共课测试

{

const int c = int.MaxValue / 10;


static public void试试(I f)

{

int s = Environment.TickCount;

for(int i = 0; i< c; ++ i)

f.Do();

int e = Environment.TickCount;

Console.WriteLine(es);

}


static public void GTry< F>(F f)其中F:I

{

int s =环境。 TickCount;

for(int i = 0; i< c; ++ i)

f.Do();

int e = Environment.TickCount;

Console.WriteLine(es);

}

}


课程计划

{


static void Main(string [] args)

{

Test .Try(new A());

Test.GTry(new A());

Test.Try(new B< int>());

Test.GTry(new B< int >());

Test.Try(new C());

}


}

<<<<<<<<<<<


在该计划中有3种类型:


一个非泛型接口

一个实现接口的非泛型类。

一个通用类也实现了接口。 />

所有的接口声明都是一个trival no-op方法。


有两个_exactly equivalent_方法只在那个方面有所不同

非常一般地使用接口,而另一个采用任何类型

一般实现接口。

方法只是在参数上调用一个简单的函数数字

次并输出经过的时间。


您是否预计在每种情况下拨打Do()的时间会有所不同?


我认真地不会(这不是一个虚拟的方法而且它是微不足道的)


现在编译发布模式(确保代码优化)并运行它

_outside IDE_。


我得到以下令人惊讶的结果:


1515

1516

2531

140

2109

为什么这令人惊讶?


一些东西:


(1)来自非泛型A类的方法在

调用中根本没有内联。


(2)泛型类B中的方法与虚拟方法一样慢从非通用接口调用时,
(与C类比较)

(A不会发生的事情)


(3)当调用泛型类B(非泛型类A上的非b $ b)和泛型参数(因此完全是
)时,该方法只能完全内联
保留其动态类型而不是界面。


任何人都可以帮助我理解它吗? [或在

基准测试中发现错误?]


TIA

-

费尔南多Cacciola

SciSoft
http://fcacciola.50webs.com /




using System;

public interface I { void Do () ; }

public class A : I { public void Do () {} }

public struct B<T> : I { public void Do () {} }

public class C : I { public virtual void Do () {} }

public class Test
{
const int c = int.MaxValue / 10 ;

static public void Try( I f )
{
int s = Environment.TickCount ;
for( int i = 0 ; i < c ; ++ i )
f.Do();
int e = Environment.TickCount ;
Console.WriteLine(e-s);
}

static public void GTry<F>( F f ) where F : I
{
int s = Environment.TickCount ;
for( int i = 0 ; i < c ; ++ i )
f.Do();
int e = Environment.TickCount ;
Console.WriteLine(e-s);
}
}

class Program
{

static void Main(string[] args)
{
Test.Try ( new A() );
Test.GTry( new A() );
Test.Try ( new B<int>() );
Test.GTry( new B<int>() );
Test.Try ( new C() );
}

}
<<<<<<<<<<<

In that program there are 3 types:

A a non-generic interface
A non-generic class implementing the interface.
And a generic class also implementing the interface.

All the interface declares is a trival no-op method.

There are two _exactly equivalent_ methods which differ ONLY in that one
takes the interface non-generically, and the other takes any type
implementing the interface generically.
The methods just call the trivial function on the parameter a number of
times and outputs the ellapsed time taken.

Do you expect any difference in the times taken to call Do() in each case?

I certianly wouldn''t (Do is not a virtual method and it''s trivial)

Now compile in release mode (making sure the code is optimized) and run it
_outside the IDE_.

I get the following surprising results:

1515
1516
2531
140
2109

Why is this surprising?

A few things:

(1) The method from the non-generic class A is not inlined at all in the
calls.

(2) The method in the generic-class B is as slow as if it were virtual
(compare it to class C) when called from the non-generic interface
(something which doesn''t happen with A)

(3) The method is ONLY fully inlined when called on the generic-class B (not
on the non-generic class A) AND from the a generic parameter (thus fully
retaining its dynamic type) rather than an interface.

Can anyone help me make sense out of it? [or spot a mistake in the
benchmarks?]

TIA
--
Fernando Cacciola
SciSoft
http://fcacciola.50webs.com/



推荐答案

费尔南多,


见内联:
Fernando,

See inline:
(1)在
调用中,非泛型类A的方法根本没有内联。


为什么会这样?您正在传递接口实现。接口上的

调用永远不会被JIT内联。

(2)泛型类B中的方法和它是虚拟的一样慢
(与C类比较)从非通用界面调用
(A不会发生的事情)


不,它是因为你正在将一个结构传递给B而且这个方法的非泛型版本中包含了b / b
。在通用的

版本中,参数不是盒装的。

(3)该方法仅在通用类B上调用时才完全内联
(不在非泛型类A)AND和一个泛型参数(因此
完全保留其动态类型)而不是一个接口。


这与通用无关。相反,它是B中的b
,公共方法作为实现公开,并且它不是虚拟的b $ b,因此,该方法可以内联(这并不是说它是/ b $ b ^是^内联,但它可能是因为它不是虚拟的。


希望这有帮助。


-

- Nicholas Paldino [.NET / C#MVP]

- mv*@spam.guard.caspershouse.com

" Fernando Cacciola" < FE *************** @ hotmail.com>在消息中写道

新闻:Oq **************** @ TK2MSFTNGP15.phx.gbl ...

我最初是在VS 2005中对C#的inlinine功能进行基准测试,而我偶然发现了以下令人惊讶的结果。

请查看以下完整示例(您可以只是复制+粘贴并自行编译)
(1) The method from the non-generic class A is not inlined at all in the
calls.
Why should it be? You are passing an interface implementation. The
call on the interface is never going to be inlined by the JIT.
(2) The method in the generic-class B is as slow as if it were virtual
(compare it to class C) when called from the non-generic interface
(something which doesn''t happen with A)
No, it is slow because you are passing a structure to B and it is
getting boxed in the non-generic version of the method. In the generic
version, the parameter is not boxed.
(3) The method is ONLY fully inlined when called on the generic-class B
(not on the non-generic class A) AND from the a generic parameter (thus
fully retaining its dynamic type) rather than an interface.
This has nothing to do with it being generic. Rather, it is a matter of
in B, the public method is exposed as the implementation, and it is not
virtual, so therefore, that method can be inlined (that''s not to say that it
^is^ inlined, but it is possible that it can be since it is not virtual).

Hope this helps.

--
- Nicholas Paldino [.NET/C# MVP]
- mv*@spam.guard.caspershouse.com
"Fernando Cacciola" <fe***************@hotmail.com> wrote in message
news:Oq****************@TK2MSFTNGP15.phx.gbl... Hi,

I was initially benchmarking inlinine capabilities of C# in VS 2005 while
I stumble on the following surprising results.

Please take a look at the following complete sample (you can just
copy+paste and compile it yourself)
>>>>>>
>>>>>>



使用System;

公共接口I {void Do();公共类A:我{public void Do(){}}
公共结构B< T> :我{public void Do(){}}
公共课C:我{public virtual void Do(){}}
公共课测试
{
const int c = int.MaxValue / 10;

static public void试试(I f)
{s / s int = Environment.TickCount;
for(int i = 0; i< c; ++ i)
f.Do();
int e = Environment.TickCount;
Console.WriteLine(es);
}

静态公共空虚GTry< F>(F f)其中F:I
{
int s = Environment.TickCount;
for(int i = 0; i< c; ++ i)
f.Do();
int e = Environment.TickCount;
Console.WriteLine(es);
} <课程计划
{

static void Main(string [] args)
{
Test.Try(new A());
Test.GTry(new A());
Test.Try(new B< int>());
Test.GTry(new B< int>( ));
Test.Try(new C());
}

}<<<<<<<<< ;<< ;<

在该程序中有3种类型:

非通用接口
实现接口的非泛型类。
还有一个通用类也实现了接口。

所有接口声明都是一个trival no-op方法。

有两个_exactly equivalent_方法只有一个不同
采用非泛型接口,另一种采用任何类型实现接口一般。
方法只需在参数上调用一些简单的函数,然后输出花了很多时间。

你认为在每种情况下调用Do()的时间有什么不同吗?

我认真地不会(不是虚拟的)方法,它是微不足道的。

现在在发布模式下编译(确保代码已经优化)并运行它
_在IDE_之外。

我得到以下令人惊讶的结果:

1515
2516
140
2109
为什么这令人惊讶?

一些事情:

任何人都可以帮助我理解它吗? [或在
基准测试中发现错误?]

TIA

-
Fernando Cacciola
SciSoft
http://fcacciola.50webs.com/

>



using System;

public interface I { void Do () ; }

public class A : I { public void Do () {} }

public struct B<T> : I { public void Do () {} }

public class C : I { public virtual void Do () {} }

public class Test
{
const int c = int.MaxValue / 10 ;

static public void Try( I f )
{
int s = Environment.TickCount ;
for( int i = 0 ; i < c ; ++ i )
f.Do();
int e = Environment.TickCount ;
Console.WriteLine(e-s);
}

static public void GTry<F>( F f ) where F : I
{
int s = Environment.TickCount ;
for( int i = 0 ; i < c ; ++ i )
f.Do();
int e = Environment.TickCount ;
Console.WriteLine(e-s);
}
}

class Program
{

static void Main(string[] args)
{
Test.Try ( new A() );
Test.GTry( new A() );
Test.Try ( new B<int>() );
Test.GTry( new B<int>() );
Test.Try ( new C() );
}

}
<<<<<<<<<<<

In that program there are 3 types:

A a non-generic interface
A non-generic class implementing the interface.
And a generic class also implementing the interface.

All the interface declares is a trival no-op method.

There are two _exactly equivalent_ methods which differ ONLY in that one
takes the interface non-generically, and the other takes any type
implementing the interface generically.
The methods just call the trivial function on the parameter a number of
times and outputs the ellapsed time taken.

Do you expect any difference in the times taken to call Do() in each case?

I certianly wouldn''t (Do is not a virtual method and it''s trivial)

Now compile in release mode (making sure the code is optimized) and run it
_outside the IDE_.

I get the following surprising results:

1515
1516
2531
140
2109

Why is this surprising?

A few things:
Can anyone help me make sense out of it? [or spot a mistake in the
benchmarks?]

TIA
--
Fernando Cacciola
SciSoft
http://fcacciola.50webs.com/




Nicholas Paldino [.NET / C#MVP]写道:
Nicholas Paldino [.NET/C# MVP] wrote:
费尔南多,

见内联:
Fernando,

See inline:
(1)来自非泛型A类的方法在调用中根本没有内联。
为什么会这样?您正在传递接口实现。接口上的
调用永远不会被JIT内联。
(1) The method from the non-generic class A is not inlined at all in
the calls.
Why should it be? You are passing an interface implementation. The
call on the interface is never going to be inlined by the JIT.



我肯定期望从接口调用的方法被内联,但是,好吧,

现在我很遗憾知道他们不会。


I surely expected methods called from interfaces to be inlined, but, well,
now I regret to know they won''t.

(2)泛型类B中的方法与如果它是非虚拟的(与C类相比)从非通用界面调用时(A不会发生的事情)
(2) The method in the generic-class B is as slow as if it were
virtual (compare it to class C) when called from the non-generic
interface (something which doesn''t happen with A)



不,它很慢,因为你将一个结构传递给B并且它在方法的非泛型版本中被装箱。



No, it is slow because you are passing a structure to B and it is
getting boxed in the non-generic version of the method.




你是说当你将一个值类型传递给接受

接口的方法时,该值被装箱了?

我肯定没想到。

另外,即使你是对的,差异是1秒,当函数对象传递给Try时,最多只有1个单一拳击。

>
方法。所以不,你的解释并不能解释差异。

如果你不同意,请参见下文。

在通用版本中,参数不是盒装的。



Are you saying that when you pass a value-type to a method accepting an
interface, the value is boxed?
I surely never expected that.
Also, even if you were right, the difference is 1 second and there is at
most just 1 single boxing when the function object is passed to the Try
method. So no, your explanation doesn''t account for the difference.
See below if you disagree.
In the generic version, the parameter is not boxed.

(3)当在
泛型类B(不在非泛型类A上)和从
泛型参数调用时,该方法仅完全内联(因此完全保留其动态类型)而不是界面。
(3) The method is ONLY fully inlined when called on the
generic-class B (not on the non-generic class A) AND from the a
generic parameter (thus fully retaining its dynamic type) rather
than an interface.



这与通用无关。相反,它是B中的一个问题,公共方法作为实现公开,并且它不是虚拟的,因此,该方法可以内联
(即'不是说它是^内联,但它可能是因为它不是虚拟的。)



This has nothing to do with it being generic. Rather, it is a
matter of in B, the public method is exposed as the implementation,
and it is not virtual, so therefore, that method can be inlined
(that''s not to say that it ^is^ inlined, but it is possible that it
can be since it is not virtual).



对我来说它与它有关泛型(编译器看到

实现_precisely_因为它没有处理接口而是具体类型的

)(这就是为什么我提到它的动态类型是完全的

保留)

也许我应该说这个非常特别的结果是我预计的唯一一个


我改变了我的代码,这样B就不再是一个结构了,而是一个类(这是多年C ++样本片段中的一个机械错误)


这些是新结果:


1484

1484

2078

3235

2062

请注意


Test.Try(new B< int>());


仍然和虚拟通话一样慢。


但是......内心电话的内容发生了什么!! ??

为什么

Test.GTry(新B< int>());


现在比虚拟电话更多_ven?我只是把B从一个结构改为

到了一个类。


困惑

-

Fernando Cacciola

SciSoft
http://fcacciola.50webs。 com /



To me it has to do with it being generic (the compiler sees the
implementation _precisely_ because its not dealing with an interface but a
concrete type) (that''s why I mentioned that its dynamic type was fully
retained)
Maybe I should have said that this very particular result was the only one I
expected.

I changed my code so that B is not a struct anymore but a class (that was a
mechanical mistake from years of C++ sample snippets)

These are the new results:

1484
1484
2078
3235
2062

Notice that

Test.Try ( new B<int>() );

Is still as slow as the virtual call.

But... What happened to the greately inlined call!!??
Why is
Test.GTry ( new B<int>() );

now taking _ven more_than the virtual call? I just changed B from a struct
to a class.

Puzzled
--
Fernando Cacciola
SciSoft
http://fcacciola.50webs.com/



Fernando,
Fernando,
我当然希望从接口调用的方法内联,但是,好吧,现在我很遗憾知道他们不会。


不,JIT不会内联它们。

你是说当你将值类型传递给接受
接口的方法时,价值是盒装?


绝对。

此外,即使你是对的,差异是1秒,并且最多只有1个拳击时函数对象传递给Try
方法。所以不,你的解释并没有说明差异。
如果你不同意,请参阅下文。


只有一个拳击,但你必须记住,一切都在

大量。你的数学有点偏差。


两者之间有1016个差异。你不是什么

因为这个方法中有214,748,364个调用!!!!

如果你将1016个分数除以,那么你得出的是4.73 e-6这是每次通话额外的4.73e-9美元
!这是不到十亿美元的第二个百万美元。


是的,你是对的,这不是因为拳击,但你可以''只是

忽略你迭代的次数,而不是把它重新计入每个电话号码的




另外,你应该使用System.Diagnostics中的新秒表类来确定这些东西。计数器基于性能

计数器,这更准确。

对我而言,它与通用(编译器看到
实现)有关_precisely_因为它不涉及界面而是具体类型)(这就是为什么我提到它的动态类型完全保留了)
也许我应该说这个非常特别结果是我所期待的唯一一个。


是的,但它仍然与通用无关。如果方法

不是虚拟的,或者是一个接口实现,那么它就不会被内联。

-

- Nicholas Paldino [.NET / C#MVP]

- mv * @ spam。 guard.caspershouse.com


" Fernando Cacciola" < FE *************** @ hotmail.com>在消息中写道

新闻:eZ **************** @ tk2msftngp13.phx.gbl ... Nicholas Paldino [.NET / C#MVP]写道:
I surely expected methods called from interfaces to be inlined, but, well,
now I regret to know they won''t.
Nope, the JIT will not inline them.
Are you saying that when you pass a value-type to a method accepting an
interface, the value is boxed?
Absolutely.
Also, even if you were right, the difference is 1 second and there is at
most just 1 single boxing when the function object is passed to the Try
method. So no, your explanation doesn''t account for the difference.
See below if you disagree.
There is only one boxing, but you have to remember, everything counts in
large amounts. Your math is a little off.

There is a difference of 1016 ticks between the two. What you are not
factoring in is that there are 214,748,364 calls occuring in the method!!!!
If you divide the 1016 ticks by that, you come out with 4.73e-6 which is
4.73e-9 extra seconds per call! That''s a little less than one billionth of
a second.

Yes, you are right, it is not because of boxing, but you can''t just
ignore the number of times you iterate and not factor it back into a
per-call number.

Also, you should use the new Stopwatch class in System.Diagnostics in
order to time these things. The counter is based on the performance
counter, which is more accurate.
To me it has to do with it being generic (the compiler sees the
implementation _precisely_ because its not dealing with an interface but a
concrete type) (that''s why I mentioned that its dynamic type was fully
retained)
Maybe I should have said that this very particular result was the only one
I expected.
Yes, but it still has nothing to do with being generic. If the method
is not virtual, or an interface implementation, then there is no chance that
it will be inlined.
--
- Nicholas Paldino [.NET/C# MVP]
- mv*@spam.guard.caspershouse.com

"Fernando Cacciola" <fe***************@hotmail.com> wrote in message
news:eZ****************@tk2msftngp13.phx.gbl... Nicholas Paldino [.NET/C# MVP] wrote:
Fernando,

见内联:
Fernando,

See inline:
(1)非泛型A类的方法根本没有内联在电话中。
(1) The method from the non-generic class A is not inlined at all in
the calls.



为什么会这样?您正在传递接口实现。接口上的
调用永远不会被JIT内联。



Why should it be? You are passing an interface implementation. The
call on the interface is never going to be inlined by the JIT.


(2)中的方法当从非泛型接口调用时,泛型类B的速度与它是虚拟的(与C类比较)一样慢(对于A不会发生这种情况)
(2) The method in the generic-class B is as slow as if it were
virtual (compare it to class C) when called from the non-generic
interface (something which doesn''t happen with A)



不,它很慢,因为你将结构传递给B,并且它在方法的非泛型版本中被装箱。



No, it is slow because you are passing a structure to B and it is
getting boxed in the non-generic version of the method.



我当然没想到。



I surely never expected that.

在通用版本中,参数没有装箱。
In the generic version, the parameter is not boxed.
(3)该方法仅限完全内联时调用
泛型类B(不是非泛型类A)和一个
泛型参数(因此完全保留其动态类型)而不是接口。
(3) The method is ONLY fully inlined when called on the
generic-class B (not on the non-generic class A) AND from the a
generic parameter (thus fully retaining its dynamic type) rather
than an interface.



这与通用无关。相反,它是B中的一个问题,公共方法作为实现公开,并且它不是虚拟的,因此,该方法可以内联
(即'不是说它是^内联,但它可能是因为它不是虚拟的。



This has nothing to do with it being generic. Rather, it is a
matter of in B, the public method is exposed as the implementation,
and it is not virtual, so therefore, that method can be inlined
(that''s not to say that it ^is^ inlined, but it is possible that it
can be since it is not virtual).


对我来说它与通用(编译器)有关看到
实现_precisely_因为它没有处理接口而是具体类型)(这就是为什么我提到它的动态类型完全保留了
也许我应该说这个非常特别的结果是我所期待的唯一一个结果。

我改变了我的代码,这样B就不再是一个结构了,而是一个类(那个是
一个来自多年C ++样本片段的机械错误)

这些是新结果:

1484
1484
2078
3235 2062

注意

Test.Try(新B< int>());

仍然和虚拟一样慢打电话。

但是......内心的电话怎么了!! ??
为什么

Test.GTry(新B< int>() );

现在比虚拟电话更多_ven?我刚刚将B从一个结构改为一个类。

困惑

-
Fernando Cacciola
SciSoft
< a rel =nofollowhref =http://fcacciola.50webs.com/target =_ blank> http://fcacciola.50webs.com/


To me it has to do with it being generic (the compiler sees the
implementation _precisely_ because its not dealing with an interface but a
concrete type) (that''s why I mentioned that its dynamic type was fully
retained)
Maybe I should have said that this very particular result was the only one
I expected.

I changed my code so that B is not a struct anymore but a class (that was
a mechanical mistake from years of C++ sample snippets)

These are the new results:

1484
1484
2078
3235
2062

Notice that

Test.Try ( new B<int>() );

Is still as slow as the virtual call.

But... What happened to the greately inlined call!!??
Why is
Test.GTry ( new B<int>() );

now taking _ven more_than the virtual call? I just changed B from a struct
to a class.

Puzzled
--
Fernando Cacciola
SciSoft
http://fcacciola.50webs.com/



这篇关于有趣的表演气味的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆