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

时间:2012-04-12 00:12:03

标签: javascript html5 local-storage web-storage

  

*“高效”这里基本上意味着更小的尺寸(减少IO等待时间),以及快速的检索/反序列化时间。存储时间并不重要。

我必须在浏览器的localStorage中存储几十个整数数组,每个数组的数值范围为0-50,包括1800个值 - 也就是字符串。

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

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

  • 对数字进行零填充,使每个长度为2个字符,例如:

    [5, 27, 7, 38] ==> "05270738"
    
  • base 50编码:

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

    [5, 11, 7, 38] ==> "%+'F" (String.fromCharCode(37), String.fromCharCode(43) ...)
    

以下是我的结果:

                  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建议对数据使用压缩。 Combining this LZW implementation使用基数50和charCode数据。我还测试了aroth的代码(将4个整数打包成3个字节)。我得到了这些结果:

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

3 个答案:

答案 0 :(得分:4)

如果您的范围是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));
};

以下是一个简单示例:http://jsfiddle.net/NWyBx/1

请注意,通过对结果字符串应用gzip压缩,可以进一步降低存储大小。

或者,如果您的数字排序不重要,那么您可以使用51个桶进行桶式排序(假设0-50包括0和50作为有效数字)并存储每个桶的计数而不是数字本身。与其他任何方法相比,这可能会给你更好的压缩和效率。

答案 1 :(得分:0)

假设(在你的测试中)压缩花费的时间比减小尺寸所花费的时间多,你的char编码是没有位移的最小值。您当前正在为每个数字使用一个字节,但如果它们保证足够小,则可以在每个字节中放置两个数字。这可能是一个过度优化,除非这是你的代码中非常热门的部分。

答案 2 :(得分:0)

您可能需要考虑使用Uint8ArrayArrayBufferThis blogpost显示了它是如何完成的。复制他的逻辑,这是一个例子,假设你有一个名为Uint8Array的现有arr

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
});