为什么简单的get语句如此之慢?
几年前,我在学校完成了一项任务,在那里我必须并行化Raytracer。
这是一个简单的任务,我真的很喜欢它的工作。
今天,我觉得要分析光线跟踪器,看看我是否可以让它更快地运行(没有完全检修代码)。 在分析期间,我发现了一些有趣的东西:
// Sphere.Intersect public bool Intersect(Ray ray, Intersection hit) { double a = ray.Dir.x * ray.Dir.x + ray.Dir.y * ray.Dir.y + ray.Dir.z * ray.Dir.z; double b = 2 * (ray.Dir.x * (ray.Pos.x - Center.x) + ray.Dir.y * (ray.Pos.y - Center.y) + ray.Dir.z * (ray.Pos.z - Center.z)); double c = (ray.Pos.x - Center.x) * (ray.Pos.x - Center.x) + (ray.Pos.y - Center.y) * (ray.Pos.y - Center.y) + (ray.Pos.z - Center.z) * (ray.Pos.z - Center.z) - Radius * Radius; // more stuff here }
根据分析器,25%的CPU时间花在get_Dir
和get_Pos
,这就是为什么我决定以下列方式优化代码:
// Sphere.Intersect public bool Intersect(Ray ray, Intersection hit) { Vector3d dir = ray.Dir, pos = ray.Pos; double xDir = dir.x, yDir = dir.y, zDir = dir.z, xPos = pos.x, yPos = pos.y, zPos = pos.z, xCen = Center.x, yCen = Center.y, zCen = Center.z; double a = xDir * xDir + yDir * yDir + zDir * zDir; double b = 2 * (xDir * (xPos - xCen) + yDir * (yPos - yCen) + zDir * (zPos - zCen)); double c = (xPos - xCen) * (xPos - xCen) + (yPos - yCen) * (yPos - yCen) + (zPos - zCen) * (zPos - zCen) - Radius * Radius; // more stuff here }
取得惊人的成果。
在原始代码中,使用默认参数运行光线跟踪器(创建仅具有直接闪电且没有AA的1024×1024图像)将花费大约88秒 。
在修改后的代码中,同样需要不到60秒 。
只需对代码进行一点修改,我就实现了~1.5的加速。
起初,我认为Ray.Dir
和Ray.Pos
的吸气剂正在幕后做一些事情,这会减慢程序的速度。
以下是两者的吸气剂:
public Vector3d Pos { get { return _pos; } } public Vector3d Dir { get { return _dir; } }
所以,两者都返回一个Vector3D,就是这样。
我真的很想知道,与直接访问变量相比,调用getter会花费更长的时间。
是因为CPU缓存变量? 或者也许重复调用这些方法的开销加起来了? 或者JIT处理后一种情况可能比前者更好? 或者也许还有其他我没看到的东西?
任何见解将不胜感激。
编辑:
正如@MatthewWatson建议的那样,我使用StopWatch
来定时发布调试器之外的版本。 为了消除噪音,我多次运行测试。 结果,前一个代码需要大约21秒 (在20.7和20.9之间)才能完成,而后者仅需要大约19秒 (在19和19.2之间)。
差异已经可以忽略不计了,但它仍然存在。
介绍
我愿意打赌原始代码是如此之慢,因为C#中的怪癖涉及类型结构的属性。 它并不完全直观,但这种类型的属性本质上很慢。 为什么? 因为结构不是通过引用传递的。 因此,为了访问ray.Dir.x
,您必须这样做
- 加载局部变量
ray
。 - 调用
get_Dir
并将结果存储在临时变量中。 这涉及复制整个结构,即使只使用字段’x’。 - 从临时副本访问字段
x
。
查看原始代码,get访问器被调用18次。 这是一个巨大的浪费,因为这意味着整个结构被复制18次。 在优化的代码中,只有两个副本Dir
和Pos
都只被调用一次; 进一步访问值只包括上面的第三步:
- 从临时副本访问字段
x
。
总结一下,结构和属性不会结合在一起。
为什么C#使用struct属性以这种方式运行?
它与C#中的结构是值类型有关。 您传递值本身,而不是指向值的指针。
为什么编译器不能识别get访问器只是返回一个字段,并且完全绕过该属性?
在调试模式下,跳过这样的优化以提供更好的debegging体验。 即使在发布模式下,您也会发现大多数紧张情绪通常不会这样做。 我不确切知道为什么,但我相信这是因为该字段并不总是字对齐。 现代CPU具有奇怪的性能要求。 ?
上述就是C#学习教程:为什么简单的get语句如此之慢?分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注—计算机技术网(www.ctvol.com)!
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/cdevelopment/1010831.html