压缩效率

时间:2016-12-13 08:48:04

标签: javascript performance optimization gzip

有一项任务是将数据保存在某些本地存储中或用于网络传输。数据以普通对象形式(键值对)存储。 为了节省带宽和存储空间,我计划使用从冗长的详细密钥名称到相应的数字(使用地图)的转换,并在接收数据时恢复它们。例如:

var statesMap = {
    SEARCH: 0,
    SORT: 1,
    FILTER: 2,
    DISPLAY_FAV: 3,
    PANEL_POS: 4,
    MENU_LIST_POS: 5,
    MAIN_LIST_POS: 6,
    INFO_LIST_POS: 7,
    CHANNEL: 8
};

config = {
        APP: ['SEARCH', 'SORT', 'DISPLAY_FAV', 'PANEL_POS', 'MENU_LIST_POS', 'MAIN_LIST_POS', 'CHANNEL']
    };

app.state = {};

// restore state from {"0":"ui","1":"NAME","3":false,"4":0,"5":3,"6":0}
config[appName.toUpperCase()].forEach(function ( state ) {
    var value = storage[statesMap[state]]; // 'storage' stores my data
    // convert "compressed" properties to full equivalents
    app.state[state] = value != null ? value : '';
});

// result {"SEARCH":"ui","SORT":"NAME","DISPLAY_FAV":false,"PANEL_POS":0,"MENU_LIST_POS":3,"MAIN_LIST_POS":0,"CHANNEL":""}

// store data
var tmp = {};

// filter fields with empty values, don't store them
Object.keys(app.state).filter(function ( key ) { return !!String(app.state[key]); }).forEach(function ( state ) {
    tmp[statesMap[state]] = app.state[state];
});

storage = tmp;

这种方法有足够的好处和优势吗?有更好的优化吗?这种优化是否会干扰gzip压缩算法?

非常感谢。

2 个答案:

答案 0 :(得分:2)

您所指的优化可能被称为"令牌替换"或类似的东西,是一种合理的域特定压缩方法。

这种类型的转换并不能防止像gzip这样的基于匹配+熵的算法工作,因此在应用此转换后,您不可能获得更大的最终大小。也就是说,你正在进行的替换完全 gzip擅长的事情类型,所以在调用gzip之前自己做这件事可能有点多余。

要确定,您可以简单地测试!令牌替换+ gzip的典型结果是什么,而不仅仅是gzip?

即使没有测试,以下是基于gzip的令牌替换方法的一些优点和缺点:

