关键技术: JavaScript,ArrayBuffer,Type Array,DataView,Web Worker,性能对比
ArrayBuffer

在文章开头列出了这些关键字,主要就是让大家了解本文的主要内容,如果你不感兴趣转发了就可以走;如果对这一块非常了解,欢迎多提意见多交流;如果想这方面的技术一见钟情,那不妨坐下了可以享受阅读的乐趣。

首先,为什么Web开发者需要不断优化数据的传输?因为数据是应用的核心,因这一块直接决定了用户体验的好与坏,而用户的本性是贪婪的。用户的需求随着自身满意度的不断膨胀,往往会导致这种丧心病狂的需求:“C/S下有这个效果(功能),B/S下为什么不可以?”。以前你可以笑一下,然后一副没事请挂机的表情,但随着HTML5标准的普及,妈的技术上真的可行了。HTML5提供了Canvas,WebGL,WebSocket,音视频等诸多功能,完全就是一套基于浏览器的操作系统API。这是一个很大的成就,所带来的冲击是巨大的,连Adobe也都全面拥抱HTML5了,每一个Web开发者也要跟上时代的脚步。

不管你用了HTML5的哪个功能,数据都是核心的问题,特别是大数据时代,更要我们用一个新的眼光来看待数据,而随着硬件的成熟,特别是HTML5功能的丰富,很多以前做不到的体验现在都可以了,这也直接导致了数据的需求变得原来越大。比如音视频,还是三维模型,上万条数据的传输,如果还用传统的json,xml这种形式,数据量稍大一些就难堪重任了,这问题无法回避。因此,怎么解决这种大数据的传输性能?答案很简单,向CS取经!

1. 创建和读写

传统CS下文件基本都是二进制格式,再加上zip压缩,短小精干,系统IO处理能力强,所以在数据量很大的情况下也可以胜任。最初在WebGL中也有类似的需求,JS和显卡之间大量实时的数据交换,而数据通信又必须是二进制的,JavaScript也需要这样一种有效访问二进制的方式,便产生的类型化数组。

ArrayBuffer本身就是一块内存,可供用户读写,使用方式也一样简单:

// 创建16个字节的内存
var buffer = new ArrayBuffer(16);
// 用32位的类型来绑定该内存区域,32位,每个变量是4个字节
var int32View = new Int32Array(buffer);
// 此时长度为4:4个int32类型,则4*4 = 16字节
for (var i=0; i<int32View.length; i++) {
int32View[i] = i; // 对每一个int32的变量赋值
}

可以看到用法都差不多,但可以让用户实现字节级别的处理能力。当然,new不是我们的重点,重点是如何在XMLHttpRequest请求中使用ArrayBuffer方式,和服务器进行二进制的传输方式。

var loadArrayBuffer = function(url, headers) {        return loadWithXhr({
url : url, // 告诉服务器,返回类型采用arraybuffer
responseType : 'arraybuffer',
headers : headers
});
};

OK,可想而知,相同信息下二进制则更为紧凑。下面是相同数据下大小对比,可以粗略的认为两者之间的大小比为四倍。

2. 数据解析

下面问题来了,二进制文件,看上去很压力?确实这是一个问题。《Unix编程艺术》里面会有这样一句话:“如果你想要创建一个新的二进制格式,那你应该睡一觉,第二天起来再好好想清楚是否有必要这样做。”这也是Web开发者不得不面对的问题,如果JSON已经无法满足你的需要,就要像C/S开发者一样对二进制了然于心,谁也没说二进制是C/S开发者的专属,走的路多了一点而已。当然,JS中也提供了读写ArrayBuffer的方式。

有下面两个方式,一个是DateView,一个是Type Array。

DateView API截图

Type Array具体类型

