|
|
Posted on 2006-12-07 10:14 无常 阅读(9206) 评论(72) 编辑 收藏 网摘 所属分类: dot net
一直以来只知道托管代码的效率要比非托管代码低,至于低多少也没有可参考的数据。今天在csdn看到的英特尔多核平台编程优化大赛的广告,把里面的代码下载回来,分别用非托管c/托管cpp/c#做了个简略的性能测试,不比不知道,一比吓了一跳。且看数据说话。
第一步:原始代码如:
 /**//* compute the potential energy of a collection of */
 /**//* particles interacting via pairwise potential */
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <windows.h>
#include <time.h>
#define NPARTS 1000
#define NITER 201
#define DIMS 3
int rand( void );
int computePot(void);
void initPositions(void);
void updatePositions(void);
double r[DIMS][NPARTS];
double pot;
double distx, disty, distz, dist;

 int main() {
int i;
clock_t start, stop;

initPositions();
updatePositions();
start=clock();
 for( i=0; i<NITER; i++ ) {
pot = 0.0;
computePot();
if (i%10 == 0) printf("%5d: Potential: %10.3f\n", i, pot);
updatePositions();
}
stop=clock();
printf ("Seconds = %10.9f\n",(double)(stop-start)/ CLOCKS_PER_SEC);
int e;
scanf("%d",&e);
}
 void initPositions() {
int i, j;
for( i=0; i<DIMS; i++ )
for( j=0; j<NPARTS; j++ )
r[i][j] = 0.5 + ( (double) rand() / (double) RAND_MAX );
}
 void updatePositions() {
int i, j;
for( i=0; i<DIMS; i++ )
for( j=0; j<NPARTS; j++ )
r[i][j] -= 0.5 + ( (double) rand() / (double) RAND_MAX );
}
 int computePot() {
int i, j;

 for( i=0; i<NPARTS; i++ ) {
 for( j=0; j<i-1; j++ ) {
distx = pow( (r[0][j] - r[0][i]), 2 );
disty = pow( (r[1][j] - r[1][i]), 2 );
distz = pow( (r[2][j] - r[2][i]), 2 );
dist = sqrt( distx + disty + distz );
pot += 1.0 / dist;
}
}
return 0;
}

执行结果如下:
执行时间4.609s。
第二步:托管
新建一个 C++ CLR Console Aplication,命名为mcpp。打开mcpp.cpp文件,将原始代码粘贴进来即可(代码太长这里就不贴出来了,可以在全贴下面的下载全部源码)。
执行结果如下:
执行时间:15.1720s。
第二步:c#
笔者将原始代码翻译成CS代码,如下:
using System;
using System.Collections.Generic;
using System.Text;


namespace cs
  {
class Program
 {
private const int RAND_MAX = 0x7fff;
private const int NPARTS = 1000;
private const int NITER = 201;
private const int DIMS = 3;
private double pot;
private double distx, disty, distz, dist;
private Random random = new Random(Environment.TickCount);
private double[][] r = new double[DIMS][];

public void main()
 {
int i;
int start, stop;

for (int ii = 0; ii < DIMS; ii++)
 {
r[ii] = new double[NPARTS];
}

initPositions();
updatePositions();

start = Environment.TickCount;
for (i = 0; i < NITER; i++)
 {
pot = 0.0;
computePot();
if (i % 10 == 0)
Console.WriteLine("{0}: Potential: {1:##########.###}", i, pot);
updatePositions();
}
stop = Environment.TickCount;
Console.WriteLine("Seconds = {0:##########.#########}", (double)(stop - start)/1000);
}

private void computePot()
 {
int i, j;

for (i = 0; i < NPARTS; i++)
 {
for (j = 0; j < i - 1; j++)
 {
distx = Math.Pow((r[0][j] - r[0][i]), 2);
disty = Math.Pow((r[1][j] - r[1][i]), 2);
distz = Math.Pow((r[2][j] - r[2][i]), 2);
dist = Math.Sqrt(distx + disty + distz);
pot += 1.0 / dist;
}
}
}

private void updatePositions()
 {
int i, j;

for (i = 0; i < DIMS; i++)
for (j = 0; j < NPARTS; j++)
r[i][j] -= 0.5 + ((double)random.Next(RAND_MAX)/(double)RAND_MAX);
}

private void initPositions()
 {
int i, j;
for (i = 0; i < DIMS; i++)
for (j = 0; j < NPARTS; j++)
r[i][j] = 0.5 + ((double)random.Next(RAND_MAX)/(double)RAND_MAX);
}

static void Main(string[] args)
 {
Program p = new Program();
p.main();
Console.ReadLine();

}
}
}

