使用Javascript在localStorage中存储大型整数数组的最有效方法

使用Javascript在localStorage中存储大型整数数组的最有效方法,javascript,html,local-storage,web-storage,Javascript,Html,Local Storage,Web Storage,*这里的“高效”基本上是指更小的大小(减少IO等待时间)和更快的检索/反序列化时间。储存时间并不重要 我必须在浏览器的本地存储中存储几十个整数数组,每个数组有1800个0-50范围内的值——也就是说,作为字符串 显然,最简单的方法是只JSON.stringifyit,但是,考虑到数据的范围是众所周知的,这会添加很多不必要的信息。其中一个阵列的平均大小约为5500字节 以下是我尝试过的一些其他方法(结果大小,以及最后反序列化1000次的时间) 零填充数字,使每个数字都有2个字符长,例如: [5,

*这里的“高效”基本上是指更小的大小(减少IO等待时间)和更快的检索/反序列化时间。储存时间并不重要

我必须在浏览器的本地存储中存储几十个整数数组,每个数组有1800个0-50范围内的值——也就是说,作为字符串

显然,最简单的方法是只
JSON.stringify
it,但是,考虑到数据的范围是众所周知的,这会添加很多不必要的信息。其中一个阵列的平均大小约为5500字节

以下是我尝试过的一些其他方法(结果大小,以及最后反序列化1000次的时间)

  • 零填充数字,使每个数字都有2个字符长,例如:

    [5, 27, 7, 38] ==> "05270738"
    
  • 以50为基数对其进行编码:

    [5, 11, 7, 38] ==> "5b7C"
    
  • 仅将该值用作字符代码(添加32以避免开始时出现奇怪的控制字符):

以下是我的结果:

                  size     Chrome 18   Firefox 11
-------------------------------------------------
JSON.stringify    5286          60ms         99ms
zero-padded       3600         354ms        703ms
base 50           1800         315ms        400ms
charCodes         1800          21ms        178ms
我的问题是,是否有更好的方法我还没有考虑

更新
MΓΓББLL建议对数据进行压缩。使用基数50和字符码数据。我还测试了aroth的代码(将4个整数打包成3个字节)。我得到了这些结果:

                  size     Chrome 18   Firefox 11
-------------------------------------------------
LZW base 50       1103         494ms        999ms
LZW charCodes     1103         194ms        882ms
bitpacking        1350        2395ms        331ms

假设(在您的测试中)压缩所花费的时间比减少大小所节省的时间要多,您的字符编码是在不进行位移位的情况下得到的最小编码。当前每个数字使用一个字节,但如果保证它们足够小,则可以在每个字节中放入两个数字。这可能是一个过度优化,除非这是一个非常热门的代码。

你可能想考虑使用<代码> UTI8LARDAs/CODE >或<代码> ArrayBuffer < /代码>。展示了它是如何完成的。复制他的逻辑,这里是一个示例,假设您有一个名为
arr
的现有
Uint8Array

function arrayBufferToBinaryString(buffer, cb) {
    var blobBuilder = new BlobBuilder();
    blobBuilder.append(buffer);
    var blob = blobBuilder.getBlob();
    var reader = new FileReader();
    reader.onload = function (e) {
        cb(reader.result);
    };
    reader.readAsBinaryString(blob);
}
arrayBufferToBinaryString(arr.buffer, function(s) { 
  // do something with s
});

如果范围为0-50,则可以将4个数字打包为3个字节(每个数字6位)。这将允许您使用~1350字节存储1800个数字。此代码应执行以下操作:

window._firstChar = 48;

window.decodeArray = function(encodedText) {
    var result = [];
    var temp = [];

    for (var index = 0; index < encodedText.length; index += 3) {
        //skipping bounds checking because the encoded text is assumed to be valid
        var firstChar = encodedText.charAt(index).charCodeAt() - _firstChar;
        var secondChar = encodedText.charAt(index + 1).charCodeAt() - _firstChar;
        var thirdChar = encodedText.charAt(index + 2).charCodeAt() - _firstChar;

        temp.push((firstChar >> 2) & 0x3F);    //6 bits, 'a'
        temp.push(((firstChar & 0x03) << 4) | ((secondChar >> 4) & 0xF));  //2 bits + 4 bits, 'b'
        temp.push(((secondChar & 0x0F) << 2) | ((thirdChar >> 6) & 0x3));  //4 bits + 2 bits, 'c'
        temp.push(thirdChar & 0x3F);  //6 bits, 'd'

    }

    //filter out 'padding' numbers, if present; this is an extremely inefficient way to do it
    for (var index = 0; index < temp.length; index++) {
        if(temp[index] != 63) {
            result.push(temp[index]);
        }            
    }

    return result;
};

window.encodeArray = function(array) {
    var encodedData = [];

    for (var index = 0; index < dataSet.length; index += 4) {
        var num1 = dataSet[index];
        var num2 = index + 1 < dataSet.length ? dataSet[index + 1] : 63;
        var num3 = index + 2 < dataSet.length ? dataSet[index + 2] : 63;
        var num4 = index + 3 < dataSet.length ? dataSet[index + 3] : 63;

        encodeSet(num1, num2, num3, num4, encodedData);
    }

    return encodedData;
};

window.encodeSet = function(a, b, c, d, outArray) {
    //we can encode 4 numbers in 3 bytes
    var firstChar = ((a & 0x3F) << 2) | ((b >> 4) & 0x03);   //6 bits for 'a', 2 from 'b'
    var secondChar = ((b & 0x0F) << 4) | ((c >> 2) & 0x0F);  //remaining 4 bits from 'b', 4 from 'c'
    var thirdChar = ((c & 0x03) << 6) | (d & 0x3F);          //remaining 2 bits from 'c', 6 bits for 'd'

    //add _firstChar so that all values map to a printable character
    outArray.push(String.fromCharCode(firstChar + _firstChar));
    outArray.push(String.fromCharCode(secondChar + _firstChar));
    outArray.push(String.fromCharCode(thirdChar + _firstChar));
};
窗口。_firstChar=48;
window.decodeArray=函数(encodedText){
var结果=[];
var-temp=[];
对于(变量索引=0;索引>2)&0x3F);//6位,“a”
临时推送(((firstChar&0x03)>4)和0xF));//2位+4位,“b”
临时推送((secondChar&0x0F)>6)&0x3));//4位+2位,“c”
临时推送(thirdChar&0x3F);//6位'd'
}
//过滤掉“填充”数字(如果存在);这是一种效率极低的方法
对于(var索引=0;索引4)&0x03);//6位表示“a”,2位表示“b”
var secondChar=((b&0x0F)>2)&0x0F);//来自“b”的剩余4位,来自“c”的剩余4位