如图是两者风格上的不同,严格说,完全使用一种也能实现解析,不同处在于前者主要是提供了函数的形式,而后者主要是以变量的形式。个人经验是搭配使用效果更佳,一个是小家碧玉,一个是大家闺秀,各有各的好啊。一片连续的数据,比如VBO之类的就用TypeArray直接对应float类型,而对于多个属性变量组成的结构体,可以通过DataView有序解析。好吧,完全靠感觉,下面的代码,自己来找找感觉吧。

var pos = 0;var view = new DataView(buffer);
var minimumHeight = view.getFloat32(pos, true);
pos += Float32Array.BYTES_PER_ELEMENT;
var vertexCount = view.getUint32(pos, true);
pos += Uint32Array.BYTES_PER_ELEMENT;
var encodedVertexBuffer = new Uint16Array(buffer, pos, vertexCount * 3);

如上是一段实际应用中的代码,DataView封装buffer,然后提供了基本的函数getFloat32、getUint32来实现对其中变量的逐次读取。同时对于VertexBuffer这样的大块类型则用了Uint16Array直接获取。

可见,二进制的解析关键是对二进制格式的清晰,而觉得解析二进制复杂,主要还是得克服心理的作用。

这里有两处需要强调,第一就是提倡大家使用BYTES_PER_ELEMENT,每一个Type Array都会有这个属性来记录长度,万一以后该变量长度变化,而你代码写死了(可能性为0),你哭都来不及。可能强迫症吧,觉得这样好。另外就是要注意Uint16Array构造函数中的参数,其中pos是字节单位,而VertexCount的单位则是Uint16,两个字节,两者的单位是不同的,自己到底要移动多少自己,一定要谨慎处理。

不同数组类型操作运算符的性能对比

IE下读写操作对比

Chrome下读写操作对比

上面是在我笔记本下的性能对比图(create,read&wirte),ArrayBuffer的创建速度几乎是Array的四倍以上;读操作快了一倍;但Array的写操作简直是神速;另外不同的类型下Byte,INT的差别并不大;另外IE相比Chrome简直慢成鬼了。看来不同配置,不同浏览器差别还是非常大的。看来有能力还是看看JS引擎的实现,又有很多可以涨知识的地方了。

再说一下不太常用,但也是非常好的一种使用方式。IMG标签的形式,有些时候因为各种原因,会把二进制信息作为图片的像素存储,这样通过img标签来传输,方便快捷,而且有一定的加密性,对应的是Canvas的ImageData。但在客户端就需要一个IMG转为Type Array的一个过程,思路也不麻烦,通过ImageData来做中间过度:

//假设是服务端发送过来的img图片
var imgInfo = new Image;
// 将该图片绘制到canvas上context.drawImage(imgInfo,0,0,width,height);
// 获取该Canvas里面的像素
var imgData = context.getImageData(0,0,width,height);
// 其为uint8clampedArray
var typeArray = imgData.data

另外,二进制的问题其实还没有这么简单,还有字节大小端和字节对齐的问题。字节大小端的概念大家可以google查一下,不在此多言,DataView中提供了参数,默认是低字节排序。而字节对齐呢,则是Uint16Array中你所声明的长度必须是该类型字节长度的整数倍,比如Uint16是两个字节,则该长度要被2整除,否则浏览器会alert。

3. 数据处理:Web Worker

很有意思的一个地方是,JavaScript支持异步,但本质是单线程环境,以往我们都采用setTimeout的方法来模拟实时性。而对于CS的开发者而言,多线程是处理大数据的有效手段。举个例子,当数据量很大的时候,如何在数据处理的同时避免UI响应停滞,通常我们都是开辟一个工作者线程来处理数据,处理后的数据都放在共享池中,这时UI主线程直接使用数据,保证界面响应的顺畅,而JavaScript对此无能无力,即使采用Ajax也只能局部更新,只是“看上去有了响应,但总体时间还是不变,甚至会变慢”,HTML5中提供了Web Worker的多线程机制,则可以很好的解决这个问题。

