本文翻译自:http://stuff.lanowen.com/Ogre/2.0/OGRE.2.0.Proposal.Slides.pdf

由于本人才疏学浅,翻译难免有误,望各位不吝惜指正。


感谢作者为我们带来的分享:Ogre的不足与改进(Ogre2.0设计方案)


不足

  • 大量代码的写法对缓存不友好,造成缓存命中率低:(
  • 采用的场景遍历方式较为低效
  • 顶点格式复杂,且不够灵活
  • 固定功能管线 vs 可编程管线
  • "setFog",等等

缓存失效

  • 缓存友好真的非常重要

    • Linux系统的路由包在有线情况下传输速率可以达到30Mbps,无线情况下可以达到20Mbps,而我们的WinCE系统在有线情况下传输速率勉强达到12Mbps,无线情况下6Mbps
    • 经过研究,我们发现WinCE系统传输速率不如Linux系统的原因就是相关代码的缓存命中率低
    • 我们改进了WinCE系统的相关代码,让其对缓存友好,最终让WinCE的有线传输速率达到了35MBps,无线传输速率达到了25Bps,比Linux系统快了20%

辣眼睛


大量代码对缓存不友好


对缓存的优化可以参考"Typical C++ Bullshit"

和"Culling the Battlefield"

寒霜2引擎使用结构体数组,SIMD和conditional move(条件移动指令)来对缓存进行优化


在Ogre中有大量代码容易导致缓存失效

在2000年时,因为ALU数量有限,缓存失效还不是性能的最大瓶颈

但随后几年,随着ALU数量的大幅增加,内存和带宽成为瓶颈,缓存友好变得非常重要

可以参考"Pitfalls of Object Oriented Programming"

对于x86和x64处理器,它们的分支预测功能可以在一定程度上缓解缓存失效引发的性能问题

但对于android和iPhone,以及主机所使用的处理器,条件分支导致的缓存失效对性能影响较大


好的代码应该避免条件分支

如果可以,分别实现条件分支所对应的不同情况


良好设计的游戏引擎,它的子节点的位置信息应该由父节点偏移计算得到,并且这一偏移计算在每帧应该只进行一次

如果在一帧中需要多次更新子节点的位置信息,应该手动调用_updateFromParent或直接调用_getDerivedPositionUpdated。出现这种情况,需要我们对引擎的内部实现有十分清晰的认识


缓存失效和指令流水线阻塞


指令流水线阻塞

浮点运算和条件分支不能同时进行

应该尽量使用conditional move(条件移动指令)来消除不必要的条件分支,从而避免指令流水线阻塞

对于PPC架构,可以使用fsel指令

对于x87 FPU,可以使用fcmov指令

虽然,直到SSE5才引入conditional move(条件移动指令),但我们可以在SSE2下使用cmp和mask指令来模拟它。

参考"Down With fcmp"


使用conditional move(条件移动指令)避免指令流水线阻塞

"fsel"函数编译后可能会根据硬件架构调用不同的fsel/fcmov/sse指令。最好查看编译器生成的汇编代码,确保fsel函数内联了正确的汇编指令。


使用SSE2来实现conditional move(条件移动指令)

实现方法:

  • mask=cmp(condition1,condition2)根据比较结果建立mask
  • t1=arg1&mask
  • t2=arg2&~mask
  • r=t1|t2

总共需要4条指令


使用SSE2来实现conditional move(条件移动指令)

实现方法:

  • mask=cmp(condition1,condition2)根据比较结果建立mask
  • t1=arg1&mask
  • t2=arg2&~mask
  • r=t1|t2

总共需要4条指令

一些SSE架构实现会在内部标记xmm寄存器当前存储的是浮点数据还是整型数据,因此,在使用浮点xmm寄存器进行整型运算(比如|或运算)可能会造成一定的性能问题(整型运算前标记xmm寄存器存储的是整型数据,之后将其标记为存储的是浮点数据)。

这也是为什么MOVAPS(float)指令看起来和MOVDQA(int)指令功能一样,却并非多余,合理使用它们可以避免不必要地更改寄存器的内容标记信息。


使用SSE2来实现conditional move(条件移动指令)

更好的实现:

  • mask=cmp(condition1,condition2)根据比较结果建立mask
  • t=arg2-arg1
  • t=t&mask
  • r=arg1+t

同样是4条指令


使用SSE2来实现conditional move(条件移动指令)

更好的实现:

  • mask=cmp(condition1,condition2)根据比较结果建立mask
  • t=arg2-arg1
  • t=t&mask
  • r=arg1+t

加法运算和减法运算都是代价极小的运算。新的方法同样使用了4条指令,但只有一个寄存器会被从浮点标记为整型,又从整型标记回浮点。而之前的实现,需要更改所有寄存器的标记信息。

但是,新的实现需要保证arg1的值不是nan或inf!(可以使用断言来保证这点,同时维护旧实现备用)


过早优化是万恶之源


过早优化是万恶之源

不认同!

每当见到加载窗口,我都会觉得或许我们应该更早地进行优化


过早优化是万恶之源

不认同!

每当见到加载窗口,我都会觉得或许我们应该更早地进行优化:)

  • 进行这些优化并非过早,优化后得到的提升也并不微小

