Tagged: Chrome Toggle Comment Threads | 键盘快捷键

  • Roger 8:27 pm on December 29, 2013 固定链接 | 回复
    Tags: , , Chrome, ,   

    我的2013 – 年终总结 + 浏览器渲染发展的一些思考 

    又到一年的年末,秉承传统,继续为这一年写一篇总结的文章。今年依旧延续了进入公司后每年都更换不同小组的传统,下半年调到了内核渲染组,不过之前在技术研究组的工作也一直是专注于浏览器渲染相关的技术研究,所以这次调动也算是顺理成章了,工作内容基本上没有任何变化。

     
    今年一年以来的工作都跟硬件加速渲染有关,除了在不断完善和优化原有的硬件加速/图层混合加速的渲染架构,保证UC加速版的顺利发布外,另外最主要的工作就是设计和实现2D Canvas的加速渲染架构,从年初的初始实现版本到UC9.4加速版的版本,其间一共更替了三套不同的架构,而且每一次基本上都是完全推倒重来,但也解决了前一版本无法克服的一些内在缺陷,当前最新的架构基本上已经没有什么性能上明显的瓶颈,也足够灵活地根据不同的设备动态调整自己的行为,在兼容性,资源占用,性能这三者之间取得一个较好的平衡。
     
    回顾自己这一年的收获,通过研究Android图形子系统的底层实现,研究GPU和其它各种硬件的工作原理,自己对渲染架构和性能的理解也越来越深刻。“物尽其用,要事优先”是自己对高性能渲染架构的总结,一个高性能的渲染架构,设计者需要对硬件有相当的认识,知道如何将一个完整的渲染任务合理地进行分解,然后调度不同的硬件尽可能并发地完成它;要了解哪里是性能的瓶颈,哪部分是关键的任务,要保证关键任务优先获得资源的分配,尽可能降低瓶颈的影响。
     
    下半年开始,在和主管的工作总结中,还有职业等级评定的答辩会议上,都不断地被问到下面这两个问题,同时自己也在不断地进行思考。
    • 浏览器的渲染架构还有没有可能有更大的性能突破?
    • 浏览器能不能够成为一个承载游戏运行的良好平台,使得H5游戏能够跟原生游戏在同一层面竞争?
     
    浏览器的渲染架构还有没有可能有更大的性能突破?
     
    目前基于WebKit或者Blink内核浏览器的渲染架构,虽然各自的实现不太一样,但是整体架构设计都是由Apple奠定的,Apple当时把自己在MacOS上的那套Layer Rendering的渲染架构引入WebKit,率先在Safari上实现了Accelerated Compositing(图层混合加速),Chrome接着完成了自己的图层混合加速实现,而Android直到3.x的系统浏览器才开始支持,到了4.1才算是比较完善。虽然我不太了解Firefox的渲染架构实现,不过之前看过的一些文章介绍,思路总体上应该跟WebKit差不多。
     
    图层混合加速将渲染任务分解为Paint(绘制)和Composite(混合),绘制由WebKit内核自身使用CPU在一个独立的线程完成,绘制或者光栅化后的结果Layer Buffer(图层缓存)被发送到混合线程,由GPU来进行混合,而Blit(缓存的混合)则是GPU最擅长的工作,甚至可能不需要动用GPU的3D加速单元,使用2D Blitter就够了。
     
    这种架构主要的缺陷在于:
    1. 为了保证渲染性能和效果,浏览器需要大量的图层缓存,尤其对于是一些图层非常复杂,数量非常多的网页,这个问题在移动设备上更加明显;
    2. 由于绘制仍然使用CPU,对于普通的网页,绘制任务主要由图片,文本,各种不同图形的填充(使用纯色,渐变或者纹理)组成,CPU在大部份情况下的确做的比GPU更好,而且资源消耗更少,另外也有助于让GPU更专注于混合任务;但是对一些复杂的动态特效,像是CSS Filter的使用,单纯靠CPU会很困难,并且这种先绘制再混合的方式对于这些动态特效来说反而会造成性能上的损失,并且需要更多缓存;
    第一个问题,如果GPU能够支持一种无损压缩的纹理作为图层缓存,并且压缩可以高效实时的完成(甚至可以使用GPU,在纹理上传的同时完成压缩),就能够得到极大的缓解(对于网页这种大部份以文本和图形为主的位图,压缩率应该非常高,使用ZIP压缩1/10都是很正常的),不过在了解了一些纹理压缩相关的知识后,发现这一点的确不太可能,GPU主要还是为了游戏设计的,而游戏使用的都是事先生成的压缩纹理,并且GPU为了读取压缩纹理时的速度考虑,不可能支持可变压缩率,所以也无法支持无损压缩。
     
    第二个问题,可能的答案是将一部分绘制任务丢到混合线程直接由GPU来完成。不过困难在于两个方面:首先是否能够对绘制任务进行合理的分解,将其中一部分适合GPU完成的任务由GPU在混合线程直接进行绘制,而剩下的仍然保持原有的方式,简单说就是实现一种Hybrid Rendering架构;另一方面是使用GPU做缓存的混合是比较简单的,但是要直接完成复杂的绘制则比较困难,如果使用OpenGL,意味着要实现各种不同绘制的Shader,如果使用OpenVG这样的矢量图绘制API,貌似硬件兼容性和系统支持程度又有很多问题(Android系统本身是不支持OpenVG的,即使大部份GPU硬件上都支持)。除此之外使用RenderScript或者OpenCL这样的异构计算API来进行GPU加速绘制也是一种可能的方式。
     
    浏览器能不能够成为一个承载游戏运行的良好平台,使得H5游戏能够跟原生游戏在同一层面竞争?
     
    回答这个问题之前,先看一下现状,浏览器目前是通过2D Canvas和3D Canvas(WebGL)来支持2D/3D的即时绘图,这也是目前H5游戏的基础。在Android平台上,Chrome和UC都比较完整实现了2D Canvas的GPU加速,并且Chrome已经支持WebGL,UC接下来也会提供WebGL的支持,而WebGL本来就是对应到GLES。使用2D/3D Canvas开发H5游戏,存在的一些性能问题是:
    1. Canvas元素只是网页中所有元素的其中一个,所以它的绘制必须先绘制到一个单独的缓存中,仍然再跟其它元素的图层缓存进行混合输出,这个会带来一些额外的开销,导致性能损耗。不过这个问题还好,对PC来说这个额外开销不算高,而移动设备GPU的性能也越来越好(不过分辨率飙升过快也是一个问题…)
    2. 2D Canvas只提供了最基础的绘图API,难以通过这些API高效实现各种复杂的特效,比如说要实现一个粒子效果,不得不调用几十次甚至上百次drawRect或者drawBitmap,这样的效率非常低。而WebGL还好,可以允许游戏自己直接控制Buffer,自己编写Shader实现各种特效,直接交由GPU运行,从这点看,长远来说WebGL对游戏还是更有前途一些,不过相对来说,它的开发难度就要高不少(即使是使用2D Canvas开发的游戏,很多游戏也会在一些静态不需要太高性能的场景改成使用CSS+DIV来降低开发难度);
    3. 无论是2D Canvas也好,WebGL也好,它们都只提供了绘图API,离一个完整的游戏引擎相去甚远,而要依靠JS实现一个高性能的大型游戏引擎(包括物理运动引擎等)用来支撑一个大型游戏,受限于JS本身的性能,并且缺少底层硬件的直接控制能力,这点不得不说是最大的困难;
    对于第三点,Firefox和Chrome的解决思路完全不同:
    • Chrome通过Native Client允许原生代码直接在浏览器提供的一个受限环境里面运行;
    • Firefox通过ASM.js + WebGL的方式,遵循ASM.js语法的代码仍然是标准的JS代码,但是特殊的语法使得JS虚拟机可以直接将它们翻译成机器码,高效地运行;
    这两种方式先不谈技术本身的成熟度,共同存在的问题就是它们都太过复杂,游戏开发者不直接使用Cocos2D或者Unity3D这样成熟的游戏引擎开发原生游戏而使用Native Client或者ASM.js +WebGL,那简直就是脱裤子放屁,多此一举。
     
    有没有第三种解决问题的思路,我个人认为,浏览器自带游戏引擎(自行开发或者整合第三方的游戏引擎),然后通过一套标准的游戏引擎API供JS调用是一种可行的方式,这样JS就基本上变成像Lua这样的场景控制脚本,这种方式能够同时解决性能的问题和游戏开发难度的问题,当然它的问题是这样的游戏引擎标准API的制定和浏览器的实现还是相当虚无缥缈的事情……
     
    除了性能问题,H5游戏还有打包,发行等问题,不过这些可以通过打包应用(Packaged App)和Web App Store来解决。
     
    最后是我这一年写的比较满意的一些文章:
    1. Why Your Android Apps Suck https://plus.google.com/104793567654398886385/posts/TEr9MYV7tVy
    2. Android GraphicBuffer Introduction and Its Usage in Rendering https://plus.google.com/104793567654398886385/posts/j1LcfHAr5BS
    3. High Performance Canvas Game for Android(高性能Android Canvas游戏开发) http://tech.uc.cn/?p=2414
     
    • Luin 8:49 下午 on 十二月 29, 2013 固定链接 | 回复

      收获很多,多谢

    • Luke 12:42 上午 on 十二月 30, 2013 固定链接 | 回复

      好习惯啊,而且都是干货。
      游戏是一种对浏览器App较高强度的考验,而且前景不错,以游戏的性能来作为评价指标也不失为一个好办法。
      顺着你的思路,我觉得优化空间应该还是有的:
      1. 纹理方面: a. 元素很多的页面是否做到了纹理拼接和压缩?(但对应的缓存应该还是需要的吧)
      b. 纹理压缩算法本身可以优化。一方面可以考虑并行运算的优化,另外一方面各种算法在内存消耗和运算消耗方面可以做trade-off。
      2. 更多元素使用GPU绘制,包括css3的效果等。理论上好像基于加速的skia就好?
      3. js引擎的继续优化。
      针对游戏开发,我觉得使用游戏开发套件或者引擎来开发肯定会是主流了,基于WebGL作为图形引擎的应该会成为主流,不用过分担心2D游戏的效率问题。
      Native Client技术很好,但暂时来说我觉得和Android NDK之于SDK一样,作为辅助或者完成一部分关键功能比较合适,主流的开发环境还是JS+HTML5。不过谁知道以后会不会有NaCL的游戏引擎?再说了Js后端也可能用PNacl/LLVM啊。
      从实用的角度来说,UC还可以发布一个基于浏览器的游戏引擎。

    • 艾楠 3:11 下午 on 三月 31, 2014 固定链接 | 回复

      好习惯!~

    • 韩国女装品牌网站 12:32 下午 on 三月 25, 2016 固定链接 | 回复

      世间有许多事功亏一篑的主要原因就是意志不坚定,不能坚持到底。现实生活中,如果没有坚定意志,我们就不能享受成功的幸福。

    • 韩国食品网上商城 12:36 下午 on 三月 25, 2016 固定链接 | 回复

      成功的人,是在无数次投资失败后站起来的。

    • 韩国时尚购物起义 1:08 下午 on 三月 25, 2016 固定链接 | 回复

      对投资而言,欲速则不达

    • 韩国创意设计用品网站 8:36 上午 on 六月 29, 2016 固定链接 | 回复

      像采金一样追求知识。

  • Roger 9:37 am on September 2, 2013 固定链接 | 回复
    Tags: , , Chrome, , , uc   

    High Performance Canvas Game for Android 

    UC在今年5月份发布了支持硬件加速的实验室版,开始实验性地支持对2D Canvas进行硬件加速。在9月份发布的9.3版本中开始分开两个版本 – 普通版和加速版,针对中高端手机的加速版开始正式支持硬件加速2D Canvas渲染。并且在未来将要发布的9.4加速版还会带来全新的Canvas渲染架构,进一步提升性能和减少对系统资源的占用。
    这篇文章的主要目的是为移动Canvas游戏的开发者如何针对Android UC浏览器9.4加速版进行渲染性能优化提供指导,不过文中的大部分内容也适用于Android平台其它支持硬件加速2D Canvas的浏览器,比如Chrome for Android。另外,这篇文章的内容主要是针对渲染性能优化,而不是JavaScript性能优化,不过因为Android UC浏览器使用的是V8引擎,所以您应该很容易找到很多如何针对V8优化JavaScript性能的文章。

    Rule #0 为移动平台进行优化

    为移动平台进行优化是十分重要的,因为移动平台的性能大概只有桌面平台的1/10左右(*1),它通常意味着:
    1. 更慢的CPU速度,这意味着不经过优化的JavaScript代码,性能会十分糟糕;
    2. 更小的内存,没有虚拟内存的支持,这意味着加载太多的资源容易导致内存不足,JSVM更容易引发GC,并且GC造成的停顿时间也越长;
    3. 更慢的GPU速度,没有独立的显存,内存带宽相比PC要慢的多,这意味着即使使用GPU对Canvas进行加速,您仍然需要小心网页DOM树的复杂度,游戏所使用的分辨率(Canvas的大小),图片资源的分辨率,游戏场景的复杂度和尽量避免Overdraw等等;
    注释:
    1. 如果您需要对移动平台浏览器的性能有一个全面的了解,建议阅读文章“Why mobile web apps are slow”(原文译文)和”5 Myths About Mobile Web Performance“(原文译文)。

    Rule #1 为Android而不是iOS而优化

    牢记这一点非常重要,Mobile Safari的Canvas渲染机制跟Android平台有很大的不同,特别地针对Mobile Safari进行优化的Canvas游戏在Android平台的渲染性能会十分的糟糕。
    Mobile Safari使用了iOS/MacOS平台特有的IOSurface作为Canvas的Buffer,当通过Canvas API往IOSurface绘制内容的时候是没有GPU加速的,iOS仍然使用CPU进行绘制,但是将一个IOSurface绘制到另外一个IOSurface上的时候,iOS会使用GPU的2D位拷贝加速单元进行加速(*1)。这种机制其实也是iOS UI界面Layer Rendering渲染架构的基础。所以为iOS优化的Canvas游戏会倾向于使用大量的Off-Screen Canvas(*2),不管是静态的图片也好,还是需要动态产生的内容也好,统统都缓存到一个Off-Screen Canvas上,最终游戏场景的绘制就是一个把一堆Off-Screen Canvas绘制到一个On-Screen Canvas的过程,这样就可以充分利用iOS绘制IOSurface到IOSurface使用了GPU加速的特性来提升渲染性能。
    但是这种大量使用Off-Screen Canvas的做法在Android平台的浏览器上会非常糟糕。Android平台并没有IOSurface的同等物(一块同时支持CPU读写和GPU读写的缓冲区),所以它只能使用GL API对Canvas进行加速,一个加速的Canvas,它的Buffer是一个GL Texture(被attach到一个FBO上),这意味着:
    1. 无论是绘制到Canvas本身,还是Canvas绘制到Canvas都是GPU加速的,普通的位图要绘制到Canvas上,需要先被加载到一个Texture中;
    2. Texture Buffer只能通过GPU进行读写,如果要使用CPU访问,必须先通过glReadPixels把内容从显存拷贝到一块普通内存,而这个操作会非常慢并且会造成GL渲染流水线的阻塞;
    3. 如果游戏频繁创建和销毁一些比较小的Canvas,会很容易造成显存的碎片化,让显存的耗尽速度加快,并且创建太多的Canvas也容易把GPU资源都消耗光,导致后续的分配失败和渲染错误;
    4. 当每个Game Loop都对多个Canvas进行同时更新时,会导致GL Context不断地切换不同的Render Target(FBO),而这对GL的渲染性能有很大的影响;
    后续的内容会进一步说明如何针对使用GL加速的Canvas渲染架构进行优化。
    注释:
    1. 一般GPU都会带有多个独立的加速单元,包括3D加速单元,支持GL和D3D这样的3D API;2D位拷贝加速单元,对将一块缓冲区绘制到另外一块缓冲区进行加速;2D矢量绘制加速单元,支持像OpenVG这样的2D API,但是Android平台只支持通过GL API使用GPU加速,并没有公开的2D位拷贝加速API,虽然2.x的时候厂商可以提供一个copybit模块对位拷贝进行加速,供SurfaceFlinger使用,但这个模块不是通用的,并且不对外公开,另外在4.x的时候也已经移除了。
    2. Off-Screen Canvas在文中是指display:none,没有attach到DOM树上的Canvas,相对于On-Screen Canvas而言。

    Rule #2 优化网页的DOM树结构

     

    Canvas只是网页的一部分,它最终要显示出来还需要浏览器对网页本身的绘制,如果网页的DOM树结构越复杂,浏览器花在网页绘制上的时间也就越长,网页绘制占用的CPU/GPU资源也就越多,而留给Canvas绘制的CPU/GPU资源也就越少,这意味着Canvas绘制本身需要的时间也越长。并且网页绘制的耗时越长,Canvas最终更新到屏幕上的延迟也就越长,总之,这是一个此消彼涨的过程。所以,优化网页的DOM树结构,让其尽可能简单,浏览器就可以把更多的系统资源花费在Canvas的绘制上,从而提升Canvas的渲染性能。

     

    最理想的DOM结构就是只包含一个<body>,加上一个<div>作为容器和加上一个<canvas>本身。如果Canvas上面需要显示其它的网页内容,最好只是用于一些临时使用的对话框之类的东西,而不是一直固定显示。

     

    Rule #3 优化网页元素的css背景设置

     

    跟#2的道理一样,背景设置越简单或者根本不设置背景,浏览器花费在网页本身绘制的开销也就越小,一般来说<canvas>元素本身都不应该设置css背景,它的背景应该通过Canvas API来绘制,避免浏览器在绘制<canvas>元素时还要先绘制背景,然后再绘制Canvas的内容。另外<body>和其它元素都应该首先考虑使用background-color而不是background-image,因为background-image的绘制耗时比一个纯色填充要大的多,而且背景图片本身还需要消耗额外的显存资源(生成Texture)。

     

     Rule #4 使用合适大小的Canvas

     

    考虑移动设备的性能限制,Canvas不适宜太大,否则需要消耗更多的GPU资源和内存带宽,480p或者600p是一个比较合适的选择(横屏游戏可以选择800p或者960p),一般不应该超过720p。并且游戏图片资源的分辨率应该跟Canvas的分辨率保持一致,也就是正常情况下图片绘制到Canvas上应该不需要缩放。

     

    我们需要避免创建了一个较大的Canvas,但是仍然使用较低分辨率的图片资源,图片绘制到Canvas上还需要经过缩放的情况。这样做毫无意义,因为游戏本身的分辨率是由图片资源的分辨率来决定的,上述的情形既不能提升游戏的精美程度,也白白浪费了系统资源。

     

    如果自己预先指定了Canvas的大小,又希望Canvas在网页中全屏显示,可以通过<meta viewport>标签设置viewport的大小(*1,*2),直接告诉浏览器网页虚拟viewport的宽度应该是多少,并且让viewport的宽度等于Canvas的宽度,而浏览器会自动按照viewport宽度和屏幕宽屏的比值对网页进行整体放大。

     

    注释:
    1. <meta viewport>的设置可以参考这个例子:http://www.craftymind.com/factory/guimark3/bitmap/GM3_JS_Bitmap.html
    2. Android系统浏览器在网页不指定viewport宽度时,它会认为这是一个WWW页面,并且使用980的默认viewport宽度,UC浏览器也遵循了同样的做法。这意味着您不设置viewport宽度,并且直接使用window.clientWidth作为Canvas的宽度时,就会创建出一个980p的Canvas,通常这是毫无意义的;

     Rule #5 避免使用多个On-Screen Canvas

    如#1所述,多个Canvas同时更新会降低GL渲染的效率,并且如#2所述,多个On-Screen Canvas会导致网页本身的绘制时间增加,所以应该避免使用。

    Rule #6 合理地使用Off-Screen Canvas

    在GL加速的Canvas渲染架构下,合理地使用Off-Screen Canvas可以在某些特定的场景提升渲染性能,但是不合理的使用反而会导致性能下降。
    1. 将图片绘制到一个Off-Screen Canvas上,然后把这个Canvas当作原图片使用,这种用法,如#1所述,在iOS上是有用的,但是在Android上不必要的,甚至会导致额外的资源浪费(虽然渲染性能还是一样)。浏览器会自动将需要绘制到Canvas的图片加载成Texture并缓存起来,避免反复加载,只要这个缓存池大小没有超过限制,图片的绘制就只需要付出一次贴图的开销,这对GPU来说是很小的;
    2. 避免使用过大或者过小的Off-Screen Canvas —— 首先过大的Canvas会超过系统的Max Texture Size而无法进行加速(*1,*2),而太小的Canvas(*3,*4),因为对它加速不但不会加快渲染速度,反而会导致如#1所述的一些问题 —— 加快GPU资源的耗尽,频繁切换Render Target的额外开销等,所以也是不加速的;
    3. 避免频繁动态创建和销毁Canvas对象,这样很容易引发GC,而且浏览器为了避免大量的Canvas Buffer把GPU资源耗尽,还会在接近临界值时进行强制GC(*5),而强制GC造成的停顿比一般GC还要长,通常会达到500ms~1000ms。所以一般来说应该事先生成所有需要的Canvas然后一直使用,或者建立一个缓存池来回收和重用;
    4. Canvas初始大小设置后就不应该再改变,否则浏览器需要为它分配新的Buffer;
    5. 需要动态生成的内容,可以在一个Off-Screen Canvas上预先生成,然后直接将这个Canvas绘制到On-Screen Canvas上,但是这个生成应该是一次性的(或者偶尔),而不是每个Game Loop都需要更新,否则就会造成#1所述的问题 —— 频繁切换Render Target的额外开销;
    6. 如果场景中的部分内容很少发生变化,但是位置,缩放比例,旋转角度,透明度等属性需要频繁变化,可以把一个Off-Screen Canvas当作Layer使用,缓存这部分内容,然后在绘制这部分内容时就直接绘制这个Off-Screen Canvas;
    总结一下,Off-Screen Canvas的使用应该尽量遵循以下原则:
    1. 数量适中(越少越好);
    2. 大小适中(面积128×128以上,长宽2048以内,并且为2的幂次方对GPU来说是最友好的);
    3. 一次创建,大小固定,持续使用;
    4. 读多写少(可以在每个Game Loop都绘制到On-Screen Canvas上,但是自身更新/变化的次数应该很少,避免每个Game Loop都更新);
    注释:
    1. 一般手机的Max Texture Size是2048,高端的机器可能会到4096或者8192,Canvas长宽任意一边超过这个大小都无法使用Texture做为自己的Buffer;
    2. 非加速的Canvas仍然使用普通的Bitmap作为自己的Buffer,这意味着它的绘制仍然使用CPU,并且它绘制到另外一个Canvas还需要先加载成Texture,而加速的Canvas本身就是一个Texture,所以它绘制到另外一个Canvas上只需要一次贴图的开销;
    3. WebKit默认的设置是128×128大小以内的Canvas不加速,UC和Chrome都使用了默认的设置;
    4. 把一个比较大,加速的Canvas绘制到一个比较小,不加速的Canvas会非常非常慢,这是因为浏览器需要从显存拷贝内容到普通内存,拷贝的速度很慢并且会造成GL渲染流水线的阻塞;
    5. 一个小技巧是,如果一个Canvas不再使用,可以将它的长宽设置为0,这样在JSVM的垃圾收集器还没有回收该Canvas对象时,浏览器就可以先释放它的Buffer,这样可以避免浏览器因为Buffer占用太多而不得不强制GC,不过总的来说最好还是自己建立缓存池;

    Rule #7 避免频繁调用getImageData,putImageData和toDataURL

    因为它们都会需要从显存拷贝内容到普通内存,或者相反,拷贝的速度很慢并且会造成GL渲染流水线的阻塞。所以不要在每个Game Loop都调用这几个API。

    Rule #8 如果需要最大帧率,优先使用requestAnimationFrame而不是Timer

    如果您的游戏只需要20或者30帧,那么就只能使用Timer。但是如果希望达到设备本身的最大帧率,则应该使用rAF,因为rAF可以让浏览器把网页绘制,Canvas绘制跟屏幕刷新保持同步,减少Canvas更新的延迟,并且在网页不可见的时候还可以自动停止rAF回调,避免无谓的浪费电池。

    Rule #9 图片资源大小应该对GPU友好

    1. 避免使用太多小图片,而是应该把它们拼接成一张大图;
    2. 拼接的图片长宽应该是2的幂次方,并且小于或者等于2048,512×512,1024×1024都是不错的选择;
    3. 拼接的图片应该尽量避免留下大量空白区域,造成无谓的浪费;
     
    • shuyong 8:43 下午 on 四月 13, 2014 固定链接 | 回复

      好文章。以后写的一些文档会参考这里的观点。

      不过这个文章有一个小遗憾。在“Rule #1 为Android而不是iOS而优化”中有一句:
      “Android平台并没有IOSurface的同等物(一块同时支持CPU读写和GPU读写的缓冲区)”

      这里虽然没有错,因为ANDROID平台确实没有提供现成的off-screen nativewindow / nativewindowbuffer。但是应用程序其实是可以自己实现相应的off-screen window / buffer的。这需要思维上有点突破,把自己当成是system engineer,而不只是application engineer。多看一些其它项目,理解interface / implement,就会有新的天地,新的想法了。

    • Roger 10:21 上午 on 四月 14, 2014 固定链接 | 回复

      实际上Android 的 GraphicBuffer 有类似的作用,也可以被应用所使用,但是它有很多限制,不如IOSurface那么灵活,并且不同SoC上的兼容性问题很严重,所以在实际使用中只能用于特定的场景。

      另外不明白这里跟system engineer有何关系,浏览器毕竟不是OS本身,它需要运行在各种不同的硬件/系统版本上,浏览器自身是没办法绕过硬件/系统本身的限制的,除非像Chrome OS/Firefox OS那样。

      • shuyong 11:40 下午 on 四月 14, 2014 固定链接 | 回复

        所谓system engineer,就是有一点可能性,就会尝试实现。Chrome / Firefox也是逐步总结出抽象的移植层,在各个平台上,保证上层都有相同的特性,又要保证可移植性。如果在某个平台没有底层支持,那就实现一个。最终就是要保证上层都有相同的特性。而上层的特性足够多了,足够稳定了,自然而然就想进化到Chrome OS/Firefox OS了。

        我不了解IOSurface,所以对比不了灵活性。但是我做过测试,Android 的 GraphicBuffer应该有足够的稳定性和兼容性,不论是直接调用系统原生的,还是我自己重新实现的。我从Android 2.3到4.3,硬件平台有高通,NVIDIA,FREESCALE,TI,SUMSANG。国产的芯片的系统好像也测过几个版本。GraphicBuffer的接口已经足够稳定,特性也有足够的一致性,否则ANDROID就很难移植和运行了。

        如果自己实现一个off-screen ANativeWindow,通过重载dequeueBuffer / queueBuffer,应该很容易实现CPU / GPU之间访问内存的ZERO-COPY算法的。

        • Roger 9:45 上午 on 四月 15, 2014 固定链接

          如果你了解Chrome,就知道Chrome在Canvas上的实现更保守(或者也可以说是通用)和低效… 所以你既然没有真正了解Chrome的实现,就别在这里乱讲了…

          Android本身对GraphicBuffer的使用比较简单,NativeWindow是Double/Triple Buffering的机制,不存在需要CPU/GPU “同时” 读写同一块Buffer的状况,而Canvas是必须Single Buffer的。如果你测试过Nvidia的芯片,就应该知道Tegra是不支持CPU/GPU “同时” 读写同一块Buffer的,如果一个GraphicBuffer已经通过EGLImage binding到一个Texture上,再试图去unlock它,在Tegra的芯片上就会陷入死锁,高通的Adreno 2xx也有类似问题,Adreno 3xx虽然支持,但是Unlock/Lock一次的开销在3~7ms之间…

  • Roger 9:24 pm on August 12, 2013 固定链接 | 回复
    Tags: , , Chrome, , ios, ,   

    [译文]关于移动Web性能的5个神话 

    译者前言

     

    这篇文章(http://www.sencha.com/blog/5-myths-about-mobile-web-performance/)由Sencha的CEO  Michael Mullany所写,主要是回应早前的一篇引起较多关于移动Web性能讨论的文章“Why mobile web apps are slow”(原文译文),作者的主要观点是“Why mobile web apps are slow”文中给出的数据虽然基本正确,但是对数据的解读却存在误导的成分,并且只考量了JavaScript的性能,而对移动应用来说更关键的Graphics性能并没有被考量在内。并且移动应用性能的提升不仅仅会得益于浏览器提升JavaScript的性能,还会得益于更高程度的GPU加速渲染,多线程并行化处理等等。

     

    译文

     

    我们最近听到了一些在不断重复的关于移动HTML性能的神话,但是这并不完全准确。就像那些流传已久的都市传说一样,它们听起来似乎有理有据,让人信服。但是这些神话是基于不正确的前提,对于原生应用和Web应用的软件堆栈之间的关系的概念是错误的,只是一些因为曲解了数据而推导出的漫无目的的观点。我们认为根据我们多年收集的一些性能数据和对移动Web应用进行性能优化的丰富经验,对这些神话进行澄清是十分重要的。

     

    神话#1:移动Web性能主要是由CPU主导的JavaScript性能所决定的
    真相:大部分Web性能是由浏览器对渲染流水线的优化,DOM操作的速度和使用GPU加速的程度来决定的。更快的JavaScript性能的确不错,但它很少会成为性能的瓶颈。

     

    神话#2:依赖CPU的JavaScript性能仅仅因为硬件的提升才变得更快(aka 摩尔定律)
    真相:在过去4年里面50%以上的JavsScript性能的提升是得益于软件上的优化,而不是硬件上的提升。甚至单线程的JavaScript性能还在不断提升,更不用说现在很多应用开发者通过使用Web Workers来利用多线程提升性能。

     

    神话 #3:移动浏览器最近的性能优化已经基本停滞,未来也没有更多上升的空间
    真相:每一个移动浏览器都在一些特定的领域比起其它浏览器在性能上有10倍-40倍的提高。Surface的SVG性能比iPhone高30倍。而iPhone的DOM操作性能是Surface的10倍。所以对每个浏览器来说,仅仅做到向其它浏览器表现优秀的领域看齐,就有很多性能提升的空间。

     

    神话 #4:移动应用很难再从未来的硬件性能提升中受益
    真相:在过去三年里,每一次硬件更新换代都会带来JavaScript性能的飞跃。不但浏览器的单线程性能仍然在不断提升,并且还会因为更快的GPU速度,更快的内存带宽,和通过多线程并行化处理更充分地利用多核CPU来不断提升性能。越来越多的浏览器已经开始更多地利用并行化来减轻主线程的负担,比如说:Firefox使用了单独的混合线程;Chrome并行化处理HTML解析;还有IE把JavaScript的JIT编译放到了其它线程

     

    神话 #5:JavaScript的垃圾收集对移动应用来说是性能杀手
    真相:这种说法曾经是正确的,不过现在的状况已经不太一样。Chrome在2011年的Chrome 17引入了增量垃圾收集机制Firefox去年也实现了同样的特性。这个特性将每次GC暂停的时间从200ms降到了10ms —— 相当于丢掉一帧 vs 一个可以明显感知的停顿。避免垃圾收集事件的确可以显著提升性能,但是只有你还在使用桌面Web开发模式或者在一个老旧的浏览器上运行,垃圾收集才会成为性能杀手。以我们在Fastbook(一个Facebook HTML5应用的克隆)使用的关键技术为例,通过建立一个DOM节点的对象池来回收不再使用的DOM对象并循环使用,我们成功避免了创建新对象的开销,同时也避免了因为删除旧对象而导致GC。浏览器的确很有可能提供了一个性能非常糟糕的垃圾收集器(比如说旧的IE),但这并不是支持垃圾收集的语言比如JavaScript(或者Java)的天生的无法克服的缺陷。

     

    OK:关键要素

     

    首先,让我们回顾一下最基本的概念。从5万英尺的高度往下看,浏览器本身是构建于操作系统之上的一个丰富和复杂的抽象层。而Web应用混合运用标记语言,JavaScript脚本语言和样式表,使用这个抽象层来创建应用的体验。这个抽象层本身需要额外的性能开销,而开销的多少很大程度决定于你使用抽象层的哪一部分。抽象层的某些部分更快是因为它们离操作系统的系统调用或者某些系统库的调用更近(比如说Canvas2D on MacOS)。另外一些部分会比较慢是因为它们无法直接映射到底层的操作系统,并且它们天生就十分复杂(DOM树操作,JavaScript对象属性访问的原型链遍历)。

     

    很少有移动应用是计算密集型的:没有谁会试图在iPhone上计算DNA序列。大多数应用有着一个合理的响应模型。用户执行一个操作,然后应用会马上回应一个30fps或者更高帧率的视觉动画,然后在几百毫秒之内完成任务。只要应用满足上面的要求,没有人会在乎应用基于的抽象层距离最底层的硅晶圆之间到底有多远。从这点来说,单纯地比较原生应用的抽象层和Web应用的抽象层意义并不大。

     

    对于Sencha来说,我们知道一个优秀的开发者使用移动Web技术和基于一个现代的Web框架比如Sencha Touch,是可以创建出优秀的应用体验的,它运行的足够快,满足用户的期待。并且过去3年的移动性能飞速提升的趋势也使我们深受鼓舞。我们很乐意跟您在接下来的文章里分享一些相关的数据。

     

    但是我们的本意并不是说移动Web应用能够运行的和原生应用一样快,或者它们能够在性能上能够与桌面Web应用相媲美。这不是真实的。移动设备的硬件性能比起桌面设备要慢5倍到10倍:CPU性能比较低,缓存层次结构过于扁平,缺少多级缓存的支持,网络链接的延迟也很高。并且任何软件抽象层本身(比如浏览器)都需要付出额外的开销。其实这不仅仅是Web应用开发者的问题。iOS原生应用的开发者一样可以告诉你,当iOS CoreGraphics在第一款视网膜iPad上性能很低的时候,这使得他们相当一部分人不得不抛弃CoreGraphics而直接使用OpenGL。

     

    进一步追溯神话的真相

     

    通过过去三年持续对Sencha Touch在数据驱动应用中的使用进行性能优化的经历,我们可以很自信的说,我们很少会被最原始的JavaScript性能所困扰。仅仅是在构建Sencha Touch布局系统时,因为Android 2.x的JavaScript性能太差,使得我们改用了Flexbox。

     

    更多时候,我们碰到的问题是DOM操作太慢,浏览器渲染引擎性能比较差和垃圾事件堆积过多无法及时处理。而这些局限基本上都是因为浏览器的架构设计者和开发者造成的,跟JavaScript语言和JavaScript引擎本身并没有本质的联系。举个例子来说,有一次我们和浏览器开发者一起优化浏览器性能,我们最终在某个特定操作上(颜色属性的改变)获得了40倍的性能提高,而这之前是我们的滚动列表实现的性能瓶颈,而类似的例子还有很多。

     

    在iOS和Android上的JavaScript性能

     

    虽然我们说过JavaScript性能其实对于移动设备来说并不是那么重要,但是我们还是希望可以推翻它并没有得到改善的神话。下图是SunSpider测试(越低越好)在iOS上过去4年的得分(按照硬件版本和OS版本划分)。(很幸运,SunSpider是一个广泛被应用的测试,所以我们很容易就在网上找到旧的iOS设备的测试成绩)。2009年的时候,当时运行iOS3的iPhone 3GS得分是15,000 —— 非常的糟糕,跟当时的桌面浏览器的300-600的得分相差30倍左右。

     

     

    然而,如果你把iPhone 3GS升级到iOS 4,5和6,你就可以在同样的硬件上面体验到4倍的性能提升。(在iOS4到iOS5之间性能的巨大的飞跃主要得益于新的Nitro引擎。)实际上SunSpider成绩在iOS7上仍然会有所提高,只是基于保密协议我们这里就不再多说了。跟今天的桌面浏览器相比,最先进的移动浏览器大概还有5倍左右的性能差距 —— 比起09年的30倍已经是相当大的改进。

     

    如果需要了解更多关于iOS硬件和软件性能改进的信息,可以参考Anandtech去年10月份的评测

     

    在Android平台上也差不多有相当等级的改进。从我们的测试实验室,我们找到了一些可以认为是过去3年里面在当时比较有代表性的高性能机器。我们测试了下面4款手机:
    • 三星Captivate Android 2.2(2010年7月发布)
    • Droid Bionic Android 2.3.4(2011年9月发布)
    • 三星Galaxy Note 2 Android 4.1.2(2012年9月发布)
    • 三星Galaxy S4 Android 4.2.2(2013年4月发布)

     

    如下图所示,SunSpider的成绩在过去4年的提升非常明显。从Android 2.x到Android4.x就带来了接近3倍的提升。

     

    无论是iOS还是Android,这些性能提升都不仅仅是由于摩尔定律本身带来的。过去3年,如果按照摩尔定律,我们期望获得的性能提升大概是4倍左右(18个月提升一倍),但实际上却远远不止,所以软件上的优化的确起了相当大的作用。

     

    更多有意义的测试

     

    如之前我们已经提过的,SunSpider的测试成绩其实越来越不重要,因为它跟应用本身的性能要求关系其实并不大。相反,DOM操作的测试,还有Canvas和SVG的测试成绩对应用的用户体验关系更密切。(理想状态下,我们应该还要去测量改变CSS属性的速度,还有CSS动画,过渡动画和几何变换动画的帧率 —— 因为它们在Web应用中使用的更频繁 —— 不过由于缺少浏览器的支持,仍然无法准确地获取这些测试数据)

     

    第一个DOM操作测试:我们使用了Dromaeo Core DOM测试。下图是之前的4台Android设备上得到的测试成绩,我们把Captivates上的4项Core DOM测试成绩(Attributes,Modifications,Query,Traversal)作为1分,其它设备的测试成绩就是相对于Captivates的得分,然后取4项得分的平均值作为最终结果。

     

     

    如你所见,Android从2.x到4.x带来了3.5倍的性能提升,虽然S4比起Note2的提升幅度比较小。我们在iOS设备上也进行了Dromaeo测试。不幸的是,由于iOS无法降级,我们没法得到老的iOS版本的测试成绩,不过我们仍然可以看到随着硬件的升级,Dromaeo测试性能一样是稳步上升。并且有趣的是,不同的iOS6设备之间的Dromaeo性能提升幅度要大于它们的CPU速度提升幅度,这说明了内存带宽或者缓存的速度提升肯定带来了更大的帮助,所以才能比单纯依靠摩尔定律所能获得的结果更好。

     

     

    为了显示浏览器还有多少潜在的性能提升空间(仅仅是为了赶上其它浏览器表现优异的领域),我们还测试了Surface RT。IE槽糕的DOM操作性能一直困扰着我们,但这说明了Surface RT上的IE10在DOM操作上还有很大的改善空间。这也是我们之前打破的一个神话 —— “移动设备的软件堆栈本身已经足够好,未来没有太多的优化空间”。起码对于Windows RT来说,在DOM操作上还有10倍的差距需要去填补。(我们后面还会展现在哪些测试上,iOS表现不佳)

     

    图形性能

     

    除了展现JavaScript和DOM操作性能的巨大提升外,我们还想为您展现浏览器在Canvas和SVG上的性能提升。我们之前就发现了Canvas2D性能在同样硬件上iOS5比iOS4提升了5-8倍。甚至当iPad 2升级到iOS5后,一些局部测试提升了80倍。并且因为Canvas实际上是对iPhone上的CoreGraphics API的直接调用,所以当原生图形性能获得提升时,Canvas性能也获得了同样的提升。在下面的测试中,我们使用了mindcat Canvas2D benchmark来测试性能。这里,我们看到了随着iPhone硬件的提升(都运行iOS6),Canvas性能也在不断提升。

     

    请牢记,上图的显示的数据已经计入了iOS4到iOS5的性能飞跃。如你所见,上图显示出历代iPhone在Canvas2D上的性能提升达到了7倍之多,远比它们的CPU速度提升幅度要大(按照摩尔定律CPU最多也就提升了4倍),这也反映了iPhone的软件堆栈充分利用了CPU/GPU来提升自身的性能。移动Web性能的提升实际上有很大一部分是受益于GPU性能的提升和浏览器更多使用GPU进行图形加速。

     

     

    我们再来看看在Android上运行同样的测试,我们看到一些有趣的数据显示Android上Canvas性能跟CPU性能之间并没有必然的联系。从Android 2.x到Android 4.x上的性能飞跃主要是因为2.x的Canvas完全没有使用GPU加速。这再次说明了,仅仅是浏览器充分利用GPU加速就能够带来巨大的性能提升。

     

    SVG测试

     

    SVG是另外一个可以展现Web性能的广阔的领域。虽然不如Canvas流行(很大程度是因为Canvas已经足够快),SVG的性能随着硬件提升仍然在稳步提升。下图是来自Stephen Bannasch的一个测试,测试了绘制10,000线段构成的一个SVG路径所需的时间。再一次,测试结果显示了各代iPhone CPU/GPU性能提升带来的稳定的SVG性能提升(所有的iPhone都运行iOS6)。

     

     

    但是最大的差距还是源于软件本身:Surface RT比iPhone 5快了30倍(对比iPad4也是如此,虽然这里我们没有列出来)。实际上,Surface RT比起运行在我一年前买的Macbook上的Safari仍然快了10倍!这个差距是是否使用了GPU加速造成的,Window 8/IE10在SVG上充分利用了GPU进行加速,才获得了如此惊人的成果。只要浏览器开发者把更多原来由CPU完成的工作转移到GPU上面去,我们就有可能在iOS和Android上也看到同样的性能提升。

     

    除了上面的长路径绘制外,我们还运行了另外一个来自Cameron Adams的SVG测试,测试了500个不断反弹的小球的动画帧率。再一次,我们可以看到由硬件提升所带来的SVG性能的稳步提升。

     

     

    比起单纯的性能数据,最终的fps值更让人感兴趣。一旦动画超过了30帧,你就可以得到一个跟电影动画(24fps)相似的结果,这样的流畅度已经基本上可以让观看者满意。如果能够达到60帧,那你就会获得由GPU加速带来的极致流畅的体验。

     

    真实世界的性能:垃圾收集,动态语言和更多

     

    我们希望之前的移动性能探索之旅已经说明了一些事情,也打破了一些神话。我们希望为您展现下列真相:
    • JavaScript的性能持续地快速提升
    • 性能的提升由硬件提升和软件优化同时驱动
    • 虽然高性能的JavaScript是一件好事,但实际上大部分Web应用的性能跟JavaScript性能的关系甚少
    • 幸运的是,其它影响Web应用性能的领域,像DOM操作,Canvas,SVG的性能也在飞速提升

     

    虽然我们可以展现一些高速摄影机下的动画测试,不过实际上所有移动Web应用的开发者都清楚,CSS动画,过渡动画和属性修改的性能从Android 2.1开始已经得到极大的提高,并且它们还在不断提高。

     

    之前我们已经澄清了一些不真实的论断,现在再让我们做一个最终的说明。我们不断听到的各种传言汇总而成的最终结论是“移动Web应用总是很慢,这是因为JavaScript是一种低性能的动态语言,并且垃圾收集机制对性能是一个极大的伤害”。应该说这个结论本身并不是完全错误的。不过如果你的Web应用使用类似Sencha Touch这样的框架来动态产生DOM内容,一个很大的优势在于,我们会在浏览器之上,在特定的应用上下文下,合理地去管理对象的创建和销毁,包括事件对象。这样即使你的应用需要展现无穷尽的数据内容(通过表格,列表或者转盘),我们通过回收DOM对象,过滤多余的事件,对要执行的动作进行优先级排序等优化,可以帮助您的应用获得60fps的视觉动画体验。

     

    如果没有一个中间层进行类似的间接处理,的确很容易得到非常糟糕的移动Web应用体验 —— 就像Facebook移动Web应用的第一个版本一样。我们相信如果应用直接使用类似jQuery Mobile这样直接操作底层DOM模型的UI框架时,在可见的未来的确会持续受到性能相关问题的困扰。

     

    汇总

     

    文中包含了大量的数据和覆盖了不同的主题,最后在这里让我们再总结一下。如果您是一位开发者,您应该可以从这篇文章了解到:

    • 移动平台的速度不及桌面平台的1/5 — 较慢的CPU,还有受限的内存大小和速度和较慢的GPU等等。这些都是无法改变的事实。
    • 移动平台的JavaScript + DOM的访问速度越来越快,但是你始终应该把iPhone 5看作跟2008年在桌面电脑上运行的Chrome 1.0一样 (即比桌面版的IE8快5-10倍)。
    • 移动Web应用的图形性能随着浏览器更多使用GPU进行图形加速和其它通用软件优化,已经基本可以实现30帧每秒的动画。
    • 垃圾收集和平台渲染性能有限的问题仍然会使你困扰,所以使用一个类似Sencha Touch这样的抽象框架来获得更佳性能是十分有必要的。
    • 充分利用移动Web平台提供的远程调试器和性能监控能力:像Chrome for Android现在已经提供了一个不错的fps计数器,还可以显示需要混合的图层边界,这可以告诉你哪些网页内容实际上已经生成了贴图并由GPU负责绘制,还有贴图被加载的次数。

     
    我们希望对这些性能数据的回顾能够帮助我们打破一些虚假的神话。我需要感谢在Sencha的所有人对这篇文章的贡献,包括Ariya Hidayat 的审阅和提供了大量关于浏览器性能优化的文章链接,还有 Jacky Nguyen关于Sencha Touch的抽象层如何进行性能优化的一些实现细节。

     

    翻译后记

     

    喔,终于翻译完了,以前还没有翻译过这么长的文章,没想到还真是一件累死人的事情。每一句话都需要斟字酌句细细体会字面下的意思,再用较为通顺的中文表述出来,无论是脑力还是体力都是相当大的摧残,说多了都是泪啊 =_=

     

    应该说要翻译这篇文章,甚至要读懂这篇文章,译者和读者都需要对浏览器内核的一些工作原理有所了解。
    • 比如文中多处强调JavaScript RAW performance和DOM Interaction的区别,这是因为虽然DOM Interaction虽然也是由JS去调用,但是对浏览器来说,实际上JS只是调用浏览器内核提供的JS Binding API,整个Interaction是由浏览器本身去执行的,所以不应该当作JavaScript本身的性能来考量。
    • 又如浏览器所构建的抽象层的不同部分直接或者间接映射到OS的系统调用或者系统库调用对性能的不同影响的说法要怎么理解?举个例子来说,Web应用开发者可以用DOM+CSS或者用Canvas实现同样的动画效果,下面分别是基于QuarkJS实现的两个同样的动画,一个基于DOM,一个基于Canvas,对于Canvas绘制直接使用OS本身的2D绘图库去实现,并且支持GPU加速的浏览器来说,Canvas动画的效率会比使用DOM要高的多,这是因为基于DOM和CSS的动画,浏览器通常需要进行重新计算样式,重新排版,重新光栅化,重新上传贴图,重新混合等这样一个复杂的流程,效率自然高不起来。再举一个例子,对于支持图层混合加速(Accelerated Compositing)和硬件加速的浏览器来说,对付CSS Transform这样的动画就是小菜一碟,因为对它来说这个动画就是不断改变元素所属图层的Transform属性,然后使用GPU重新混合的过程。而支持硬件加速的浏览器所谓的图层混合其实就是通过OpenGL进行贴图的这样一个过程。

     

    最后要说的是,文中的一些观点还是需要在一定的条件下才能成立的,并不是放之四海而皆准,这是读者需要留意的地方:
    • 大部分Web应用性能跟JavaScript性能关系不大,对它的要求不高

     
    是的,大部分是这样的,但不见得你的Web应用就是这大部分之一。实际上,对于有一定复杂程度的基于Canvas的Web Game来说,JavaScript性能很有可能成为它的性能瓶颈。这些Web Game的场景通常比较复杂,包含成百甚至上千的绘图对象(比如实现一个绚丽的粒子效果),需要在JavaScript里面构建一个成百上千个节点的Scene Graph。每绘制一帧,都意味着需要对这个Scene Graph进行遍历,访问每一个节点,更新它的状态,然后再调用Canvas API将它绘制出来。如果要达到30fps的速度,这意味着最多只有30ms左右的时间来完成每一帧(实际上应该没有那么多),即使不算Canvas API本身的绘制开销,单单是遍历和状态更新的操作就很有可能达到几十毫秒的量级了,特别是状态更新中包含大量的碰撞检测和物理运动计算的时候。

    • 通过并行化处理是未来浏览器有效提升性能的一个有效手段

     
    应该说,当前通过并行化处理充分利用多核CPU/GPU提升性能是浏览器内核技术研究发展的一个热点。但是并行化并不是银弹,指望它能够短期内戏剧性地大幅度提升浏览器的整体性能并不现实。

    1. 首先对于移动设备来说,iOS还好,但是Android由于自身的开放性,硬件水平参差不齐,低端硬件还有相当大的保有量,它们缺乏足够的资源去支持并行化,并行化对它们来说反而更糟糕。不过得益于像MTK这样的芯片厂商大力提升中低端设备的性能,现在的千元机性能已经跟几年前不可同日而言,大概再过多一两年这个问题应该就不再成为问题了。
    2. 其次并行化处理并不是想象中的那么容易,因为浏览器的大部分作业实际上都有某种程度的顺序依赖和上下文依赖,需要很多额外的处理才有可能实现部分并行化(毕竟不是数据处理,要做到完全并行化可能性极低)。这样的并行化需要额外的开销,并且只适应于部分场景,有一定的局限性。目前浏览器除了网络链接的部分,并行化程度最高的应该就是渲染了,除了图层混合会运行在独立线程并且主要使用GPU外,像Chrome,Android Browser都把光栅化从主线程剥出来,渲染性能的确从并行化中获益极大,不过也付出了额外的CPU/内存开销的代价。其它领域的并行化进展还是很慢,并且也难见有可能使得性能大幅度提升,比如Chrome在做的HTML解析和样式计算的并行化,最多也就能够减少网页从开始加载到第一次完整呈现的时间,对于整体性能提升意义不大。至于JavaScript,除了IE所实现的JIT并行化外,垃圾收集也是一个有可能剥离出主线程的领域,只是我个人对JavaScript引擎了解不多,不知道具体的技术难点在哪里。
    3. 最后还需要前端开发者有意识地去使用并行化,或者为了更好地支持浏览器的并发作业对自己的应用进行专门优化,比如说Web Workers可以让部分JavaScript代码运行在独立的线程,但在实际的网页里面使用的应该很少。

     

    最后的话

     

    作为读者,如果您能够一直看到这里,说明您应该对Web App/Game开发是有着真爱的^_^ ,所以不妨再看完这最后一节。从我个人的开发经验来看,一个经过充分优化的应用比起没有经过优化的应用通常会有非常明显的性能差别,如果您的Web App/Game对性能要求很高,并且主要运行在移动平台,那么性能优化对您来说那就更加重要了(移动平台可没有那么多可以挥霍性能的空间)。而为了帮助前端开发者更好地做好性能优化,Chrome提供了可称为逆天的神器Dev Tools,学会使用这套工具(推荐Code School上面的视频教程),然后使用它来对您的应用进行性能分析和优化,您会发现这才是真正能够获得戏剧性的性能飞跃的最大可能,这也是所谓的“求诸人不如求诸己”。

     
  • Roger 1:20 pm on April 20, 2013 固定链接 | 回复
    Tags: , , , Chrome,   

    国内Android手机浏览器内核未来竞争势态分析 

    前几天跟主管谈到这个话题,讨论得出一些结论,记录下来以备将来验证。国内手机浏览器在所谓“自有内核”的竞争上将会分为两个不同阶段,目前处于第一阶段。

    第一阶段是基于WebKit开发的“自有内核”,大概有两种方式,一种是WebKit内核代码(WebCore部分)直接跟主干保持同步,定期更新,平台适配层架构基本跟Android系统浏览器的适配层架构保持一致,将大部分系统浏览器适配层代码移植过来;另外一种就是直接全部采用Android系统浏览器的代码,包括适配层代码和WebKit内核代码,然后WebKit内核代码再逐步向主干靠拢(Android系统浏览器包含的WebKit内核代码版本比较旧);当然这两种方式发展到一定程度,其实是殊途同归的,相对来说,第一种方式对团队的整体水平要求更高一些。

    这一阶段的主要玩家就是UC和腾讯,百度也有参与,但是它的移动战略实在让人看不懂,感觉十分摇摆不定,按理说百度人和钱都不缺,但是始终没有下定决心大幅度投入手机浏览器领域,不知道在第二阶段是否会改变策略。

    其它国内桌面浏览器的主要玩家如360,搜狗,金山基本不参与这一阶段的竞争,360只在Android系统提供的WebView组件之上做了一层壳,搜狗和金山压根就没有什么大动作,360去年上半年还通过猎头在挖做手机浏览器内核的人,据说金山本来也准备投入,但是后来都改变了策略,更多把人力投入到桌面浏览器的竞争上面,在手机浏览器上,估计会直接参与第二阶段的竞争(听猎头说,传言金山本来已经成立手机浏览器团队,但是后来又合并回去全力做桌面浏览器)。

    第二阶段的“自有内核”会是基于Chrome&Blink,Chrome从WebKit主干分离出Blink,开始自己独自主导浏览器内核的发展,并且围绕Blink构建了一套Content API,用于将Chrome核心模块如多进程沙盒模型,渲染架构,网络堆栈,V8&Dart虚拟机和Blink内核封装在一起,用于支持Chrome本身和第三方基于Chrome&Blink内核的浏览器的开发(如新版的Opera)。

    从目前的状况来看,Chrome&Blink内核在Android平台大范围普及的时机还不成熟,主要在于Chrome&Blink内核本身还处于一个不太稳定的状态,Content API还在剧烈变化中,代码的性能优化和系统稳定性还有很多不足,并且Chrome本身的多进程架构对手机的性能有比较高的要求,包括CPU/GPU和内存大小,一些性能较差的手机连基本操作的流畅度都无法保证,另外默认的渲染架构强制要求4.0以上的系统版本。这些原因会导致第二阶段的竞争最早也会在明年才开始,开始贴身肉搏式的剧烈争斗预估也要到2015年。

    估计360,搜狗,金山会直接投入第二阶段的竞争,可能现在已经开始筹建团队和进行初期的技术准备。对他们来说,第一阶段已经基本尘埃落定,参与的意义不大,有限的人力还不如早点开始准备第二阶段,并且他们在桌面浏览器上本来就是基于Chrome在开发,所以第二阶段的竞争对他们来说反而在技术储备上会更有利。

    最终,从明年到2015年上半年这段时间,国内Android手机浏览器在“自有内核”上竞争的主要玩家会逐步增加到5~6家,因为各方都是基于Chrome&Blink进行二次开发,要获得相对于其它竞争者的技术优势会变得殊为不易。技术上的领先更多会体现在谁能更快跟进HTML5标准的发展,跟进由Google主导的Chrome&Blink内核本身的演进,一些局部的性能优化和更好地解决设备兼容性问题和系统稳定性问题。
     
  • Roger 5:03 pm on April 4, 2013 固定链接 | 回复
    Tags: , Chrome,   

    Why Blink and Why not Blink 

    清明放假的第一天,Mozilla 和 Google同时宣布了他们新的浏览器引擎的开发计划 —— Servo 和 Blink。Servo 早前其实就一直有消息了,而 Blink 的发布则是相当突然,因为工作的原因,我自然是对 Blink 更感兴趣(放个假都不得安生,苦逼的程序员),更希望了解 Google 为什么要从 WebKit fork 出一个新的浏览器引擎(Why Blink),这样的做法会给 Chrome 后续的发展带来什么样正面变化(Why not Blink)。

    在Blink的官方主页里,Google 认为 Blink 的使命是通过技术创新和良好的社区合作来推动开放网络的发展(Blink’s Mission : To improve the open web through technical innovation and good citizenship)。根据官方 Developer FAQ 的说法,Google 之所以选择不再继续跟随 WebKit 而是独自开发新的引擎的主要原因是:

    There are two main reasons why we’re making this change.

    The main reason is that Chromium uses a different multi-process architecture than other WebKit-based browsers. So, over the years, supporting multiple architectures has led to increasing complexity for both the WebKit and Chromium communities, slowing down the collective pace of innovation.
    In addition, this gives us an opportunity to do open-ended investigations into other performance improvement strategies. We want web applications to be as fast as possible. So for example, we want to make as many of the browser’s duties run in parallel, so we can keep the main thread free for your application code. We’ve already made significant progress here–for example by reducing the impact JavaScript and layout has on page scrolling, and making it so an increasing number of CSS animations can run at 60fps even while JavaScript is doing some heavy-lifting–but this is just the start.

    We want to do for networking, rendering and layout what V8 did for JavaScript. Remember JS engines before V8? We want the same sort of healthy innovation that benefits all users of the web, on all browsers.

    在多进程架构上,Google一开始就独自开发了一套沙盒多进程架构,它和后来由Apple主导的WebKit2多进程架构差异很大,为了支持WebKit2架构而加入WebCore的大量代码,对Google不但一点用也没有,还不得不花时间去处理兼容性的问题,而Google需要修改WebCore来支持自己架构的代码又很难进入WebKit主干,必须很小心处理避免影响其它的Port,大量的代码不得不通过迂回的方式放在外部处理,一些没方法在外部处理而需要对WebCore进行大改的特性不得不暂时放弃。

    并且,因为历史原因,WebCore本身一开始就没有多线程或者多进程的概念,现有的架构对并行处理的支持非常困难,Google也认为必须对WebCore进行整体架构上的大改才能更好的支持并行处理,更充分利用多核CPU的能力,避免主线程过度拥挤(虽然现在大部分的WebKit Port都把主要的渲染工作分离到其它线程,但是主线程仍然需要负担HTML解析,CSS样式计算和匹配,排版,JS执行等繁重的任务,为了避免单项任务长时间阻塞主线程,WebCore目前是用延时Timer的方式将一个复杂任务分解成多段来顺序执行,这种方式即不优雅,更无法充分利用多核的能力)。

    另外,WebCore现在的模块化比较混乱;一些历史遗留的代码和仅仅用于支持某些特定平台的代码导致WebCore代码臃肿不堪;平台相关的处理也没有一个统一的标准和方式,没有一个很好的抽象层去隔离平台相关和平台无关的部分;WebCore为了可以同时支持不同的JS虚拟机(如JSC和V8)导致了额外的性能开销和妨碍了对JS性能更多的改进;除此以外,更安全的隔离机制;对现有的网络层进行更大的结构优化等等这些原因也是Google需要自己发展Blink的主要原因。

    总之,Chrome有太多激进的改进需要对WebCore进行大改,而原来那种在外围做文章,曲线救国的方式再也行不通,为了能够自行主导架构的演进方向,避免跟其它Port相互干扰,相互扯皮给双方带来的困扰和痛苦,加快开发的速度,从WebKit主干分离,自己发展新的浏览器引擎就成了必然的选择。

    从Chrome自身的开发者来看,对此无一不表示欢欣鼓舞之情 ^_^,纷纷表示 ——

    “这实在太令人兴奋啦!我们早就该这么做啦!”
    “以后老子想怎么改就怎么改,想蘸糖吃就蘸糖吃,想蘸醋吃就蘸醋吃,再也不用跟那帮斯扯来扯去纠缠不清”
    “自从用了Blink以后,内个不痛,月月轻松… so easy!妈妈再也不用担心我的学习啦…”

    http://infrequently.org/2013/04/probably-wrong/

     
  • Roger 11:11 am on February 2, 2013 固定链接 | 回复
    Tags: , Chrome,   

    我的2012 

    去年放假时为我的2011一整年写了一篇回顾,今年也打算继续,希望以后每年坚持下来形成习惯。

    2012年的最大变化就是工作内容从外壳的GUI框架层转向内核的网页渲染:
    • 年初第一季度的研究重点是在Android 4.0系统浏览器的硬件加速AC渲染架构和整个适配层的架构;
    • 接下第二季度的工作是参考系统浏览器的渲染架构,在我们的浏览器上也实现多线程渲染架构,当时考虑4.0的渲染架构太过复杂,而且很多版本兼容性问题无法很好解决,所以还是以2.3的系统浏览器为主要参考对象;
    • 完成初始的预研工作,实现演示Demo后,项目这边决定第三季度在8.6上多线程渲染架构改造,不过后续的实际开发工作中碰到了很多困难,主要是因为程序从原来的单线程架构一下子改成多线程,需要重写的模块和修改的地方非常多,一些原来没有很好处理多线程的地方引发了大量的崩溃问题,并且多线程在低端机上的性能表现一开始不是很好,也花费了大量的时间去做性能分析跟优化;
    • 接下来的8.7版本我的主要工作是在8.6的基础上进一步优化一些基础渲染性能,如滑屏,缩放等,最终的表现也还算不错,渲染性能比起以前的版本改进很大,虽然还无法跟支持硬件加速的4.x系统浏览器相比,但是在还未支持硬件加速的情况下也基本上做到极致了;
    • 做完8.7后,8月下旬开始准备我们浏览器整个适配层的改造工作,在8.6,8.7多线程渲染架构改造的基础上,我们决定完全抛弃掉以前自己写的C++适配层和一套从塞班时代延续过来的自己开发的C++ GUI Toolkit,完全转向跟系统浏览器一样的Java和C++结合的适配层架构,跟Android SDK一样,内核为外壳提供一个标准的Android UI组件WebView,而外壳完全使用Android原生的UI组件进行开发。不过跟8.6,8.7时不太一样的地方在于,这次我主要负责前期技术研究和框架搭建的工作,在完成Demo,技术文档和培训与宣讲后,后续的主要工作就是协助其它同学对各自负责的模块进行设计与实现,只负责一些设计和代码Review的工作而不再直接参与8.8的项目工作;
    • 10月开始了硬件加速方面的研究,在之前工作的基础上,开始移植4.1系统浏览器的渲染架构代码(这时4.1已经发布了,并且4.1比起4.0又复杂了很多),初始的移植工作完成后,已经可以运行一个硬件加速AC的Demo,接下来的主要工作就是解决版本兼容性问题,我们不但需要解决2.x和4.x之间的版本兼容性问题,而且还要解决4.x不同版本之间的兼容性问题,版本兼容性问题的解决使得我们可以一个程序同时运行在2.x和4.x上面,支持非硬件加速和硬件加速两种不同的渲染路径,可以在运行时根据系统决定选择哪种渲染方式,最后因为4.2的发布,我们又把4.2的代码也同步了过来;
    • 前面的工作完成后就差不多到年底了,马不停蹄地又开始了硬件加速2D Canvas的研究,为此也学习了Android的OpenGL ES编程,加深对Android Native Window System,EGL,GLES的理解,另外还研究了Chrome for Android的2D Canvas实现,到目前为止,硬件加速2D Canvas已经基本实现,Demo初始的测试结果还不错,跑GUIMark 3的帧数比Chrome正式版(Chrome 18)要高,不过比Chrome Beta版(Chrome 25)要低,但是考虑到目前我们所使用的Skia的代码比较旧(来自Android 4.2 AOSP),而Chrome这几个版本还在不断优化Skia的性能,后续升级Skia后,相信性能还会再有所提高;
    2013年,最重要的工作就是在之前的硬件加速相关的研究工作的基础上,协助硬件加速AC,硬件加速2D Canvas顺利进入产品,从过往的经验也可以知道,从Demo到产品,后续要做的工作还很多。另外下半年也有可能会去研究WebGL(3D Canvas)。

    而对自己的职业发展的规划,主要的技术研究方向还是会集中在以下几个领域:
    1. 网页渲染相关的研究(CSS动画,Filter/Shader,2D/3D Canvas,Chrome的多进程渲染架构等等);
    2. Android图形子系统
    3. 2D/3D绘图(Skia,OpenGL ES等等);
     
  • Roger 5:04 pm on February 4, 2012 固定链接 | 回复
    Tags: , Chrome, Firefox, IE, Opera,   

    5个浏览器和它们对应的交通工具 

    Firefox:面面俱到,功能强大,但是太多愚蠢的扩展让它几乎无法使用。

    image


    Safari:虽然性能不错,但是它的实际品质被它的使用者夸大了。

     

    image


    Opera:有一些人真的很喜欢它,但是其它人都认为它看起来真的很愚蠢。

    image


    IE:几乎没有任何作用,唯一的用途是用来被人口诛笔伐。

    image


    Chrome:非常快,嗯,就这样。

    image

     

    原文:5 Browsers and the Modes of Transportation They Resemble

     
    • fxwan 2:49 下午 on 二月 13, 2012 固定链接 | 回复

      IE: Worthless, but good for downloading other browsers.

  • Roger 11:07 am on June 24, 2011 固定链接 | 回复
    Tags: Chrome,   

    WebKit 代码阅读笔记 (二) 理解Chrome 

    WebKit 代码阅读笔记(一) 理解WebKit Port


    Image

    http://www.webkit.org/blog/101/back-to-basics/

    WebKit begins where the chrome ends.


    Chrome在WebCore中代表了外围的窗口环境,它是WebCore跟窗口环境进行整合的一个重要桥梁,Port需要实现的一个最重要的回调接口就是ChromeClient,而Chrome会把相应的调用转发给ChromeClient由Port来处理,一般而言Chrome处理如下事件或者提供如下服务:

    1. 用于接收页面刷新事件的回调,页面刷新可能是JS触发的,也可能是输入事件触发的,
    2. 用于接收页面滚动事件的回调,滚动通常是由于页面处理输入事件引发的
    3. 窗口到屏幕之间的坐标换算
    4. 当前光标形状的设置
    5. 提供窗口的大小,页面显示的ViewPort的大小
    6. 提供页面缩放系数
    7. 响应页面焦点元素的变化事件
    8. 响应页面要求打开新窗口的请求(在新窗口中打开新的页面)
    9. 响应要求窗口以模态方式运行
    10. 响应要求窗口关闭
    11. 响应对工具栏,状态栏,菜单栏的显示和隐藏
    12. 响应弹出消息提示对话框的请求(比如JS的alert)
    13. 响应鼠标悬停在某页面元素的事件
    14. 响应显示提示信息(ToolTip)的请求
    15. 响应页面打印的请求
    16. 响应页面要求获得当前地理信息的请求(未来会移到GeolocationClient处理)
    17. 响应打开文件对话框选择文件的请求
    18. 响应要求全屏播放视频的请求
    19. 响应页面要求显示通知窗口的请求(Notification)
    20. 响应页面为多选框(ComboBox)创建下拉选项窗口的请求
    21. 响应页面要求显示上下文菜单的请求

    下面以在QtWebKit中如何处理滚动为例,来说明Chrome的作用:

    1. 首先滚轮事件(WheelEvent)或者鼠标在滚动条上面的点击事件(MouseEvent)会被QWebView(Qt Port对Page提供的一个Widget的封装)获得;
    2. 事件会转发给页面的事件处理器(EventHandler,每个Frame自带一个)
    3. 事件处理器经过处理后确认滚轮事件(或者在滚动条上的点击事件)会引发一个滚动事件
    4. 然后滚动事件通过层层转发后经由Chrome转发给ChromeClientQt处理
    5. ChromeClientQt实际上最后调用QWebView的scroll方法来真正滚动UI组件
    6. QWebView的scroll会引发页面重绘,然后我们最后就看到页面内容的滚动

    整个过程让人觉得有点兜圈子的感觉,为什么QWebView不直接自己处理滚动,然后重绘页面呢,我想是WebKit本身希望在自己内部可以处理尽可能多的事情,这样对外部窗口环境的依赖性就可以降到最低,外部的窗口环境只需要依照WebKit本身的指示在适当时候将内部的状态反映(同步)出来即可。


    更多:

    QtWebKit Visual Studio 2008 编译和调试环境建立指南

    https://rogeryi.wordpress.com/2011/06/08/qtwebkit-visual-studio-2008-compile-debug-env-setup-guide/

    Chrome的编译:https://rogeryi.wordpress.com/2011/03/29/chrome-webkit-build-on-windows/

    WebKit资源收集 https://rogeryi.wordpress.com/2011/03/28/webkit-resources/

     
  • Roger 10:58 am on March 29, 2011 固定链接 | 回复
    Tags: Chrome, 编译, , 构建   

    Chrome/WebKit 编译构建 (Windows) 

    图:编译的TestShell工具和打印的渲染树

     

    主要的参考文章

    官网提供的构建指南,不过有些过时(访问需翻墙)。

    Flex的博客,提供十分有用的中文指引。

    主要工作

    Chrome的构建工作主要包括下面几个部分:

    1. 预备编译环境,包括安装VS2005或者VS2008,一大堆的补丁,Windows7 SDK和DirectX SDK,官方的构建指南里面已经有详细的说明。
    2. 获取源码,首先是获得部署工具,再使用部署工具获得源代码,可以参考Chrome编译一文。
    3. 生成VS工程并编译,新的源码已经不直接附带VS工程,需要使用部署工具动态生成,具体参考Chrome编译一文,关于工程的说明和疑难排除可以参考官方的构建指南。

    实际构建过程碰到的一些问题

    下面列出实际构建过程中碰到的一些问题和处理方法,希望对各位有帮助。

    • Windows7 SDK分32位和64位版本,下载时要选对。
    • 直接使用gclient签出所有的源代码碰到的问题会比较少。
    • 如果是自行使用svn直接签出src目录的,在调用gclient建立仓库到目录的绑定后(在根目录生产一个.gclient的文件),需要再gclient sync一下,因为src下面很多第三方库或者工具实际上在是别的svn路径上面(在trunk/deps/third-party或者trunk/tools/third-party下面),gclient会根据src下面的DEPS文件从别的路径签出这些库和工具。
    • 如果使用gclient生成VS项目工程文件时,python报错说缺少gyp模块,可以从下面地址http://code.google.com/p/gyp/签出gyp项目,然后运行“setup.py build”构建gyp模块,并把生成的模块(build\lib\gyp)拷贝到gclient使用的python程序的lib/sit-packages下面(tools\depot_tools\python_bin\Lib\site-packages)。
    • 如果不堪忍受Checkout源码的过程,可以直接下载打包好的源码包:http://chromium-browser-source.commondatastorage.googleapis.com/chromium_tarball.html建议选择这种方式,如果要更新到最新版本,运行gclient sync即可。

    官方的一些加速构建过程的建议

    Chrome的构建非常耗费时间,官方提供了一些可以加速构建过程的建议。

    1. 使用真正的多核CPU
    2. 使用64位操作系统
    3. 最好匹配8G以上的RAM(-_-p)
    4. 使用VS2008
    5. 禁止病毒防火墙扫描临时文件和工作文件夹
    6. 重整硬盘碎片
    7. 根据RAM的大小调整最大并行编译的数目,比如说12G RAM可以使用8并行编译,内存越少应该设定的并行编译的数目就越少

    个人在一台I5/4G/Win7 64/VS2008的笔记本上面编译TestShell项目大概花费差不多一个多小时,当然里面最耗费时间就是编译WebKit了,大量使用模板的C++代码编译起来真是龟速,不过以以前编译Qt动侧就是大半天来说这还算是好的了,如果实际开发的话,不知道是不是要匹配8核12G才会比较爽…

     

     
c
Compose new post
j
Next post/Next comment
k
Previous post/Previous comment
r
回复
e
编辑
o
Show/Hide comments
t
返回顶部
l
Go to login
h
Show/Hide help
shift + esc
取消