执行结果如下:
全部代码下载
Feedback
我在vista上运行的是62.619 看来机器差不多,呵呵
我觉得直接把代码对应翻译成C#可能会有些不公平。
同样的语句,不同的编译器产生的结果是有不同的。
当然,如此大的差距确实也很难解释成编译器的问题……
嗯,的确,不能直接转换,
再者本来效率就不是托管的优势
win32\win32.vcproj
文件有些什么内容??
@Boolean
的确很难解释
@ddee
@lymph
我并没有贬低托管代码和c#的意思,我也喜欢c#的语法和开发效率,转到.net平台以来一直也只和c#。
@CCC[匿名]
这个项目没用,删掉不影响
我觉得有点好笑!不要见怪
因为我一直以来认为,无论用C#还是C写一些东西都是用来解决一些问题的(除非是一个数学家,才会如你所是,用来计算)否则就会关系到很多方面的,用得最多的估计是数据库方面的了,当你考虑到一个真正的应用的时候,就不是你的那些测试数据了,这个差距不会很大的。
航天奇侠说的对,明显不公平,等会用dotTrace看看那里慢了。
我的速度c++ : Seconds = 5.844000000
配置: AMD 1.8G 内存: 1G 看来双核3G也没什么了不起嘛。
把Math.Pow()调用改成直接相乘.
C#同样是4秒多.
可见语言对效率的影响是微不足道的,最重要的是对语言的用法,呃...还是不得不承认,看来.net framework库的编写者对某些情况下的优化还是不够.
@deerchao
结果相近,我开始质疑Math内由内核实现的方法了。
这段C#中可以做下一下改进,用二维数组的声明方式。而不是交错数组。
根本不公平
只是装箱拆箱就要费好多资源
而且这里根本不用封装成一个类
干嘛非要封装后再实例化再去调用
而且.NET第一次编译肯定慢,编译后再重新运行一次,就会知道了
using System;
static class Program
{
static int RAND_MAX = 0x7fff;
static int NPARTS = 1000;
static int NITER = 201;
static int DIMS = 3;
static double pot;
static double distx, disty, distz, dist;
static double[,] r = new double[DIMS, NPARTS];
static int CLOCKS_PER_SEC = 1000;
static Random random = new Random();
static void Main()
{
int i;
int start, stop;
initPositions();
updatePositions();
start = Environment.TickCount;
for (i = 0; i < NITER; i++)
{
pot = 0.0;
computePot();
if (i % 10 == 0) Console.WriteLine("{0}: Potential: {1:##########.###}", i, pot);
updatePositions();
}
stop = Environment.TickCount;
Console.WriteLine("Seconds = {0:##########.#########}", (double)(stop - start) / CLOCKS_PER_SEC);
Console.ReadLine();
}
static void initPositions()
{
for (int i = 0; i < DIMS; i++)
for (int j = 0; j < NPARTS; j++)
r[i,j] = 0.5 + ((double)random.Next(RAND_MAX) / (double)RAND_MAX);
}
static void updatePositions()
{
for (int i = 0; i < DIMS; i++)
for (int j = 0; j < NPARTS; j++)
r[i,j] -= 0.5 + ((double)random.Next(RAND_MAX) / (double)RAND_MAX);
}
static int computePot()
{
for (int i = 0; i < NPARTS; i++)
{
for (int j = 0; j < i - 1; j++)
{
distx = (r[0, j] - r[0, i]) * (r[0, j] - r[0, i]);
disty = (r[1, j] - r[1, i]) * (r[1, j] - r[1, i]);
distz = (r[2, j] - r[2, i]) * (r[2, j] - r[2, i]);
//distx = Math.Pow((r[0,j] - r[0,i]), 2.0);
//disty = Math.Pow((r[1,j] - r[1,i]), 2.0);
//distz = Math.Pow((r[2,j] - r[2,i]), 2.0);
dist = Math.Sqrt(distx + disty + distz);
pot += 1.0 / dist;
}
}
return 0;
}
}
@freetofly
1、这里没涉及到装箱拆箱操作吧?
2、计时器是在创建了对象之后开始计的
3、“而且.NET第一次编译肯定慢,编译后再重新运行一次,就会知道了”?
好像winform没这种说法吧?
90%以上的时间集中在computePot()里,大家快想办法优化
非常有趣的问题
今天在公司内部讨论了一把,中间可以学到不少东西的。改天有时间整理出来
@
将Math.Pow改用相乘后,c#执行时间在8.5s左右,代码如下:
 将pow改用乘