我们现在要优化的ogre代码已经10+年历史了

SIMD,并行化,缓存友好的设计现在已经是行业标配(CryEngine,Frostbyte等引擎已经广泛应用这些技术)

从这些优化手段获得的性能提升是非常巨大的


不相信?


可以看到Orge占用了大量CPU时间!

顺便一提,Distant Souls.exe有40次采样命中逻辑线程等待固定帧率(16ms每帧)的wait函数。它的渲染线程则以帧率可变的方式执行。


可以看出变换操作,动画,以及视锥体剔除占用了大量的CPU时间


还不信?


Distant Souls(Ogre)

  • Intel Core 2 Quad Extreme X9650 3.0Ghz
  • AMD Radeon HD 7770 1GB RAM
  • 4 GB RAM
  • 1280x720,未开启MSAA,最高质量渲染

Distant Souls使用了2个线程。其中一个线程只运行Ogre,另一个执行逻辑和物理运算


Distant Souls(Ogre)

  • Intel Core 2 Quad Extreme X9650 3.0Ghz
  • AMD Radeon HD 7770 1GB RAM
  • 4 GB RAM
  • 1280x720,未开启MSAA,最高质量渲染

Distant Souls使用了2个线程。其中一个线程只运行Ogre,另一个执行逻辑和物理运算


Assassin's Creed 2

  • Intel Core 2 Quad Extreme X9650 3.0Ghz
  • AMD Radeon HD 7770 1GB RAM
  • 4 GB RAM
  • 1280x720,未开启MSAA,最高质量渲染

  • Intel Core 2 Quad Extreme X9650 3.0Ghz
  • AMD Radeon HD 7770 1GB RAM
  • 4 GB RAM
  • 1280x720,未开启MSAA,最高质量渲染

可以看出Assassin's Creed 2比Distant Souls多了3倍的渲染对象,帧率却更高,显然,我们的引擎设计问题很大。


Distant Souls的渲染线程渲染一帧花费了43ms,该线程的空闲时间(包含在43ms内)基本都被GPU占用

逻辑和物理线程按照每帧16ms执行(解锁固定帧率后可以节约8ms)

渲染线程根据来自其它线程的命令更新可见对象,并调用renderOneFrame进行渲染

帧率较低的主要原因可能是缓存失效和复杂的后处理(接下来,我们会讨论为什么它不高效)

