Base64 转 ArrayBuffer 效率小记

主题描述

以下三个函数功能相同,都是把 base64 转换为 ArrayBuffer
测试看看哪个效能比较好

/** * 把 base64 转换为 ArrayBuffer *  * @param {string} b64 要转成 ArrayBuffer 的 base64 * @returns {ArrayBuffer} */function f0(b64) {    return (Uint8Array.from(window.atob(b64), x => x.codePointAt(0))).buffer;}/** * 功能同 f0 * 尝试以下方向改善效能: * 1. 预先计算 ArrayBuffer 大小 * 2. 用 String.prototype.codePointAt 来减少字串的处理 */function f1(b64) {    let str = window.atob(b64);    let arr = new Uint8Array(str.length);    for(let i=arr.length;--i>=0;) {        arr[i] = str.codePointAt(i);    }    return arr.buffer;}/** * 功能同 f1 * 不使用内建的 window.atob 而是自己处理转换 */function f2(b64) {    let m = (() => {        let arr = [];        for (let i = 25; i >= 0; --i) {            arr[97 + i] = 26 + i;            arr[65 + i] = i;        }        for (let i = 9; i >= 0; --i) {            arr[48 + i] = 52 + i;        }        arr[43] = 62;        arr[47] = 63;        arr[61] = 0;        return arr;    })();    let n = b64.length, t, k = -1, i;    if (n < 4) {        return null;    }    let emptyBtyes = b64.codePointAt(n - 2) === 61 ? 2 :        (b64.codePointAt(n - 1) === 61 ? 1 : 0);    let arr = new Uint8Array((n >>> 2) * 3 - emptyBtyes);    n -= 4;    for (i = 3; i < n; i += 4) {        t = m[b64.codePointAt(i - 3)] << 18 |            m[b64.codePointAt(i - 2)] << 12 |            m[b64.codePointAt(i - 1)] << 6 |            m[b64.codePointAt(i)];        arr[++k] = (t >>> 16 & 255);        arr[++k] = (t >>> 8 & 255);        arr[++k] = (t & 255);    }    n += 4;    for (; i < n; i += 4) {        t = m[b64.codePointAt(i - 3)] << 18 |            m[b64.codePointAt(i - 2)] << 12 |            m[b64.codePointAt(i - 1)] << 6 |            m[b64.codePointAt(i)];        let len = arr.byteLength;        if (++k < len) {            arr[k] = t >>> 16 & 255;        }        if (++k < len) {            arr[k] = t >>> 8 & 255;        }        if (++k < len) {            arr[k] = t & 255;        }    }    return arr.buffer;}

测试方法

测试的程式码如下

/** * 比较两个 ArrayBuffer 内容是否相同 *  * @param {ArrayBuffer} buf1  * @param {ArrayBuffer} buf2  * @returns {bool} */function compareBuffer(buf1, buf2) {    let arr1 = new Uint8Array(buf1);    let arr2 = new Uint8Array(buf2);    if (arr1.length !== arr2.length) {        return false;    }    for (let i = arr1.length - 1; i >= 0; --i) {        if (arr1[i] !== arr2[i]) {            return false;        }    }    return true;}/** * 产生指定大小的 ArrayBuffer * 其内容用乱数填充 *  * @param {int} len 要几 bytes * @returns {ArrayBuffer} */function genRandArray(len) {    let arr = new Uint8Array(len);    for (let i = 0; i < len; ++i) {        arr[i] = Math.random() * 256 | 0;    }    return arr.buffer;}/** * 把 ArrayBuffer 转为 Base64 * (这边先不考虑效能) *  * @param {ArrayBuffer} buffer  * @returns {String} Base64 */function getBase64(buffer) {    let arr = new Uint8Array(buffer);    let len = arr.byteLength;    let str = '';    for (let i = 0; i < len; ++i) {        str += String.fromCodePoint(arr[i]);    }    return window.btoa(str);}//30 ~ 40M 大小let origBuffer = genRandArray(30000000 + Math.random() * 10000000 | 0);let origBase64 = getBase64(origBuffer);let t0 = Date.now();let buf0 = f0(origBase64);let t1 = Date.now();let buf1 = f1(origBase64);let t2 = Date.now();let buf2 = f2(origBase64);let t3 = Date.now();console.log(`f0: ${t1 - t0} ms ... ${compareBuffer(origBuffer, buf0)?'pass':'error'}`);console.log(`f1: ${t2 - t1} ms ... ${compareBuffer(origBuffer, buf1)?'pass':'error'}`);console.log(`f2: ${t3 - t2} ms ... ${compareBuffer(origBuffer, buf2)?'pass':'error'}`);

测试结果

以下是某一次测试的结果(参考)

浏览器f0f1f2firefox1888 ms479 ms613 mschrome3700 ms583 ms222 ms

结论

一般状况其实不会有转换大量资料的状况万一遇到大资料,避免字串分割有关的操作可以提升效能

关于作者: 网站小编

码农网专注IT技术教程资源分享平台,学习资源下载网站,58码农网包含计算机技术、网站程序源码下载、编程技术论坛、互联网资源下载等产品服务,提供原创、优质、完整内容的专业码农交流分享平台。

热门文章