使用缓冲区通常比使用流更快吗?
Are buffers generally faster to work with than streams?
我尝试了几个 Imagemagick 包装器库和一些 S3 库。由于性能差异很大,我很难选择最佳概念。
我已经选择了节点库 "gm",使用起来很愉快,而且有据可查。
至于 S3 我已经尝试了亚马逊自己的 AWS 库以及 "S3-Streams"
编辑: 我刚刚发现 AWS 库可以处理流。我想这是一个新函数 s3.upload(或者我只是错过了它?)。无论如何,我放弃了 s3-streams,它使用了更复杂的 s3uploadPart。 在我的测试用例中切换库流等于上传缓冲区。
我的测试用例是将一个 2MB 的 jpg 文件拆分成大约 30 个 512 像素的图块,并将每个图块发送到 S3。 Imagemagick 有一种非常快速的自动方式,可以通过裁剪命令生成切片。不幸的是,我还没有找到任何可以从自动生成的图块中捕获多文件输出的节点库。相反,我必须通过为每个图块单独调用裁剪命令来循环生成图块。
我会在详细信息之前显示总时间:
A:85 秒(s3 流)
A:34 秒(aws.s3.upload)(编辑)
B:35 秒(缓冲区)
C:25 秒(并行缓冲区)
在这种情况下,显然使用缓冲区比使用流更快。我不知道 gm 或 s3-streams 是否有错误的流实现,或者我是否应该调整一些东西。现在我会选择解决方案 B。C 更快,但占用更多内存。
我 运行 这是一台低端 Digital Ocean Ubuntu 机器。这是我试过的:
一个。生成图块并逐个流式传输
我准备了一个数组,其中包含要生成的每个图块的作物信息和 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)
});
乙。生成切片到缓冲区并使用 AWS-package
直接上传每个缓冲区
- 由于瓦片生成到缓冲区,它们直接上传到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 相同,但将所有缓冲区存储在 tile 数组中以供以后并行上传
伪代码:
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-loop 之后完成的。我似乎没有通过将限制推到 10 以上来提高速度。
async.eachLimit(tiles, 10, function(tile, callback) {
s3.upload(tile.buffer..
callback()
)
});
编辑: 根据 Mark 的要求提供更多背景信息
我最初遗漏了细节,希望我能得到关于缓冲区与流的明确答案。
目标是通过 node/Express API 以响应方式为我们的应用程序提供图像。后端数据库是 Postgres。大容量存储是 S3。
传入的文件主要是照片、平面图和 pdf 文档。照片需要以多种尺寸存储,以便我可以以响应方式将它们提供给应用程序:缩略图、低分辨率、中分辨率和原始分辨率。
平面图必须是图块,这样我才能在应用程序中逐步加载它们(滚动图块)。一张全分辨率的 A1 绘图大约可以是 50 MPixels。
上传到 S2 的文件大小从 50kB(瓷砖)到 10MB(平面图)。
文件来自各个方向,但始终以流的形式出现:
- 通过网络或其他方式形成帖子API (SendGrid)
- 从应用上传
- 当上传的文件需要更多处理时从 S3 下载流
我不喜欢将文件暂时放在本地磁盘上,因此只有缓冲区与流。如果我可以使用磁盘,我会使用 IM 自己的平铺功能来实现真正快速的平铺。
为什么不是本地磁盘?
- 图像在上传到 S3 之前被加密。我不希望未加密的文件留在临时目录中。
- 总是存在清理临时文件的问题,在意外崩溃后可能会出现孤立文件等。
经过更多修改后,我觉得有必要回答我自己的问题。
最初我使用 npm 包 s3-streams 流式传输到 S3。这个包使用 aws.s3.uploadPart.
现在我发现 aws 包有一个简洁的函数 aws.s3.upload,它接受一个缓冲区或一个流。
切换到 AWS 自己的流媒体功能后 buffer/stream-upload。
之间没有时间差异
我可能以错误的方式使用了 s3-streams。但我也发现了这个库中可能存在的错误(有关文件 > 10MB)。我发布了一个问题,但没有得到任何答案。我的猜测是,自从 s3.upload 函数出现后,该库就被废弃了。
所以,我自己的问题的答案:
buffers/streams 之间可能存在差异,但在我的测试用例中它们是相等的,这暂时不是问题。
这是每个循环中新的 "save"-部分:
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()
});
感谢您的阅读。
我尝试了几个 Imagemagick 包装器库和一些 S3 库。由于性能差异很大,我很难选择最佳概念。
我已经选择了节点库 "gm",使用起来很愉快,而且有据可查。
至于 S3 我已经尝试了亚马逊自己的 AWS 库以及 "S3-Streams"
编辑: 我刚刚发现 AWS 库可以处理流。我想这是一个新函数 s3.upload(或者我只是错过了它?)。无论如何,我放弃了 s3-streams,它使用了更复杂的 s3uploadPart。 在我的测试用例中切换库流等于上传缓冲区。
我的测试用例是将一个 2MB 的 jpg 文件拆分成大约 30 个 512 像素的图块,并将每个图块发送到 S3。 Imagemagick 有一种非常快速的自动方式,可以通过裁剪命令生成切片。不幸的是,我还没有找到任何可以从自动生成的图块中捕获多文件输出的节点库。相反,我必须通过为每个图块单独调用裁剪命令来循环生成图块。
我会在详细信息之前显示总时间:
A:85 秒(s3 流)
A:34 秒(aws.s3.upload)(编辑)
B:35 秒(缓冲区)
C:25 秒(并行缓冲区)
在这种情况下,显然使用缓冲区比使用流更快。我不知道 gm 或 s3-streams 是否有错误的流实现,或者我是否应该调整一些东西。现在我会选择解决方案 B。C 更快,但占用更多内存。
我 运行 这是一台低端 Digital Ocean Ubuntu 机器。这是我试过的:
一个。生成图块并逐个流式传输
我准备了一个数组,其中包含要生成的每个图块的作物信息和 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)
});
乙。生成切片到缓冲区并使用 AWS-package
直接上传每个缓冲区- 由于瓦片生成到缓冲区,它们直接上传到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 相同,但将所有缓冲区存储在 tile 数组中以供以后并行上传
伪代码:
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-loop 之后完成的。我似乎没有通过将限制推到 10 以上来提高速度。
async.eachLimit(tiles, 10, function(tile, callback) {
s3.upload(tile.buffer..
callback()
)
});
编辑: 根据 Mark 的要求提供更多背景信息 我最初遗漏了细节,希望我能得到关于缓冲区与流的明确答案。
目标是通过 node/Express API 以响应方式为我们的应用程序提供图像。后端数据库是 Postgres。大容量存储是 S3。
传入的文件主要是照片、平面图和 pdf 文档。照片需要以多种尺寸存储,以便我可以以响应方式将它们提供给应用程序:缩略图、低分辨率、中分辨率和原始分辨率。
平面图必须是图块,这样我才能在应用程序中逐步加载它们(滚动图块)。一张全分辨率的 A1 绘图大约可以是 50 MPixels。
上传到 S2 的文件大小从 50kB(瓷砖)到 10MB(平面图)。
文件来自各个方向,但始终以流的形式出现:
- 通过网络或其他方式形成帖子API (SendGrid)
- 从应用上传
- 当上传的文件需要更多处理时从 S3 下载流
我不喜欢将文件暂时放在本地磁盘上,因此只有缓冲区与流。如果我可以使用磁盘,我会使用 IM 自己的平铺功能来实现真正快速的平铺。
为什么不是本地磁盘?
- 图像在上传到 S3 之前被加密。我不希望未加密的文件留在临时目录中。
- 总是存在清理临时文件的问题,在意外崩溃后可能会出现孤立文件等。
经过更多修改后,我觉得有必要回答我自己的问题。
最初我使用 npm 包 s3-streams 流式传输到 S3。这个包使用 aws.s3.uploadPart.
现在我发现 aws 包有一个简洁的函数 aws.s3.upload,它接受一个缓冲区或一个流。
切换到 AWS 自己的流媒体功能后 buffer/stream-upload。
之间没有时间差异我可能以错误的方式使用了 s3-streams。但我也发现了这个库中可能存在的错误(有关文件 > 10MB)。我发布了一个问题,但没有得到任何答案。我的猜测是,自从 s3.upload 函数出现后,该库就被废弃了。
所以,我自己的问题的答案:
buffers/streams 之间可能存在差异,但在我的测试用例中它们是相等的,这暂时不是问题。
这是每个循环中新的 "save"-部分:
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()
});
感谢您的阅读。