为什么要提到Web Worker呢,因为往往数据解析后,则会进入数据处理的过程,比如解析后的数据构建三角网,或者对数据进行解压缩,解码等操作,如果放在主线程上处理总是不太完美的方案,这样自然就会想到使用工作者线程Web Worker来处理。而且目前Google Earth的WebGL版本也在用Worker来处理数据,而Baidu的3D地图还没有,深入研究会发现很多技术上有意思的区别。这块以后会有详细介绍,因为也和数据有关系,这里只是开个头涉及一下。

下面例子比较简单,但个人感觉真要实现功能还是有很多限制,设计上也有很多技巧,所以也不多说了,多线程还是得多做才能积累经验,给出下面这个简单的例子,让大家有一个简单的了解。

主脚本:

var worker = new Worker('doWork.js');

worker.addEventListener('message', function(e) {
console.log('Worker said: ', e.data);
}, false); worker.postMessage('Hello World'); // 把数据传给工作者线程.

doWork.js (Worker):

self.addEventListener('message', function(e) {
self.postMessage(e.data);
}, false);
4. 数据渲染

本来这个跟本节内容无关,但为了说明一个数据自始至终的过程,所以加进来吧。WebGL硬件加速,直接使用显卡批次渲染,是我知道的唯一的大数据渲染的一种方式,因为对其他大数据下高性能渲染还没研究,这里只提供WebGL一种思路。

5. 其他

1.异步

JS中数据一般都在服务器上,数据的传输也为异步,不同于CS多数情况下都在本地直接加载,这样在调度上的复杂性会加大,而浏览器TCP连接数也有限制,所以同时请求的数目应该有所控制,服务器网卡带宽也是一个瓶颈,通过跨域,多个IP来增大同时下载的数据量;这样,可能你还会采用zip压缩,提高浏览器缓存的复用度,要考虑的点很多,实践性也很强。所以在设计时也应有所考虑。封装一个合理的Primise模式会增加代码的可阅读性。

2.数据安全

JS代码虽然可以混淆,但是在客户端还是可以调试。换句话说,通过阅读你的JS源码还是能够获取你的数据格式的。而数据往往都是核心的,二进制的数据很多情况下并不想让用户知道里面的结构,但很遗憾,这在JS技术本身无法对数据保密,所以只能另辟蹊径。个人觉得有两个可能,一个是服务端的授权,Token的方式。另一个是在数据里面增加一些冗余信息,作为自己数据的一个特有标志,如果其他人盗用数据时,这些就是版权证据。比如地图厂商往往会在地图上加一些特有的不存在的位置点,如果其他厂商使用了,说明他们没有考察真实性而直接盗用数据,这就是一个证据。

.总结

HTML5有很多很好的特性,对Web开发也是一个极大的挑战,但单纯从技术上来说是充满诱惑的,而在大数据时代,其实这些都是很好的B/S上大数据的解决思路和基本技术。我对大数据并没有什么研究,以我现有的肤浅了解,我认为一个Web应用如果没有上面这些二进制,硬件加速方面的技术应用,都不能称为大数据技术,充其量最多不过一些策略上的优化,时间和空间,服务器和客户端之间的平衡,而不是在质的角度解决问题。


       这让我想到了上面这个图(盗用同学公司)。不要轻易的否定自己认为不可能的事。技术的革新总会填补某种不存在。

如下是最高自由落体跳伞世界纪录保持者(对,就是那位穿着宇航服跳伞,速度超过音速的Google高管)面对女儿提的一个问题,三次不同的答复:

It is impossible.

Even it is not impossible, it is very very hard.

Well, maybe it is not hard, it is just I do not know.

