|
|
Posted on 2006-12-07 10:14 无常 阅读(6473) 评论(69) 编辑 收藏 所属分类: 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()
 &nb |