namespace cs
  {
class Program
 {
private const int RAND_MAX = 0x7fff;
private const int NPARTS = 1000;
private const int NITER = 201;
private const int DIMS = 3;
private double pot;
private double distx, disty, distz, dist;
private Random random = new Random(Environment.TickCount);
private double[][] r = new double[DIMS][];

public void main()
 {
int i;
int start, stop;

for (int ii = 0; ii < DIMS; ii++)
 {
r[ii] = new double[NPARTS];
}

initPositions();
updatePositions();

start = Environment.TickCount;
for (i = 0; i < NITER; i++)
 {
pot = 0.0;
computePot();
if (i % 10 == 0)
Console.WriteLine("{0}: Potential: {1:##########.###}", i, pot);
updatePositions();
}
stop = Environment.TickCount;
Console.WriteLine("Seconds = {0:##########.#########}", (double)(stop - start)/1000);
}

private void computePot()
 {
int i, j;

for (i = 0; i < NPARTS; i++)
 {
for (j = 0; j < i - 1; j++)
 {
//distx = Math.Pow((r[0][j] - r[0][i]), 2);
//disty = Math.Pow((r[1][j] - r[1][i]), 2);
//distz = Math.Pow((r[2][j] - r[2][i]), 2);
distx = (r[0][j] - r[0][i])*(r[0][j] - r[0][i]);
disty = (r[1][j] - r[1][i])*(r[1][j] - r[1][i]);
distz = (r[2][j] - r[2][i])*(r[2][j] - r[2][i]);

dist = Math.Sqrt(distx + disty + distz);
pot += 1.0 / dist;
}
}
}

private void updatePositions()
 {
int i, j;

for (i = 0; i < DIMS; i++)
for (j = 0; j < NPARTS; j++)
r[i][j] -= 0.5 + ((double)random.Next(RAND_MAX)/(double)RAND_MAX);
}

private void initPositions()
 {
int i, j;
for (i = 0; i < DIMS; i++)
for (j = 0; j < NPARTS; j++)
r[i][j] = 0.5 + ((double)random.Next(RAND_MAX) / (double)RAND_MAX);
}

static void Main(string[] args)
 {
Program p = new Program();
p.main();
Console.ReadLine();

}
}
}

To
让你更意想不到的结果,再改用二维数组后c#执行时间在12.5s左右。代码如下:
 将交错数组改用二维数组
