使用缓冲区是否比使用流更快?

4

我尝试了几个Imagemagick的包裹库和一些S3库。由于性能差异很大,我很难选择最佳概念。

我已经选定了node库“gm”,这是一个非常好用并且文档非常齐全的库。

至于S3,我尝试过亚马逊自己的AWS库以及“S3-Streams”。


编辑:我刚发现AWS库可以处理流。我想这是一个新的功能s3.upload(或者我只是错过了它?)。不管怎样,我放弃了s3-streams,因为它使用了更加复杂的s3uploadPart。在切换库之后,在我的测试用例中,流式传输等同于上传缓冲区。


我的测试用例是将一个2MB的jpg文件分成大约30个512px瓷砖,并将每个瓷砖发送到S3。Imagemagick有一种非常快速的自动方式通过裁剪命令生成瓦片。不幸的是,我没有找到任何可以捕获自动生成的瓦片的多文件输出的node库。相反,我必须通过单独调用每个瓦片的裁剪命令来循环生成瓦片。

我将在详细介绍之前提供总时间:

A:85秒(s3-streams)

A:34秒(aws.s3.upload) (编辑)

B:35秒(缓冲区)

C:25秒(并行缓冲区)

显然,在这种情况下,缓冲区比流更快。我不知道gms3-streams是否对流进行了糟糕的实现,或者我是否应该调整一些东西。现在我将使用B解决方案。C更快,但会占用更多内存。

我在低端Digital Ocean Ubuntu机器上运行此代码。这是我尝试过的内容:

A. 生成瓦片并逐个传输它们

  • 我准备了一个包含每个要生成瓷砖的裁剪信息和s3Key的数组

  • 该数组循环使用“async.eachLimit(1)”。我没有成功地一次生成超过一个瓷砖,因此限制(1)。

  • 随着瓦片的生成,它们直接流式传输到S3

伪代码:

async.eachLimit(tiles, 1, function(tile, callback) {
    gm(originalFileBuffer)
    .crop(tile.width, tile.height, tile.x, tile.y)
    .stream()
    .pipe(s3Stream({Key: tile.key, Bucket: tile.bucket}))  //using "s3-streams" package
    .on('finish', callback)
});

B. 将图块生成到缓冲区,并使用AWS包直接上传每个缓冲区

  • 当图块生成到缓冲区时,它们会直接上传到S3。

伪代码:

async.eachLimit(tiles, 1, function(tile, callback) {
    gm(originalFileBuffer)
    .crop(tile.width, tile.height, tile.x, tile.y)
    .toBuffer(function(err, buffer) {
      s3.upload(..
        callback()
      )        
    })
});

C. 与B相同,但将所有缓冲区存储在瓦片数组中以供后续并行上传

伪代码:

async.eachLimit(tiles, 1, function(tile, callback) {
    gm(originalFileBuffer)
    .crop(tile.width, tile.height, tile.x, tile.y)
    .toBufer(function(err, buffer) {
      tile.buffer = buffer;
      callback()
    })
});

在完成第一个each循环之后,进行下一步操作。我发现将限制推到超过10并没有增加速度。

async.eachLimit(tiles, 10, function(tile, callback) {
  s3.upload(tile.buffer..
    callback()
  )        
});

编辑:根据马克的要求提供一些背景信息。我最初没有提供详细信息,希望我可以清楚地了解缓冲区与流的差异。
目标是通过node/Express API以反应灵敏的方式为我们的应用程序提供图片服务。后端db是Postgres。大容量储存是S3。
传入的文件主要是照片、平面图和pdf文档。这些照片需要以多种尺寸存储,以便我可以以响应式方式向应用程序提供它们:缩略图、低分辨率、中分辨率和原始分辨率。
平面图必须是平铺的,以便我可以在应用程序中逐步加载(滚动平铺)。一个完整分辨率的A1图可以达到50 MPixels。
上传到S2的文件大小从50KB(平铺)到10MB(平面图)不等。
这些文件来自不同的方向,但总是以流的形式:
  • 通过Web或其他API(SendGrid)的表单邮件
  • 应用程序中的上传
  • 上传的文件需要更多处理时从S3下载的流
我不喜欢将文件临时存储在本地磁盘上,因此只使用缓冲区与流。如果我可以使用磁盘,我将使用IM自己的瓦片功能进行非常快速的平铺。
为什么不用本地磁盘呢?
  • 上传到S3之前会对图像进行加密。我不希望未经加密的文件在临时目录中逗留。
  • 总是有清理临时文件的问题,可能会在意外崩溃后留下孤儿文件等问题。

你有点直接深入细节,没有给出任何关于你想要实现什么的概述... 你为什么要发送图片?来自哪里?它们会回来吗?它们的尺寸是多少?它们是JPEG还是PNG或其他格式?你是想尽可能快地将许多图像发送到AWS/S3吗?还是并行处理一些东西?或者重新组装一些东西?为什么必须将它们分成瓦片? - Mark Setchell
我已在原始帖子中添加了一些背景。感谢您的关注。 - Michael
作为一个旁注,我可以在OSX下并行运行gm.crop()(每个限制10),但是同样的代码在Ubuntu上会崩溃。在OSX上,当我并行运行时,它只需要6秒,而不是25秒。 - Michael
1个回答

2

经过一番尝试,我觉得有必要回答一下自己的问题。

最初我使用npm包s3-streams来进行向S3的流式传输。这个包使用了aws.s3.uploadPart。

现在我发现aws包有一个很棒的函数aws.s3.upload,可以接受缓冲区或流作为参数。

切换到AWS自带的流式传输函数后,缓冲区/流上传之间没有时间差异。

也许我没有正确地使用s3-streams。但我还发现了这个库可能存在的一个bug(涉及文件 > 10MB)。我提交了一个问题,但没有得到任何答复。我猜测这个库已经被放弃了,因为出现了s3.upload函数。

所以,我的问题的答案是:

缓冲区/流之间可能存在差异,但在我的测试用例中它们是相等的,这使得这个问题暂时不存在。

以下是每个循环中新的“保存”部分:

    let fileStream = gm(originalFileBuffer)
      .crop(tile.width, tile.height, tile.x, tile.y)
      .stream();
    let params = {Bucket: 'myBucket', Key: tile.s3Key, Body: fileStream};
    let s3options = {partSize: 10 * 1024 * 1024, queueSize: 1};

    s3.upload(params, s3options, function(err, data) {
      console.log(err, data);
      callback()
    });

感谢您的阅读。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接