memory
、map
、area
和 disk
资源限制以字节为单位定义。
要将它们设置convert -limit thread 2
首先启用2个并行线程作为第一步。
然后重新运行基准测试,并查看它是否真的会有所不同,如果有,那么差距有多大。
之后,您可以将限制设置为4甚至8,并重复这个过程...
5. 使用Magick像素缓存(MPC)和/或Magick持久注册表(MPR)
最后,您可以尝试使用ImageMagick像素缓存的特殊内部格式。
此格式称为MPC
(Magick像素缓存)。
它仅存在于内存中。
创建MPC时,处理的输入图像以未压缩的光栅格式保留在RAM中。
因此,MPC基本上是ImageMagick的本机内存中未压缩文件格式。
它只是直接内存转储到磁盘。
读取是从磁盘到内存的快速内存映射,根据需要进行(类似于内存页面交换)。
但不需要进行任何图像解码。
(更多技术细节:MPC作为格式不可移植。
它也不适合作为长期归档格式。
它的唯一适用性是高性能图像处理的中间格式。
它需要两个文件来支持一个图像。)
如果您仍然想将此格式保存到磁盘,请注意以下事项:
- 图像属性会写入扩展名为 .mpc 的文件中。
- 图像像素会写入带有扩展名 .cache 的文件中。
它的主要优点是在以下情况下体现出来...
- ...处理非常大的图像,或者
- ...对同一图像应用多个操作在“操作管道”中。
MPC专为与标准读取多次,写入一次相匹配的工作流模式而设计。
一些人说,对于这样的操作,性能会提高,但我没有个人经验。
首先将基础图片转换为MPC:
convert input.jpeg input.mpc
然后再运行:
convert input.mpc [...your long-long-long list of crops and operations...]
那么看看这是否显著节省您的时间。
很可能您甚至可以使用此MPC格式“内联”(使用特殊的mpc:
符号表示法,参见下文)。
MPR格式(内存持久寄存器)执行类似操作。它将图像读入命名内存寄存器中。如果处理管道需要多次访问图像,则该管道还可以从该寄存器再次读取图像。图像在当前命令管道退出时保留在寄存器中。
但我从未将此技术应用于实际问题,因此无法确定其在实际生活中的运作方式。
6. 构建合适的IM处理管道以一步完成所有任务
根据您描述的过程,它由4个不同的步骤组成:
- 将TIFF转换为JPEG。
- 将JPEG图像调整大小为xx (?? 值 ??)
- 将JPEG剪裁到200px。
- 添加文本水印。
请告诉我是否正确理解您的意图:
- 您有1个输入文件,即TIFF。
- 您想要2个最终输出文件:
- 1个缩略图JPEG,大小为200x200像素;
- 1个带有1024像素宽度的标记JPEG(高度保持输入TIFF的纵横比);
- 1个(未标记的)JPEG仅是中间文件,您并不真正想要保留它。
基本上,每个步骤都使用自己的命令--总共4个不同的命令。通过使用单个命令管道来执行所有步骤,这可以大大加快速度。
此外,您似乎并不需要将未标记的JPEG作为最终结果保留--然而您用于生成它作为一个中间临时文件的唯一命令将其保存到了磁盘上。我们可以尝试跳过这个步骤,直接实现最终结果,而不需要这个额外的写入磁盘。
有不同的方法可以实现此更改。我现在只会向您(和其他读者)显示一种--仅针对CLI,而不是PHP。我不是PHP专家——你需要把我的CLI方法转换成适当的PHP调用。
(但请务必先确保真正理解更复杂的ImageMagick命令行的架构和结构!为此目标,请参考我其他答案中的内容:)
您的4个步骤转化为以下单独的ImageMagick命令:
convert image.tiff image.jpg
convert image.jpg -resize 1024x image-1024.jpg
convert image-1024.jpg -thumbnail 200x200 image-thumb.jpg
convert -background white image-1024.jpg label:12345 -append image-labelled.jpg
现在将这个工作流转换为一个单一的管道命令...以下命令可以实现此目的。它应该执行得更快(无论您在遵循上述步骤0.--4.时得到什么结果):
现在要将这个工作流程转化为一个单一的管道命令,下面的命令可以做到这一点。它应该比你按照上述步骤0.--4.执行的结果更快:convert image.tiff \
-respect-parentheses \
+write mpr:XY \
\( mpr:XY +write image-1024.jpg \) \
\( mpr:XY -thumbnail 200x200 +write image-thumb.jpg \) \
\( mpr:XY -background white label:12345 -append +write image-labelled.jpg \) \
null:
说明:
-respect-parentheses
:
需要使用此选项,使得 \( .... \)
括号内的子命令真正成为独立的命令。
+write mpr:XY
:
用于将输入文件写入 MPR 存储器中。
XY
是一个标签(您可以使用任何名称),用于稍后重新调用相同的图像。
+write image-1024.jpg
:
将在第一个括号对中执行的子命令的结果写入磁盘。
+write image-thumb.jpg
:
将在第二个括号对中执行的子命令的结果写入磁盘。
+write image-labelled.jpg
:
将在第三个括号对中执行的子命令的结果写入磁盘。
null:
:
终止命令管道。如果没有这个选项,我们会以最后一个子命令的闭合括号结尾。
7. 对比运行 4 个单独的命令和单一管道的性能差异
为了对我的建议有一个粗略的感觉,我运行了下面的命令。
第一个命令运行了 100 次 4 个独立命令的序列(并将所有生成的图像保存在不同的文件名下)。
time for i in $(seq -w 1 100); do
convert image.tiff \
image-indiv-run-${i}.jpg
convert image-indiv-run-${i}.jpg -sample 1024x \
image-1024-indiv-run-${i}.jpg
convert image-1024-indiv-run-${i}.jpg -thumbnail 200x200 \
image-thumb-indiv-run-${i}.jpg
convert -background white image-1024-indiv-run-${i}.jpg label:12345 -append \
image-labelled-indiv-run-${i}.jpg
echo "DONE: run indiv $i ..."
done
我对4个单独的命令(重复100次!)的结果是这样的:
real 0m49.165s
user 0m39.004s
sys 0m6.661s
第二个命令对单个管道进行计时:time for i in $(seq -w 1 100); do
convert image.tiff \
-respect-parentheses \
+write mpr:XY \
\( mpr:XY -resize 1024x \
+write image-1024-pipel-run-${i}.jpg \) \
\( mpr:XY -thumbnail 200x200 \
+write image-thumb-pipel-run-${i}.jpg \) \
\( mpr:XY -resize 1024x \
-background white label:12345 -append \
+write image-labelled-pipel-run-${i}.jpg \) \
null:
echo "DONE: run pipeline $i ..."
done
单管道(重复100次!)的结果如下:
real 0m29.128s
user 0m28.450s
sys 0m2.897s
从结果可以看出,单个管道比4个单独的命令快约40%!
现在你可以投资于多CPU、更多内存和快速SSD硬件以进一步提高速度 :-)
但首先将这个CLI方法转换成PHP代码...
关于这个主题还有一些要说的话。
但是我的时间已经用完了。
我可能会在几天后回到这个答案,再更新一些内容...
更新: 我不得不使用新的基准测试数字更新这个问题的答案:
最初我忘记了将-resize 1024x
操作(我真笨!)包含在管道版本中。
虽然仍然存在性能提升,但不像之前那么大。
8. 使用-clone 0
在内存中复制图像
这是另一个替代方案,可以尝试使用mpr:
方法和上面建议的命名内存寄存器。
它再次使用括号内的“旁路处理”-clone 0
操作。
它的工作方式如下:
convert
从磁盘一次读取输入TIFF并加载到内存中。
- 每个
-clone 0
操作符都会复制第一个加载的图像(因为它在当前图像堆栈中具有索引0
)。
- 总命令管道的每个“括号内”子管道对克隆执行某些操作。
- 每个
+write
操作将相应结果保存到磁盘。
所以这里是基准测试的命令:
time for i in $(seq -w 1 100); do
convert image.tiff \
-respect-parentheses \
\( -clone 0 -thumbnail 200x200 \
+write image-thumb-pipel-run-${i}.jpg \) \
\( -clone 0 -resize 1024x \
-background white label:12345 -append \
+write image-labelled-pipel-run-${i}.jpg \) \
null:
echo "DONE: run pipeline $i ..."
done
我的结果:
real 0m19.432s
user 0m18.214s
sys 0m1.897s
令我惊讶的是,这比使用 mpr:
版本要快!
9. 使用 -scale
或 -sample
替代 -resize
这个替代方案很可能会加速您的重新调整大小子操作。但它很可能会导致图像质量稍微变差(您需要验证是否可见差异)。
关于 -resize
、-sample
和 -scale
之间的区别的一些背景信息,请参见以下答案:
我也试过了:
time for i in $(seq -w 1 100); do
convert image.tiff \
-respect-parentheses \
\( -clone 0 -thumbnail 200x200 \
+write image-thumb-pipel-run-${i}.jpg \) \
\( -clone 0 -scale 1024x \
-background white label:12345 -append \
+write image-labelled-pipel-run-${i}.jpg \) \
null:
echo "DONE: run pipeline $i ..."
done
我的结果:
real 0m16.551s
user 0m16.124s
sys 0m1.567s
这是目前为止最快的结果(我将其与+clone
变体结合使用)。
当然,这种修改也可以应用于您最初运行4个不同命令的方法。
10. 通过在命令中添加-depth 8
来模拟Q8
构建。
我实际上没有运行和测试过这个命令,但完整的命令应该是:
time for i in $(seq -w 1 100); do
convert image.tiff \
-respect-parentheses \
\( -clone 0 -thumbnail 200x200 -depth 8 \
+write d08-image-thumb-pipel-run-${i}.jpg \) \
\( -clone 0 -scale 1024x -depth 8 \
-background white label:12345 -append \
+write d08-image-labelled-pipel-run-${i}.jpg \) \
null:
echo "DONE: run pipeline $i ..."
done
这种修改方法也适用于你最初的“我运行4个不同命令”的方式。
11. 使用GNU parallel
结合Mark Setchell的建议进行组合
当然,只有在您的整体工作流程允许这种并行处理时,才适合您使用此方法。
对于我的小型基准测试来说,它是适用的。对于您的 Web 服务,可能一次只能处理一个任务...
time for i in $(seq -w 1 100); do \
cat <<EOF
convert image.tiff \
\( -clone 0 -scale 1024x -depth 8 \
-background white label:12345 -append \
+write d08-image-labelled-pipel-run-${i}.jpg \) \
\( -clone 0 -thumbnail 200x200 -depth 8 \
+write d08-image-thumb-pipel-run-${i}.jpg \) \
null:
echo "DONE: run pipeline $i ..."
EOF
done | parallel --will-cite
结果:
real 0m6.806s
user 0m37.582s
sys 0m6.642s
用户时间user
和实际时间real
的表面矛盾可以解释为:
user
时间表示在8个不同的CPU核心上计时的所有时间间隔之和。
从用户看到的手表上的角度来看,时间过得更快:不到10秒钟。
12. 总结
选择自己喜欢的方法--结合不同的方法:
通过构建更聪明的命令管道可以获得一些加速(与当前图像质量相同),
避免运行各种命令(其中每个convert
导致一个新的进程,并且必须从磁盘读取其输入)。
将所有图像处理打包成一个单一的进程。
利用“带括号的侧向处理”。
利用-clone
或mbr:
或mbc:
甚至组合每一个。
可以通过在性能和图像质量之间做出权衡来进一步提高速度:
您的一些选择是:
-depth 8
(必须在OP的系统上声明)vs。-depth 16
(OP的系统上的默认值)
-resize 1024
vs. -sample 1024x
vs. -scale 1024x
如果您的工作流允许,则可以使用GNUparallel
。