变量thirdChar=((c&0x03)尝试将base 50或charCodes方法与deflate/gzip相结合。我不确定是否有足够的数据用于压缩,以满足开销的需要,但检查一下也无妨。@mö最多两个字节(每个字节)在计算数据大小时,一定要考虑到这一点。0-50之间的数字占6位。你最好希望每个字节有1.33个数字。不过,正如前面所述,你可以很容易地将两个数字打包到一个UTF-16代码点中。我昨晚睡觉后也有同样的想法,但谢天谢地,你帮我省去了实际使用时的头疼计算实现。我已经在OP中的表格中添加了基准测试结果。不幸的是,Javascript的位操作似乎没有得到很好的优化。而且,顺序很重要,所以Bucket的想法是不可行的。更新:Chrome的位操作没有得到优化。Firefox似乎做得很好(至少是比较而言)。有两件事:首先,在
U+0100
之前,您不会在一行中获得256个可打印字符。从
U+0080
U+00ff
的范围是控制字符。因此,偏移48不会在编码字符串中只提供可打印字符。请看-警报显示“12字节”,但我只计算11个字符。其次,JavaScript字符串存储为16位整数的数组。您必须将8个数字打包为6个字节(3个字符)。除非您仅使用一半的位,否则您将无法使用可打印字符构造编码字符串。
window._firstChar = 48;

window.decodeArray = function(encodedText) {
    var result = [];
    var temp = [];

    for (var index = 0; index < encodedText.length; index += 3) {
        //skipping bounds checking because the encoded text is assumed to be valid
        var firstChar = encodedText.charAt(index).charCodeAt() - _firstChar;
        var secondChar = encodedText.charAt(index + 1).charCodeAt() - _firstChar;
        var thirdChar = encodedText.charAt(index + 2).charCodeAt() - _firstChar;

        temp.push((firstChar >> 2) & 0x3F);    //6 bits, 'a'
        temp.push(((firstChar & 0x03) << 4) | ((secondChar >> 4) & 0xF));  //2 bits + 4 bits, 'b'
        temp.push(((secondChar & 0x0F) << 2) | ((thirdChar >> 6) & 0x3));  //4 bits + 2 bits, 'c'
        temp.push(thirdChar & 0x3F);  //6 bits, 'd'

    }

    //filter out 'padding' numbers, if present; this is an extremely inefficient way to do it
    for (var index = 0; index < temp.length; index++) {
        if(temp[index] != 63) {
            result.push(temp[index]);
        }            
    }

    return result;
};

window.encodeArray = function(array) {
    var encodedData = [];

    for (var index = 0; index < dataSet.length; index += 4) {
        var num1 = dataSet[index];
        var num2 = index + 1 < dataSet.length ? dataSet[index + 1] : 63;
        var num3 = index + 2 < dataSet.length ? dataSet[index + 2] : 63;
        var num4 = index + 3 < dataSet.length ? dataSet[index + 3] : 63;

        encodeSet(num1, num2, num3, num4, encodedData);
    }

    return encodedData;
};

window.encodeSet = function(a, b, c, d, outArray) {
    //we can encode 4 numbers in 3 bytes
    var firstChar = ((a & 0x3F) << 2) | ((b >> 4) & 0x03);   //6 bits for 'a', 2 from 'b'
    var secondChar = ((b & 0x0F) << 4) | ((c >> 2) & 0x0F);  //remaining 4 bits from 'b', 4 from 'c'
    var thirdChar = ((c & 0x03) << 6) | (d & 0x3F);          //remaining 2 bits from 'c', 6 bits for 'd'

    //add _firstChar so that all values map to a printable character
    outArray.push(String.fromCharCode(firstChar + _firstChar));
    outArray.push(String.fromCharCode(secondChar + _firstChar));
    outArray.push(String.fromCharCode(thirdChar + _firstChar));
};