优点

  1. 有时你可以比gzip更有效地进行令牌替换,如果你在输出生成的早期就做,并且可以在你的大部分处理链中使用这种更紧凑的形式。您可能会在代码的其他部分获得加速,因为字符串比较之类的东西现在被非常快速的整数比较所取代(但是,请参见缺点#1)。
  2. gzip有一定的局限性,部分取决于它的年龄和大量目标硬件,您可以使用令牌替换。例如,它只能在32 KiB窗口中找到匹配项,而您的令牌替换项适用于整个格式。
  3. 令牌替换有效地利用了您对格式的了解,比gzip更有效地进行编码。例如,尽管gzip对于经常出现的密钥执行的替换大致相同,但是对于不常发生的密钥,您的替换将会很好(但是如果有很多密钥,这并不重要)。实际上,您可以使用预先分发的带外字典,这有助于压缩。
  4. 缺点

    1. Gzip通常使用您的操作系统或运行时环境提供的本机库高效实现。特别是因为你正在调用gzip,使用gzip 只是可能比在语言级别更换令牌更快。更快(或更慢)多少取决于您的实施。
    2. 压缩智能gzip在很大程度上执行相同的替换操作(加上更多),因此自己替换令牌有点多余。也就是说,gzip会查找"匹配":文本中较早出现的字符串,并将其替换为令牌,就像您一样。此外,它还有许多其他好东西,比如对令牌进行熵编码,因此无论如何都要将它作为最后一步。
    3. 添加自己的预压缩步骤可能不会损害压缩(即使它没有多大帮助),但它增加了复杂性,错误来源,可能性能问题等等上。你需要以某种方式传输密钥< - >整数映射到远程客户端等等。
    4. 基本上,我建议反对它,除非您的测试表明它提供了显着的性能提升。通常它不会,因为gzip已经删除了大部分冗余,但这取决于你的具体情况。

答案 1 :(得分:0)

以下是我对你问题的看法。

如果您使用自己的想法,请不要使用gzip或在数据长于 x 字节时使用它。

最好是不要使用它。

这就是为什么我说不要使用你的方法:

  • 可读性比保存30个字节更重要
  • 您在服务器和客户端代码上增加了复杂性
  • 很难调试,因为没有什么是明确的了
  • 可扩展性极其困难且容易出错。

使用您的方法具有以下优点:

  • 如果您在没有gzip支持的情况下从浏览器访问数据,这仍然可以节省带宽
  • 由于您了解格式,因此无需gzip即可实现更好的压缩

但是,你必须考虑这个:

  • Gzip(以及所有压缩格式)可以更好地处理更大和重复的数据。这就是他们闪耀的地方。如果您自己删除冗余并发送一个小输出,则会浪费处理能力以仅增加输出大小。这种没有减少的输出是不可能压缩的。
  • 使用您的方法会显着降低可读性并消除所有灵活性。它会强制您将所有必需的数据放在开头,将可选的数据放在最后。它不适合您可能拥有的每个测试用例,并会强制您增加逻辑代码来处理它。

一个例子是,如果将来要添加您不会总是返回的调试信息或字段。这是一个非常基本的例子:

  • 如果操作成功:{"error":false,[... data goes here ...]}
  • 如果某些"软错误"发生(例如:验证失败):{"error":true,"type":"..."}
  • 如果发生了令人讨厌的错误:{"error":500,"desc":"Service unavaliable"}

使用您的代码,第二个字段可以是"desc""type"或任何其他字段。

您可以说:"我们可以添加desctype字段并始终发送它们!"。现在,您正在添加更多数据,从而无法使用您的方法。

备份我的声明

如果没有一些数据我会得到什么样的答案会备份大胆的陈述,例如"无法压缩"?

让我们考虑您的示例数据:

{"SEARCH":"ui","SORT":"NAME","DISPLAY_FAV":false,"PANEL_POS":0,"MENU_LIST_POS":3,"MAIN_LIST_POS":0,"CHANNEL":""}

你(非最佳)减少到这个:

{"0":"ui","1":"NAME","3":false,"4":0,"5":3,"6":0}

所有压缩值都将使用此基本PHP代码:

$output = '<content>';

echo 'gzip:', strlen(gzencode($output)), ' original:', strlen($output);

其中显示了gzip压缩的大小和原始大小。我将保持代码尽可能简单,以避免疏远非PHP开发人员。

以下是我从不同执行中获得的结果:

  • {"SEARCH":"ui","SORT":"NAME","DISPLAY_FAV":false,"PANEL_POS":0,"MENU_LIST_POS":3,"MAIN_LIST_POS":0,"CHANNEL":""}
    结果:gzip:112 original:112(+0字节)
  • {"0":"ui","1":"NAME","3":false,"4":0,"5":3,"6":0}
    结果:gzip:65 original:49 +16字节超过原始版本)
  • ["ui","NAME",false,0,3,0](适当的JSON输出,基于您的对象)
    结果:gzip:45 original:25 +20字节超过原始版本)

您可以尝试http://sandbox.onlinephpfunctions.com/code/d8d0799147e3256ede2b730cb1ded7cf66c1eb67

上的代码

输出是从问题中的评论中获得的,而不是由我编写的。除了最后一个,它基于第二个输出。第二个输出显示一个数组表示为一个对象(顺序数字键)。

好的,我说了很多话,显示了一些代码,但没有。我的结论?

对于您的情况,请使用gzip或使用您的方法。我会坚持简单明了的gzip,并称之为一天。

你不会不必要地增加你的输出尺寸。

即便如此,如果你真的想要你的方法,请在你的服务器上禁用gzip并保存CPU cicles。

相关问题