namespace cs
  {
class Program
 {
private const int RAND_MAX = 0x7fff;
private const int NPARTS = 1000;
private const int NITER = 201;
private const int DIMS = 3;
private double pot;
private double distx, disty, distz, dist;
private Random random = new Random(Environment.TickCount);
private double[,] r = new double[DIMS, NPARTS];

public void main()
 {
int i;
int start, stop;

initPositions();
updatePositions();

start = Environment.TickCount;
for (i = 0; i < NITER; i++)
 {
pot = 0.0;
computePot();
if (i % 10 == 0)
Console.WriteLine("{0}: Potential: {1:##########.###}", i, pot);
updatePositions();
}
stop = Environment.TickCount;
Console.WriteLine("Seconds = {0:##########.#########}", (double)(stop - start)/1000);
}

private void computePot()
 {
int i, j;

for (i = 0; i < NPARTS; i++)
 {
for (j = 0; j < i - 1; j++)
 {
//distx = Math.Pow((r[0][j] - r[0][i]), 2);
//disty = Math.Pow((r[1][j] - r[1][i]), 2);
//distz = Math.Pow((r[2][j] - r[2][i]), 2);
distx = (r[0,j] - r[0,i])*(r[0,j] - r[0,i]);
disty = (r[1,j] - r[1,i])*(r[1,j] - r[1,i]);
distz = (r[2,j] - r[2,i])*(r[2,j] - r[2,i]);

dist = Math.Sqrt(distx + disty + distz);
pot += 1.0 / dist;
}
}
}

private void updatePositions()
 {
int i, j;

for (i = 0; i < DIMS; i++)
for (j = 0; j < NPARTS; j++)
r[i,j] -= 0.5 + ((double)random.Next(RAND_MAX)/(double)RAND_MAX);
}

private void initPositions()
 {
int i, j;
for (i = 0; i < DIMS; i++)
for (j = 0; j < NPARTS; j++)
r[i,j] = 0.5 + ((double)random.Next(RAND_MAX) / (double)RAND_MAX);
}

static void Main(string[] args)
 {
Program p = new Program();
p.main();
Console.ReadLine();

}
}
}
改用相乘后的mcpp执行时间在8.9s左右,代码如下:
 改用相乘的cpp代码
// mcpp.cpp : main project file.

#include "stdafx.h"

#define NPARTS 1000
#define NITER 201
#define DIMS 3

int rand( void );
int computePot(void);
void initPositions(void);
void updatePositions(void);

double r[DIMS][NPARTS];
double pot;
double distx, disty, distz, dist;


using namespace System;

int main(array<System::String ^> ^args)
  {
int i;
clock_t start, stop;

initPositions();
updatePositions();

start=clock();
 for( i=0; i<NITER; i++ ) {
pot = 0.0;
computePot();
if (i%10 == 0) printf("%5d: Potential: %10.3f\n", i, pot);
updatePositions();
}
stop=clock();
printf ("Seconds = %10.9f\n",(double)(stop-start)/ CLOCKS_PER_SEC);
int e;
scanf("%d",&e);
}

 void initPositions() {
int i, j;

for( i=0; i<DIMS; i++ )
for( j=0; j<NPARTS; j++ )
r[i][j] = 0.5 + ( (double) rand() / (double) RAND_MAX );
}


 void updatePositions() {
int i, j;

for( i=0; i<DIMS; i++ )
for( j=0; j<NPARTS; j++ )
r[i][j] -= 0.5 + ( (double) rand() / (double) RAND_MAX );
}


 int computePot() {
int i, j;

 for( i=0; i<NPARTS; i++ ) {
 for( j=0; j<i-1; j++ ) {
//distx = pow( (r[0][j] - r[0][i]), 2 );
//disty = pow( (r[1][j] - r[1][i]), 2 );
//distz = pow( (r[2][j] - r[2][i]), 2 );
distx = (r[0][j] - r[0][i])*(r[0][j] - r[0][i]);
disty = (r[1][j] - r[1][i])*(r[1][j] - r[1][i]);
distz = (r[2][j] - r[2][i])*(r[2][j] - r[2][i]);

dist = sqrt( distx + disty + distz );

pot += 1.0 / dist;
}
}
return 0;
}

因为mcpp里的pow实现是用内联方式实现,所以改用相乘效果并不是很明显。
 math.h中pow函数的实现
template<class _Ty> inline
_Ty _Pow_int(_Ty _X, int _Y)
 {unsigned int _N;
if (_Y >= 0)
_N = (unsigned int)_Y;
else
_N = (unsigned int)(-_Y);
for (_Ty _Z = _Ty(1); ; _X *= _X)
 {if ((_N & 1) != 0)
_Z *= _X;
if ((_N >>= 1) == 0)
return (_Y < 0 ? _Ty(1) / _Z : _Z); }}

