我有一个相对简单的复合操作,正在使用 AWS Lambda 上的 imagemagick 执行。为什么它通常需要一分钟以上的时间才能完成?
这将在具有透明孔的较大背景“后面”合成四幅较小的图像:
convert -size 1280x720 'xc:#747784' \
\( /tmp/$uuid/face-1.png -background none -distort SRT "0.132 16.094" \) -geometry +324.929+110.781 -composite \
\( /tmp/$uuid/face-2.png -background none -distort SRT "0.132 24.486" \) -geometry +401.5+106.375 -composite
\( /tmp/$uuid/face-3.png -background none -distort SRT "1 0" \) -geometry +-166+-250 -composite
\( /tmp/$uuid/face-4.png -background none -distort SRT "1 0" \) -geometry +-166+-250 -composite
/tmp/$uuid/____MASTER_720P_00230.png -composite \
-depth 8 png:/tmp/$uuid/230.png
在我的(6年旧的)笔记本电脑上 - 这些操作通常需要 2 到 3 秒。
在 Lambda 上(即使考虑到将五个源文件从 s3 复制到 /tmp 文件系统的开销),这需要大约 30 秒的时间,并且经常达到我设置的最大执行时间(1 分钟)。我将 CPU 和超时时间增加到 2 分 30 秒,但仍然看到超时。
据称,Lambda 内置的 ImageMagick (v6) 在 OpenMP 支持方面存在错误,因此我尝试通过使用以下方法进行缓解:
OMP_NUM_THREADS=1 \
MAGICK_THREAD_LIMIT=1 \
convert -size 1280x720 'xc:#747784' \
\( /tmp/$uuid/face-1.png -background none -distort SRT "0.132 16.094" \) -geometry +324.929+110.781 -composite \
\( /tmp/$uuid/face-2.png -background none -distort SRT "0.132 24.486" \) -geometry +401.5+106.375 -composite
\( /tmp/$uuid/face-3.png -background none -distort SRT "1 0" \) -geometry +-166+-250 -composite
\( /tmp/$uuid/face-4.png -background none -distort SRT "1 0" \) -geometry +-166+-250 -composite
/tmp/$uuid/____MASTER_720P_00230.png -composite \
-depth 8 png:/tmp/$uuid/230.png
但这仍然让我的运行时间达到两分钟及以上(经常超时)。
然后,我编译了一个完全静态的 ImageMagick v7 (7.0.7-13)(添加--with-quantum-depth=8
以尝试减少处理开销)并将其添加到我的 Lambda 包中,并将转换调用更改为:
/var/task/bin/magick -size 1280x720 'xc:#747784'
\( /tmp/$uuid/face-1.png -background none -distort SRT "0.132 16.094" \) -geometry +324.929+110.781 -composite \
\( /tmp/$uuid/face-2.png -background none -distort SRT "0.132 24.486" \) -geometry +401.5+106.375 -composite \
\( /tmp/$uuid/face-3.png -background none -distort SRT "1 0" \) -geometry +-166+-250 -composite \
\( /tmp/$uuid/face-4.png -background none -distort SRT "1 0" \) -geometry +-166+-250 -composite \
/tmp/$uuid/____MASTER_720P_00230.png -composite \
-depth 8 png:/tmp/$uuid/230.png
但运行时间仍然没有明显的改善。
这是整个 Lambda 调用的代码(这里发布时经过了轻微的手动编辑,因此任何错误都只出现在这个代码片段中)
var Q = require('q');
var path = require('path');
// A lovely set of composable modules
// https://github.com/lambduh/lambduh
var execute = require('lambduh-execute');
var s3Download = require('lambduh-get-s3-object');
var upload = require('lambduh-put-s3-object');
var s3Upload = require('lambduh-put-s3-object');
// This contains the relevant imagemagick convert call for each frame, indexed
// by frame
var animationFrames = require('./composite.json');
var bucket = "super.secret.bucket";
var frame, paddedFrame, uuid;
process.env['PATH'] = process.env['PATH'] + ':/tmp/:' + process.env['LAMBDA_TASK_ROOT']
exports.handler = function(event, context) {
frame = event.frame;
paddedFrame = ("00000" + frame).slice(-5);
uuid = event.uuid;
console.log("bucket: ", bucket);
console.log("frame: ", frame);
console.log("folder: ", uuid);
// make the /tmp/uuid directory where we'll download the face pngs and
// the relevant frame png to. Tidy up from old runs first.
execute(event, {
shell: "rm -rf /tmp/*; mkdir -p /tmp/" + uuid,
logOutput: true
})
//now get the face pngs and frame png and put them there
.then(function(event){
var def = Q.defer();
var s3Files = []
var message = {};
s3Files.push("backgrounds/____MASTER_720P_" + paddedFrame + ".png");
s3Files.push("videos/" + uuid + "/face-1.png")
s3Files.push("videos/" + uuid + "/face-2.png")
s3Files.push("videos/" + uuid + "/face-3.png")
s3Files.push("videos/" + uuid + "/face-4.png")
var promises = [];
s3Files.forEach(function(s3File) {
console.log("Going to download %s/%s to /tmp/%s/%s", bucket, s3File, uuid, path.basename(s3File));
promises.push(s3Download(event, {
srcBucket: bucket,
srcKey: s3File,
downloadFilepath: "/tmp/" + uuid + "/" + path.basename(s3File)
})
.fail(function(){
console.log("Couldn't download ", s3File);
})
)});
Q.all(promises)
.then(function(event) {
def.resolve(event[0]);
})
.fail(function(err) {
def.reject(err);
});
return def.promise;
})
.then(function(event){
console.log("Compositing with imagemagick");
return execute(event, {
shell: "export uuid=" + uuid + " && " + animationFrames[frame],
logOutput: true
})
})
.then(function(event){
console.log("Going to upload /tmp/%s/%s.png to %s/videos/%s/%s.png", uuid, frame, bucket, uuid, frame);
return s3Upload(null, {
dstBucket: bucket,
dstKey: "videos/" + uuid + "/" + frame + ".png",
uploadFilepath: "/tmp/" + uuid + "/" + frame + ".png"
})
})
.then(function(event){
console.log("finished");
console.log(event);
context.succeed(frame)
})
.fail(function(err) {
console.log(err);
//fail soft so lambda doesn't try to run this function again
context.done(null, err);
});
}