首页 文章

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

提问于
浏览
13

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

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

显然,最简单的方法是,只是考虑到数据的范围是众所周知的,它会增加许多不必要的信息 . 这些数组之一的平均大小为~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

我的问题是,如果有一个更好的方法,我还没有考虑过?

Update
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 回答

  • 3

    您可能需要考虑使用 Uint8ArrayArrayBuffer . This blogpost显示了它如何's done. Copying his logic, here'是一个例子,假设你有一个名为 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

    如果您的范围是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作为有效数字)并存储每个桶的计数而不是数字本身 . 这可能会比任何其他方法更好地提供压缩和效率 .

  • 0

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

相关问题