A.Z 你用你的代码测试过么?
我用这样的测试结果是4.5s.
我的是C2.4,机子正在干别的事情,测试结果:119.594s
3、“而且.NET第一次编译肯定慢,编译后再重新运行一次,就会知道了”?
好像winform没这种说法吧?
--------------------------------------------------------------
和webform还是winform没有关系的吧
对于dll文件,IL被JIT编译生成的本地代码后会驻留内存,下次调用就直接运行本地代码,不用再次JIT的过程
如果程序关闭后再次运行,会再次JIT编译
wuChang: 你用的是release的代码,然后直接执行的么? 我这样做的结果是4.5s左右. P4/2.4G/512M
另,我也尝试过把锯状数组改成二维数组,结论与你的一样,效率稍有下降,令我很奇怪--因为MSDN上明明说的与此相反.
Math.Pow()只有一个overload,就是public staticdouble Pow(double x, double y); 有趣的是用reflector查看源码时,得到的结果是这样的:[MethodImpl(MethodImplOptions.InternalCall)] public static extern double Pow(double x, double y);
@iamlibai
谢谢指教
@deerchao
以上我的测试都是release F5 执行的。
Mark
个人认为比较托管C++ 和C#的意义较大,从楼主的铁中可以看出,托管C++的效率要明显好于C#,看来在.net 平台中托管C++还是有存在的必要的.
@wuChang
指教实不敢当
我觉得虽然托管与非托管代码之间存在效率差异,也不至于本例所示这么大差距
问题是不是在于c#的算法,或者FCL中个别类的效率问题?
问题应该是Math.Pow()没有提供足够的overload. 如果再提供一个Pow(double, int)的重载的话,原代码的执行效率就会大幅提升.反过来看,用户也有责任,在十分注重效率的场合,没有弄清楚所调用的库函数的效率如何.
@deerchao
我认为也是Math.Pow()的问题了。
@wuChang
没有意想不到,因为我最初是参考你的代码所以我在贴出来之前也比对了这两种数组结构声明的实际情况,你测试的那次我觉得要要重复几次,差异是有的,但是没有这么大。
对了,格式化字符串有点小小的失误,应该是index,对齐宽度:formatstring
关于[MethodImpl(MethodImplOptions.InternalCall)],我自己用c++编成的native method在mixed assembly里面就会有这种属性。好像频繁的调用这种方法就很慢。纯.NET或者纯native code比混合的要快。
在 VS2005 调试时,启动的是 *.vshost.exe 如果直接在Release目录下执行 速度会更快
把 Pow 改为相乘之后 C++ 是 3.6 左右,托管 C++ 是 3.1 左右,C# 是 4.0 左右。 我分析的原因是 C# 的 Pow 幂参数只有 double,没有 int 的,应该是效率低的原因吧,而 C++ 就有很多重载的版本,针对 int 有另外的算法。
Sqrt就是调用return (double) sqrt(d)
Power好像有些判断
double r1;
if(IS_DBL_INFINITY(y)) {
if(IS_DBL_ONE(x)) {
return x;
}
if(IS_DBL_NEGATIVEONE(x)) {
*((INT64 *)(&r1)) = CLR_NAN_64;
return r1;
}
}
else if(_isnan(y) || _isnan(x)) {
*((INT64 *)(&r1)) = CLR_NAN_64;
return r1;
}
return (double) pow(x, y);
总的来说还是值得封送效率,不知道为什么要调用nativecode,这个程序里pow的执行次数是最多的。
这个问题挺有趣的,看来问题是集中在 Math.Pow 上了,托管代码中的语言效率区别是不大的! https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=94548
我用C++ CLR桥接了一下pow rand sqrt,比起纯.net代码有进步略逊于直接乘积(用了double,int的重载版,本身C++CLR有一定的代码优化的),并且我注意到debug版的执行结果和原生的C++版一致的,但是release版却有一定的偏差。和顶楼贴出来结果一样。
等以后winfx“普及”了,不知还能用native code吗?
逐步停止对native api的支持,那么诸如jvm之类的是否要被赶出windows?
我也作了几个实现来对比性能;包括C/C++\C++ CLR\C#\java
我把pow(x,2),改为x*x (避免库中pow函数的不同实现造成的问题)
自己写了随机函数的实现(保证结果一致,高效)
代码 x/(double)RAND_MAX 改为 x* (1.0/RAND_MAX)) (保证高效)
经过这些处理,computePot的运行时间占总时间的99%左右(输出几乎不占用时间)
然后测试它们的运行时间(单线程、AMD64 3600+)
语言 编译环境 release下运行时间(秒)
C/C++ vs2005 2.11(/fp:fast) 2.45(/fp:precise) 3.70(/fp:strict) 2.359(/fp:fast /arch:SSE2)
C++ CLR vs2005 2.34
C# vs2005 5.64
java eclipse3.2.1(2006) 3.34
这样的比较没有什么意义,因为这都是基本数值计算的问题, 经过编译器优化后,没有太大区别. 托管代码的性能与原生代码的相互的特点并没有得到体现.
前一向CSDN的一个家伙还比较JAVA和汇编,最终的结论是: JAVA和汇编的效率也差不多....
首先我感觉涉及的IO操作台多了,如果你用GUI的输出更慢。
如果说托管c++和c#比较意义还有的话,那么他们和C的比较我感觉没有意义了,不同平台上的各种内置函数都不一样,不考虑这些函数的效率吗?况且C#还检查数组越界。
还有慢是必然的,虽然我对CLR之类的不很了解,但是我知道c编出来的东西直接运行在cpu上,而.net的程序运行在虚拟机上,最终工作在cpu上。
我认真测试了一下 其实本身楼主的代码就不公平,全是Native 代码
不过我还是想告诉你,其实我的测试是C++/Clr更快
代码改动了一处 //int rand( void ); 注释掉了 否则/CLR 通不过
对比测试的编译器测试如下:
Native版:
打开SSE2 使用Runtime内联
取消函数溢出检查 优化开到最高 打开全程序优化,已经内联等
托管版:
Runtime必须是DLL链接
无法打开SSE2选项
使用公共语言运行库支持(/clr) 而不是纯MSIL
我提醒一下搂主:测试CLR的速度不能够像Native程序那样 用Release 然后直接按调试启动 默认设置下就算是Release CLR在调试模式下也不会进行代码优化!!!
测试都是编译以后用 不调试运行方式
:/CLR:
结果如下
0: Potential: 673435.381
10: Potential: 280080.608
20: Potential: 210827.276
30: Potential: 173851.965
40: Potential: 151629.165
50: Potential: 135179.212
60: Potential: 122719.471
70: Potential: 113023.770
80: Potential: 106216.967
90: Potential: 99870.995
100: Potential: 94841.602
110: Potential: 91778.965
120: Potential: 88353.235
130: Potential: 84668.246
140: Potential: 81691.768
150: Potential: 79569.487
160: Potential: 76820.990
170: Potential: 74927.476
180: Potential: 72408.035
190: Potential: 70389.235
200: Potential: 68712.394
Seconds = 3.070000000
Native:
0: Potential: 686445.957
10: Potential: 280129.985
20: Potential: 206437.945
30: Potential: 170828.518
40: Potential: 147831.460
50: Potential: 133650.139
60: Potential: 122234.989
70: Potential: 114473.868
80: Potential: 106560.989
90: Potential: 99199.081
100: Potential: 93318.977
110: Potential: 89056.673
120: Potential: 85229.771
130: Potential: 82200.783
140: Potential: 79693.601
150: Potential: 77797.890
160: Potential: 75235.849
170: Potential: 73208.540
180: Potential: 71404.941
190: Potential: 69980.301
200: Potential: 68477.591
Seconds = 3.114000000
/Clr略快一点点 我没有inter 编译器 就不继续测试了
目前VS2005的Native的C++ 只能够优化到SSE2指令
在有,你跟C#的测试就显然不公平了 因为库函数不同,
如果要测试应该把所有的库函数调用都改写再来测试 才有一点意义
楼主,我看到你发布的这个结果,第一反应就是关注你的C#代码,毕竟c,c++他的效率摆在那里的,而C#的效率跟你调用的C#函数有关,有些C#函数效率就很低,我看到你的写的C#例程都是数值运算,应该效率不会相差这么大,所以我当时就怀疑你使用的Math.Pow函数,可能这个函数的效率会很低,所以我把你的代码Down下来,在2003下重新编译了一下,运行时间跟你发布的差不多56左右。
后来我把你的Math.Pow函数替换成 X*X,
for (i = 0; i < NPARTS; i++) 51 { 52 for (j = 0; j < i - 1; j++) 53 { 54 distx = (r[0][j] - r[0][i])*(r[0][j] - r[0][i]);// Math.Pow((r[0][j] - r[0][i]), 2); 55 disty =(r[1][j] - r[1][i])*(r[1][j] - r[1][i]);// Math.Pow((r[1][j] - r[1][i]), 2); 56 distz =(r[2][j] - r[2][i])*(r[2][j] - r[2][i]); //Math.Pow((r[2][j] - r[2][i]), 2); 57 dist = Math.Sqrt(distx + disty + distz); 58 pot += 1.0 / dist; 59 } 60 }
经过这样的修改,效率大为提高,运行时间在我的机器上为10.25s
上一次贴的代码太长了~~!
哇,大家还在讨论呀。以前我的分析发在下面的
http://eparg.spaces.live.com/blog/cns!59BFC22C0E7E1A76!2274.entry
非常感谢 的回复,我也是偶尔看到这篇文章,抱着怀疑的心态试验了一下,没有您那么专业,看了您的文章才豁然开朗,谢谢您!
我的测试更悬乎!! (在VS2005下编码实现) 完全相同的浮点算法,C++下0.718秒,C#下居然高达11.6秒!为什么?? 速度相差20来倍!!! 对M$夸海口吹捧C#的行径表示愤慨。 我自己的浮点运算性能测试代码如下:
-------->>C++<<--------------- #include "stdafx.h" #include <math.h> #include <Windows.h> #include <iostream> using namespace std;
void Test() { double x = 0, y = 0, z = 0; for (int index = 0; index < 1000000000; index++) { x = 2.34 * index; y = 3.45 * index; z = (x * x) * (y * y) * index; } cout<<x+y+z<<endl; }
int main(int ac, char** av) { unsigned time = GetTickCount(); Test(); time = GetTickCount() - time; cout << (double)time/1000 << endl; int ddd; cin>>ddd; }
-------->>C#<<--------------- using System; using System.Collections.Generic; using System.Text;
namespace ConsoleApplication1 { class Program { static double Test() { double x = 0, y = 0, z = 0; DateTime t1 = DateTime.Now; for (int index = 0; index < 1000000000; index++) { x = 2.34 * index; y = 3.45 * index; z = (x * x) * (y * y) * index; } DateTime t2 = DateTime.Now; Console.WriteLine("Value is{0}", x + y + z); return (double)(t2 - t1).Seconds + (double)(t2 - t1).Milliseconds/1000; }
static void Main(string[] args) { Console.WriteLine("Time is: {0}", Test()); Console.ReadKey(); } } }
r[i][j] -= 0.5 + ((double)random.Next(RAND_MAX)/(double)RAND_MAX);
本身这段就已经非常搞笑了
random.Next本身就是返回类型就是integer,为什么非要再进行一次强制类型转换?你知道这中间损失多少效率吗?
C++的强制类型转换是直接用指针转换,不复制新的对象
而C#的强制类型转换实际上已经创建了新对象
不知道我有没有说错
托管代码肯定比非托管代码慢
一个简单的例子说明不了问题
如果用多线程处理的话,.NET超方便而且非常快,反正现在内存这么便宜了,多几条线程也不会太浪费内存
如果是处理MSSQL数据库的话,.NET更加是首选,开发方便,而且还提供了专用的SqlClient类,SqlClient类使用TDS跟MSSQL直接通讯,比以往的ADO连接方式都要快。
如果你是认为自己有能力写的每一句C++代码&算法都比.NET的效率高,那么你可以使用C++,如果不是的话,那请使用.NET吧,哈哈……
!!!!!!!!!!!!!!!!!!!!
所有人都注意,真正最快的 C# 代码如下(我只是C#新手,不知优化的好不好,但速度上确实比本讨论中任何一个人的C#代码的速度都快)
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
namespace cs
{
class Program
{
private const int RAND_MAX = 0x7fff;
private const int NPARTS = 1000;
private const int NITER = 201;
private const int DIMS = 3;
private double pot;
private double distx, disty, distz, dist;
private Random random = new Random(Environment.TickCount);
private double[] r = new double[DIMS*NPARTS];
public void main()
{
int i;
int start, stop;
initPositions();
updatePositions();
start = Environment.TickCount;
for (i = 0; i < NITER; i++)
{
pot = 0.0;
computePot();
if (i % 10 == 0)
Console.WriteLine("{0}: Potential: {1:##########.###}", i, pot);
updatePositions();
}
stop = Environment.TickCount;
Console.WriteLine("Seconds = {0:##########.#########}", (stop - start) / (1000*1.0));
}
private void computePot()
{
int i, j;
double t1, t2, t3;
for (i = 0; i < NPARTS; i++)
{
for (j = 0; j < i - 1; j++)
{
t1 = r[j] - r[i];
t2 = r[DIMS + j] - r[DIMS + i];
t3 = r[DIMS * 2 + j] - r[DIMS * 2 + i];
distx = t1 * t1;
disty = t2 * t2;
distz = t3 * t3;
dist = Math.Sqrt(distx + disty + distz);
pot += 1.0 / dist;
}
}
}
private void updatePositions()
{
int i, j;
for (i = 0; i < DIMS; i++)
for (j = 0; j < NPARTS; j++)
r[DIMS*i+j] -= 0.5 + (random.Next(RAND_MAX) * 1.0000 / RAND_MAX);
}
private void initPositions()
{
int i, j;
for (i = 0; i < DIMS; i++)
for (j = 0; j < NPARTS; j++)
r[DIMS * i + j] = 0.5 + (random.Next(RAND_MAX) * 1.0000 / RAND_MAX);
}
static void Main(string[] args)
{
Program p = new Program();
p.main();
Console.ReadLine();
}
}
}
Mark!
很好的文章,我是.net新手,学习了。
C++快一点点是无须质疑的。不过,我的机器也就是AMD 3000+,怎么C#代码是2.7秒,C++代码是2.4秒?
C#和C++代码都不用pow函数,而改成使用 f*f的方式。
不过,我用C++代码对
dist = Math.Sqrt(distx + disty + distz);
pot += 1.0 / dist;
做了优化后,时间是1.85秒左右。
平方跟的倒数有快速浮点算法的,不过,我不知道用C#怎么写这个优化,我对C#不熟悉,C#又不能直接写汇编和用指针。
如果非要说C++的优势,应该就是这些已经存在的大量的技巧性的东西和汇编的无缝结合吧。
微软的东西一天一个扬,C++搞得乱七八糟的.舍去了指针,gcnew代替了new,运算符^替代了*,真不知道想干沈蒙
这么多回复,感兴趣的人还真多啊。
哪位有兴趣用Nvidia CUDA来试一下?我本本不是N记显卡。
C和C#基本没得比,毕竟是针对不同的问题的语言。C++开发效率不如C#好。内存白菜价的时代,效率是比较重要的。
晕,我拿楼主的代码重新编译了一下,一样快的啊,都是6秒以下,不知道楼主是怎么运行的
我觉得托管代码更重要的问题不是在于效率,而是安全沙箱。微软的安全策略不一定适合我们自己的应用环境。但一旦你选择了托管代码,你就必须想好安全性这个问题。除非你的代码永远只在本地运行。但如果真是这样的话,我们还会需要托管代码吗?
ARCHITECT说得很有道理啊。于是我重新拿windbg开始分析。果然,问题不在InternalCall上。问题在于:
1. pow函数接受的参数定义为double,也就是说pow可以做浮点指数运算。浮点指数运算比整数指数运算复杂多了,如果用CPU的一些优化,比如MMX,可以提高效率
2. C++看到pow(2),直接优化为乘法,同时还inline,避免调用浮点指数运算来提高效率
3. jit看到internalcall,简单判断后直接生成对CRT的调用,所以就没有优化为乘法这一步.最后就把这个2当成浮点数在计算,所以慢了
4. 察看InternalCall的调用,其实就是一个简单的call,根本没有什么context switch.
为了印证上面的说法,把pow(2)修改为pow(2.5),测试下来,C#比C++还快了5秒!赶紧认错去.
看到了吧,我跟ARCHITECT的层次差了多少
邪门了,本机测试,c++ (mcpp) 26秒 c#(cs) 9.8秒。
amd 3000+ 1.6G
1G
未来的电脑软件开发,应该可以不考虑硬件问题。需要考虑的是如何快捷开发出所需要的软件,用于提高生产力。
|