C# vs C++ 全局照明渲染性能比试
作者:网络转载 发布时间:[ 2015/2/10 13:41:35 ] 推荐标签:C++ C# 软件开发
C#版本
using System;
using System.IO;
namespace smallpt_cs {
struct Vec { // Usage: time ./smallpt 5000 && xv image.ppm
public double x,y,z; // position,also color (r,g,b)
public Vec(double x_,double y_,double z_) {x=x_;y=y_;z=z_;}
public static Vec operator +(Vec a,Vec b) {return new Vec(a.x+b.x,a.y+b.y,a.z+b.z);}
public static Vec operator -(Vec a,Vec b) {return new Vec(a.x-b.x,a.y-b.y,a.z-b.z);}
public static Vec operator *(Vec a,double b) {return new Vec(a.x*b,a.y*b,a.z*b);}
public Vec mult(Vec b) { return new Vec(x*b.x,y*b.y,z*b.z);}
public Vec norm() { return this=this*(1/Math.Sqrt(x*x+y*y+z*z));}
public double dot(Vec b) { return x*b.x+y*b.y+z*b.z;}//cross:
public static Vec operator %(Vec a,Vec b) { return new Vec(a.y*b.z-a.z*b.y,a.z*b.x-a.x*b.z,a.x*b.y-a.y*b.x);}
}
enum Refl_t { DIFF,SPEC,REFR }; // material types,used in radiance()
struct Ray { public Vec o,d;public Ray(Vec o_,Vec d_) { o=o_;d=d_;} }
class Sphere {
public double rad; // radius
public Vec p,e,c; // position,emission,color
public Refl_t refl; // reflection type (DIFFuse,SPECular,REFRactive)
public Sphere(double rad_,Vec p_,Vec e_,Vec c_,Refl_t refl_) {
rad=rad_;p=p_;e=e_;c=c_;refl=refl_;
}
public double intersect(Ray r)
{ // returns distance,0 if nohit
Vec op=p-r.o;// Solve t^2*d.d+2*t*(o-p).d+(o-p).(o-p)-R^2=0
double t,eps=1e-4,b=op.dot(r.d),det=b*b-op.dot(op)+rad*rad;
if (det<0) return 0;else det=Math.Sqrt(det);
return (t=b-det) > eps?t : ((t=b+det) > eps?t : 0);
}
};
class smallpt {
static Random random=new Random();
static double erand48() { return random.NextDouble();}
static Sphere[] spheres={//Scene: radius,position,emission,color,material
new Sphere(1e5,new Vec( 1e5+1,40.8,81.6), new Vec(),new Vec(.75,.25,.25),Refl_t.DIFF),//Left
new Sphere(1e5,new Vec(-1e5+99,40.8,81.6),new Vec(),new Vec(.25,.25,.75),Refl_t.DIFF),//Rght
new Sphere(1e5,new Vec(50,40.8,1e5), new Vec(),new Vec(.75,.75,.75),Refl_t.DIFF),//Back
new Sphere(1e5,new Vec(50,40.8,-1e5+170), new Vec(),new Vec(), Refl_t.DIFF),//Frnt
new Sphere(1e5,new Vec(50,1e5,81.6), new Vec(),new Vec(.75,.75,.75),Refl_t.DIFF),//Botm
new Sphere(1e5,new Vec(50,-1e5+81.6,81.6),new Vec(),new Vec(.75,.75,.75),Refl_t.DIFF),//Top
new Sphere(16.5,new Vec(27,16.5,47), new Vec(),new Vec(1,1,1)*.999, Refl_t.SPEC),//Mirr
new Sphere(16.5,new Vec(73,16.5,78), new Vec(),new Vec(1,1,1)*.999, Refl_t.REFR),//Glas
new Sphere(600,new Vec(50,681.6-.27,81.6),new Vec(12,12,12), new Vec(), Refl_t.DIFF) //Lite
};
static double clamp(double x) { return x<0?0 : x > 1?1 : x;}
static int toInt(double x) { return (int)(Math.Pow(clamp(x),1 / 2.2)*255+.5);}
static bool intersect(Ray r,ref double t,ref int id) {
double d,inf=t=1e20;
for (int i=spheres.Length-1;i >= 0;i--)
if ((d=spheres[i].intersect(r)) != 0 && d<t) { t=d;id=i;}
return t<inf;
}
static Vec radiance(Ray r,int depth) {
double t=0; // distance to intersection
int id=0; // id of intersected object
if (!intersect(r,ref t,ref id)) return new Vec();// if miss,return black
Sphere obj=spheres[id]; // the hit object
Vec x=r.o+r.d*t,n=(x-obj.p).norm(),nl=n.dot(r.d)<0?n:n*-1,f=obj.c;
double p=f.x>f.y&&f.x>f.z?f.x:f.y>f.z?f.y:f.z;//max refl
if (++depth > 5) if (erand48()<p) f=f*(1 / p);else return obj.e;//R.R.
if (depth > 100) return obj.e;
if (obj.refl == Refl_t.DIFF) { // Ideal DIFFUSE reflection
double r1=2*Math.PI*erand48(),r2=erand48(),r2s=Math.Sqrt(r2);
Vec w=nl,u=((Math.Abs(w.x)>.1?new Vec(0,1,0):new Vec(1,0,0))%w).norm(),v=w%u;
Vec d=(u*Math.Cos(r1)*r2s+v*Math.Sin(r1)*r2s+w*Math.Sqrt(1-r2)).norm();
return obj.e+f.mult(radiance(new Ray(x,d),depth));
}
else if (obj.refl == Refl_t.SPEC) // Ideal SPECULAR reflection
return obj.e+f.mult(radiance(new Ray(x,r.d-n*2*n.dot(r.d)),depth));
Ray reflRay=new Ray(x,r.d-n*2*n.dot(r.d));//IdealdielectricREFRACTION
bool into=n.dot(nl) > 0; // Ray from outside going in?
double nc=1,nt=1.5,nnt=into?nc / nt : nt / nc,ddn=r.d.dot(nl),cos2t;
if ((cos2t=1-nnt*nnt*(1-ddn*ddn))<0) //Total internal reflection
return obj.e+f.mult(radiance(reflRay,depth));
Vec tdir=(r.d*nnt-n*((into?1:-1)*(ddn*nnt+Math.Sqrt(cos2t)))).norm();
double a=nt-nc,b=nt+nc,R0=a*a/(b*b),c=1-(into?-ddn:tdir.dot(n));
double Re=R0+(1-R0)*c*c*c*c*c,Tr=1-Re,P=.25+.5*Re,RP=Re/P,TP=Tr/(1-P);
return obj.e+f.mult(depth > 2?(erand48()<P? // Russian roulette
radiance(reflRay,depth)*RP:radiance(new Ray(x,tdir),depth)*TP):
radiance(reflRay,depth)*Re+radiance(new Ray(x,tdir),depth)*Tr);
}
public static void Main(string[] args) {
DateTime start=DateTime.Now;
int w=256,h=256,samps=args.Length==2?int.Parse(args[1])/4:25;// # samples
Ray cam=new Ray(new Vec(50,52,295.6),new Vec(0,-0.042612,-1).norm());//cam pos,dir
Vec cx=new Vec(w*.5135/h,0,0),cy=(cx%cam.d).norm()*.5135,r;Vec[] c=new Vec[w*h];
for (int y=0;y<h;y++) { // Loop over image rows
Console.Write(" Rendering ({0}spp) {1:F2}%",samps*4,100.0*y/(h-1));
for (int x=0;x<w;x++) // Loop cols
for (int sy=0,i=(h-y-1)*w+x;sy<2;sy++) // 2x2 subpixel rows
for (int sx=0;sx<2;sx++) { // 2x2 subpixel cols
r=new Vec();
for (int s=0;s<samps;s++) {
double r1=2*erand48(),dx=r1<1?Math.Sqrt(r1)-1:1-Math.Sqrt(2-r1);
double r2=2*erand48(),dy=r2<1?Math.Sqrt(r2)-1:1-Math.Sqrt(2-r2);
Vec d=cx*(((sx+.5+dx)/2+x)/w-.5)+
cy*(((sy+.5+dy)/2+y)/h-.5)+cam.d;
r=r+radiance(new Ray(cam.o+d*140,d.norm()),0)*(1.0/samps);
} // Camera rays are pushed ^^^^^ forward to start in interior
c[i]=c[i]+new Vec(clamp(r.x),clamp(r.y),clamp(r.z))*.25;
}
}
Console.WriteLine(" {0} sec",(DateTime.Now-start).TotalSeconds);
using (StreamWriter sw=new StreamWriter("image.ppm")) {
sw.Write("P3 {0} {1} {2} ",w,h,255);
for (int i=0;i<w*h;i++)
sw.Write("{0} {1} {2} ",toInt(c[i].x),toInt(c[i].y),toInt(c[i].z));
sw.Close();
}
}
}
}
Vec和Ray需要不断在计算中产生实例,所以设它们为struct,struct在C#代表值类型(value type),ibpp在堆栈上高效分配内存的,不需使用GC。渲染时,Sphere是只读对象,因此用class作为引用类型(reference type)去避免不必要的复制。
实验结果和分析
实验环境是Visual Studio 2008/.Net Framework 3.5编译,Intel I7 920 (4核、超线程)。渲染512x512解像度,每像素100个采样。结果如下:
测试版本 需时(秒)
(a) C++ 45.548
(b) C# 61.044
(c) C++ SIMD 20.500
(d) C++(OpenMP) 7.397
(e) C++ SIMD(OpenMP) 3.470
(f)* C++ LCG 17.365
(g)* C# LCG 59.623
(h)* C++ LCG (OpenMP) 3.427
*2010/6/23 加入(f)(g)(h),?更新1
基本,应比较(a)和(b)。两者皆使用单线程。 C++版本性能比C#版本快大约34%。这其实已远远超出我对C#/.Net的期望,没想到用JIT代码的运行速度,能这么接近传统的编译方式。
采用SIMD的C++版本(c),虽然仍未大量优化,但性能比没有SIMD的版本高122%,比C#版本高接近两倍。不过,采用SIMD后,数值运算的精确度变低,所以这比较只能作为参考。
采用OpenMP能活用i7的8个逻辑核心。使用OpenMP的非SIMD(d)和SIMD(e)版本,分别比没使用OpenMP的版本(a)和(c),性能各为6.16倍和5.9倍。这已经很接近理想值8,说明这应用能充分利用CPU并行性。而OpenMP强大的地方,在于只需加入1句编译器#pragma指令能自动并行。
结语
虽然本文的实验只能反映个别情况。但实验可以说明,在某些应用上,C#/.Net的性能可以非常贴近C++,差别小于一个数量级。
本文实验所用的程序代码,有不少进一步优化的空间,源代码可于这里下载。有兴趣的朋友也可把代码移植至Java及其他语言。
后,本人认为,各种平台和语言,都有其适用时机。作为程序员,理想是认识各种技术,以及认清每个技术的特长、短处,以便为应用找到好的配?。
本文内容不用于商业目的,如涉及知识产权问题,请权利人联系SPASVO小编(021-61079698-8054),我们将立即处理,马上删除。
相关推荐
更新发布
功能测试和接口测试的区别
2023/3/23 14:23:39如何写好测试用例文档
2023/3/22 16:17:39常用的选择回归测试的方式有哪些?
2022/6/14 16:14:27测试流程中需要重点把关几个过程?
2021/10/18 15:37:44性能测试的七种方法
2021/9/17 15:19:29全链路压测优化思路
2021/9/14 15:42:25性能测试流程浅谈
2021/5/28 17:25:47常见的APP性能测试指标
2021/5/8 17:01:11热门文章
常见的移动App Bug??崩溃的测试用例设计如何用Jmeter做压力测试QC使用说明APP压力测试入门教程移动app测试中的主要问题jenkins+testng+ant+webdriver持续集成测试使用JMeter进行HTTP负载测试Selenium 2.0 WebDriver 使用指南