ArrayBuffer简析的更多相关文章

  1. 简析.NET Core 以及与 .NET Framework的关系

    简析.NET Core 以及与 .NET Framework的关系 一 .NET 的 Framework 们 二 .NET Core的到来 1. Runtime 2. Unified BCL 3. W ...

  2. 简析 .NET Core 构成体系

    简析 .NET Core 构成体系 Roslyn 编译器 RyuJIT 编译器 CoreCLR & CoreRT CoreFX(.NET Core Libraries) .NET Core 代 ...

  3. RecycleView + CardView 控件简析

    今天使用了V7包加入的RecycleView 和 CardView,写篇简析. 先上效果图: 原理图: 这是RecycleView的工作原理: 1.LayoutManager用来处理RecycleVi ...

  4. Java Android 注解(Annotation) 及几个常用开源项目注解原理简析

    不少开源库(ButterKnife.Retrofit.ActiveAndroid等等)都用到了注解的方式来简化代码提高开发效率. 本文简单介绍下 Annotation 示例.概念及作用.分类.自定义. ...

  5. PHP的错误报错级别设置原理简析

    原理简析 摘录php.ini文件的默认配置(php5.4): ; Common Values: ; E_ALL (Show all errors, warnings and notices inclu ...

  6. Android 启动过程简析

    首先我们先来看android构架图: android系统是构建在linux系统上面的. 所以android设备启动经历3个过程. Boot Loader,Linux Kernel & Andr ...

  7. Android RecycleView + CardView 控件简析

    今天使用了V7包加入的RecycleView 和 CardView,写篇简析. 先上效果图: 原理图: 这是RecycleView的工作原理: 1.LayoutManager用来处理RecycleVi ...

  8. Java Annotation 及几个常用开源项目注解原理简析

    PDF 版: Java Annotation.pdf, PPT 版:Java Annotation.pptx, Keynote 版:Java Annotation.key 一.Annotation 示 ...

  9. 【ACM/ICPC2013】POJ基础图论题简析(一)

    前言:昨天contest4的惨败经历让我懂得要想在ACM领域拿到好成绩,必须要真正的下苦功夫,不能再浪了!暑假还有一半,还有时间!今天找了POJ的分类题库,做了简单题目类型中的图论专题,还剩下二分图和 ...

随机推荐

  1. 一张图片说明MII

  2. Java的UUID

    UUID含义是通用唯一识别码 (Universally Unique Identifier),这 是一个软件建构的标准,也是被开源软件基金会 (Open Software Foundation, OS ...

  3. velocity导出word报错解决

  4. Android横竖屏切换

    ps:虽然现在的app一般都是固定一个屏幕方向,但是还是有必要了解下屏幕切换的方法和注意. 一 固定横竖屏 androidmainfest.xml中设置activoty属性:android:scree ...

  5. GnuPG 1.4.15 发布,邮件加密工具

    GnuPG 1.4.15 改进包括: * Fixed possible infinite recursion in the compressed packet parser. [CVE-2013-44 ...

  6. mono for android学习过程系列教程(7)

    首先说个抱歉,这段时候由于个人原因未及时更新博客.故此今天先给大家来个小料. 以后会及时更新我自己的学习过程. 不多说,终于私人生活安定下来了,可以安心学习.直接进入主体: 今天咱们摒弃所有的前面的知 ...

  7. 批量修改vss工作目录

    vss作为源代码版本控制工具,可以针对不同的文件夹设置不同的本地工作目录,这样可以方便我们不同的个性化需求.但是往往实际情况是,我们设置了不同的工作目录,后来却发现导致引用混乱,每个人每次获取项目文件 ...

  8. Javascript事件模型系列(一)事件及事件的三种模型

    一.开篇 在学习javascript之初,就在网上看过不少介绍javascript事件的文章,毕竟是js基础中的基础,文章零零散散有不少,但遗憾的是没有看到比较全面的系列文章.犹记得去年这个时候,参加 ...

  9. Linux 比较判断运算(if test)

    200 ? "200px" : this.width)!important;} --> 介绍 本篇文章主要是列举在shell命令中常出现的一些用来做比较的运算符,这些运算符是 ...

  10. Spring-Context之七:使用p-namesapce和c-namespace简化bean的定义

    在Spring中定义bean的方式多种多样,即使使用xml的方式来配置也能派生出很多不同的方式. 比如如下的bean定义: 1 2 3 4 5 6 7 8 9 10 11 12 <beans x ...