linux 占用缓存前10_Ogre的不足与改进(Ogre2.0设计方案)-1.缓存优化相关推荐

  1. Linux查看内存和CPU占用排名前10相关

    查看内存占比占用最多前十排名 ps auxw|head -1;ps -auxf|sort -nr -k4|head -10 查看CPU占比占用最多前十排名 ps auxw|head -1;ps -au ...

  2. Linux运维学习:高级提升(1)——HTTP服务代理缓存加速

    HTTP缓存机制 Web 缓存大致可以分为:数据库缓存.服务器端缓存(代理服务器缓存.CDN 缓存).浏览器缓存. 浏览器缓存也包含很多内容: HTTP 缓存.indexDB.cookie.local ...

  3. Linux高级运维(十)-搭建企业中最常用缓存代理服务器Squid , Varnish

    搭建varnish 缓存代理服务器 需求: 使用Varnish加速后端Web服务,代理服务器可以将远程的Web服务器页面缓存在本地,远程Web服务器对客户端用户是透明的,利用缓存机制提高网站的响应速度 ...

  4. Linux下显示前10个占用空间最大的文件或目录命令

    Linux下显示前10个占用空间最大的文件或目录命令 du -s * | sort -nr | head 转载于:https://blog.51cto.com/wapcn/933177

  5. linux安装telnet客户端_Redis 6.0 的客户端缓存是怎么肥事?一文带你了解!

    来源 | 程序员历小冰责编 | Carol封图 | CSDN 付费下载于视觉中国近日 Redis 6.0.0 GA 版本发布,这是 Redis 历史上最大的一次版本更新,包括了客户端缓存 (Clien ...

  6. linux物理内存没用完swap占用大,Linux占用swap分区过高,物理内存还有剩余

    Linux占用swap分区过高,物理内存还有剩余 问题分析 Swap配置对性能的影响 分配太多的Swap空间会浪费磁盘空间,而Swap空间太少,则系统会发生错误.如果系统的物理内存用光了,系统就会跑得 ...

  7. 【Linux】Linux筛选top前五行信息

                                     Linux筛选top前五行信息 一般的巡检需要查看cpu的使用率以及swap空间的使用率(当然如果系统并没有使用可以忽略) 现在的操作 ...

  8. Nginx一网打尽:动静分离、压缩、缓存、黑白名单、跨域、高可用、性能优化......

    干货!文章有点长,建议先收藏 引言 一.性能怪兽-Nginx概念深入浅出 二.Nginx环境搭建 三.Nginx反向代理-负载均衡 四.Nginx动静分离 五.Nginx资源压缩 六.Nginx缓冲区 ...

  9. 【转载】基于AFNetWorking3.0的图片缓存分析

    原文出处:Yasin的简书 http://www.jianshu.com/p/b1045c3fc8d0 图片在APP中占有重要的角色,对图片做好缓存是重要的一项工作. [TOC] 理论 不喜欢理论的可 ...

最新文章

  1. rnn词性标注算法_Python预测算法哪家强?权游龙妈是生还是凉凉?
  2. Swift之UIView设置部分圆角与获取其所在ViewController
  3. php 时间函数参考
  4. function “printf“ declared implicitly
  5. *PAT_B_1024_Java(20分)
  6. windows共享文件服务器迁移(NTFS权限,共享权限,磁盘配额迁移)
  7. 洛谷P3382 【模板】三分法
  8. POJ 3168 排序+扫描
  9. Java中String连接性能的分析
  10. 你绝对想不到,会Linux的程序员,到底有多吃香!
  11. python入门代码大全-初学python有哪些可以临摹的小段练习代码素材?
  12. node.js 学习书籍推荐
  13. 可能是最全的一份 2017 年中国移动互联网年度报告(建议收藏)
  14. jQuery命令汇总(转)
  15. win10壁纸不能幻灯片放映_教你巧妙解决雨林木风Win10系统中背景桌面无法自动播放幻灯片...
  16. HTML CSS参考文档,css离线手册
  17. 2020 CCF-CSP-S-第一轮-C++ 模拟试卷(五)--有答案
  18. 如何将 CHM 文件翻译成中文
  19. java 帕斯卡_Java编程实现帕斯卡三角形代码示例
  20. IC 后端仿真: process corner 和 PVT

热门文章

  1. linux中使用ntpdate命令进行时间更新时出现the NTP socket is in use, exiting
  2. 关于忘记MySQL的root用户密码的问题
  3. Java 语言 ArrayList 和 JSONArray 相互转换
  4. 微服务RPC框架-Feign
  5. ETL PostgreSQL in Oracle ODI 12c
  6. Java 8 时间日期库的20个使用演示样例
  7. Android 动态移动控件实现
  8. 基于 Workman 实现Web扫描登录
  9. 浅析 JNDI / DataSource / ConnectionPool 三者
  10. XJTUOJ wmq的队伍(树状数组求 K 元逆序对)