我有一个 8 核 CPU 和 8GB RAM,我正在创建一个批处理文件来自动执行 7-zip CLI,用尽大多数参数和变量来压缩同一组文件,最终目标是找到最强的参数组合和导致尽可能最小的存档大小的变量。
这本质上是非常耗时的,尤其是当要处理的文件集是千兆字节时。我需要一种不仅可以自动化而且可以加速整个过程的方法。
7-zip 使用不同的压缩算法,有些是单线程的,有些是多线程的,有些不需要太多内存,有些则需要大量内存,甚至可以超过 8GB。我已经成功创建了一个按顺序运行的自动批处理,其中排除了需要超过 8GB 内存的组合。
我将不同的压缩算法分成几批,以简化整个过程。例如,PPMd 中的压缩作为 7z 存档使用 1 线程,最高可达 1024MB。这是我当前的批次:
@echo off
echo mem=1m 2m 3m 4m 6m 8m 12m 16m 24m 32m 48m 64m 96m 128m 192m 256m 384m 512m 768m 1024m
echo o=2 3 4 5 6 7 8 10 12 14 16 20 24 28 32
echo s=off 1m 2m 4m 8m 16m 32m 64m 128m 256m 512m 1g 2g 4g 8g 16g 32g 64g on
echo x=1 3 5 7 9
for %%x IN (9) DO for %%d IN (1024m 768m 512m 384m 256m 192m 128m 96m 64m 48m 32m 24m 16m 12m 8m 6m 4m 3m 2m 1m) DO for %%w IN (32 28 24 20 16 14 12 10 8 7 6 5 4 3 2) DO for %%s IN (on) DO 7z.exe a teste.resultado\%%xx.ppmd.%%dd.%%ww.%%ss.7z .\teste.original\* -mx=%%x -m0=PPMd:mem=%%d:o=%%w -ms=%%s
exit
x
, s
, o
and mem
是参数,每个参数后面是 7z.exe 将使用的变量。x
and s
在这种情况下,无关紧要,它们意味着存档的压缩强度和实体块大小。
该批处理可以正常工作,但仅限于一次仅运行 1 个 7z.exe 实例,现在我正在寻找一种方法使其并行运行更多 7z.exe 实例,但不超过 8GB RAM 或 8 个线程立即(以先到者为准),然后再继续执行序列中的下一个。
我该如何改进这个?我有一些想法,但我不知道如何使它们批量工作。我正在考虑另外 2 个变量,它们不会与 7z 进程交互,但会控制下一个 7z 实例何时启动。一个变量将跟踪当前正在使用的线程数量,另一个变量将跟踪正在使用的内存量。那行得通吗?
编辑:
抱歉,我需要添加详细信息,我是这种发布风格的新手。按照这个答案 -https://stackoverflow.com/a/19481253/2896127- 我提到创建了 8 个批次,7z.PPMd 批次就是其中之一。也许列出所有批次以及 7z 如何处理参数将有助于更好地了解整个问题。我将从简单的开始:
- 7z.PPMd - 1 个充分利用的线程和字典相关的每个实例 32m-1055m 内存使用量。
- 7z.BZip2 - 8 个充分利用的线程,每个实例固定 109m 内存使用量。
- zip.Bzip2 - 8 个部分利用的线程,每个实例固定 336m 内存使用量。
- zip.Deflate - 8 个部分利用的线程,每个实例固定 260m 内存使用量。
- zip.PPMd - 每个实例有 8 个部分利用的线程和字典相关的 280m-2320m 内存使用量。
我对部分利用线程的意思是,虽然我分配 8 个线程供每个 7.exe 实例使用,但该算法可以随机方式执行可变 CPU 使用率,不受我控制,不可预测,但限制就在那里设置- 不超过 8 个线程。在 8 个线程充分利用的情况下,这意味着在我的 8 核 CPU 上,每个实例都使用 100% 的 CPU。
最复杂的 - 7z.LZMA、7z.LZMA2、zip.LZMA - 需要详细解释,但我现在时间不多了。只要我有更多空闲时间,我就会回来编辑 LZMA 部分。
再次感谢。
编辑:添加 LZMA 部分。
-
7z.LZMA - 每个实例都是n线程,范围从1到2:
- 1 fully utilized thread, dictionary dependant, 64k to 512m:
- 64k字典使用32m内存
- ...
- 512m字典使用5407m内存
- 排除范围:768m 至 1024m(高于 8192m 可用内存的限制)
- 2 partially utilized threads, dictionary dependant, 64k to 512m:
- 64k字典使用38m内存
- ...
- 512m字典使用5413m内存
- 排除范围:768m 至 1024m(高于 8192m 可用内存的限制)
-
7z.LZMA2 - 每个实例都是n线程,范围从1到8:
- 1 fully utilized thread, dictionary dependant, 64k to 512m:
- 64k字典使用32m内存
- ...
- 512m字典使用5407m内存
- 排除范围:768m 至 1024m(高于 8192m 可用内存的限制)
- 2 or 3 partially utilized threads, dictionary dependant, 64k to 512m:
- 64k字典使用38m内存
- ...
- 512m字典使用5413m内存
- 排除范围:768m 至 1024m(高于 8192m 可用内存的限制)
- 4 or 5 partially utilized threads, dictionary dependant, 64k to 256m:
- 64k字典使用51m内存
- ...
- 256m字典使用5677m内存
- 排除范围:384m 至 1024m(高于 8192m 可用内存的限制)
- 6 or 7 partially utilized threads, dictionary dependant, 64k to 192m:
- 64k字典使用62m内存
- ...
- 192m字典使用6965m内存
- 排除范围:256m 至 1024m(高于 8192m 可用内存的限制)
- 8 partially utilized threads, dictionary dependant, 64k to 128m:
- 64k字典使用72m内存
- ...
- 128m字典使用6717m内存
- 排除范围:192m 至 1024m(高于 8192m 可用内存的限制)
-
zip.LZMA - 每个实例都是 n 线程,范围从 1 到 8:
- 1 fully utilized thread, dictionary dependant, 64k to 512m:
- 64k字典使用3m内存
- ...
- 512m字典使用5378m内存
- 排除范围:768m 至 1024m(高于 8192m 可用内存的限制)
- 2 or 3 partially utilized threads, dictionary dependant, 64k to 512m:
- 64k字典使用9m内存
- ...
- 512m字典使用5384m内存
- 排除范围:768m 至 1024m(高于 8192m 可用内存的限制)
- 4 or 5 partially utilized threads, dictionary dependant, 64k to 256m:
- 64k字典使用82m内存
- ...
- 256m字典使用5456m内存
- 排除范围:384m 至 1024m(高于 8192m 可用内存的限制)
- 6 or 7 partially utilized threads, dictionary dependant, 64k to 256m:
- 64k字典使用123m内存
- ...
- 256m字典使用8184m(虽然非常接近限制,我可能会考虑排除它)
- 排除范围:384m 至 1024m(高于 8192m 可用内存的限制)
- 8 partially utilized threads, dictionary dependant, 64k to 128m:
- 64k字典使用164m内存
- ...
- 128m字典使用5536m内存
- 排除范围:192m 至 1024m(高于 8192m 可用内存的限制)
我试图理解其中包含 nul 的命令的行为。我不太明白那部分发生了什么,那些符号 ^ > ^&1 "" 的意思是什么。
2>nul del %lock%!nextProc!
%= Redirect the lock handle to the lock file. The CMD process will =%
%= maintain an exclusive lock on the lock file until the process ends. =%
start /b "" cmd /c %lockHandle%^>"%lock%!nextProc!" 2^>^&1 !cpu%%N! !cmd!
)
set "launch="
然后,在 :wait 代码处:
) 9>>"%lock%%%N"
) 2>nul
if %endCount% lss %startCount% (
1>nul 2>nul ping /n 2 ::1
goto :wait
)
2>nul del %lock%*
编辑2(2013年10月29日):添加当前情况。
经过反复试验研究,并补充了所发生情况的逐步注释,我能够理解上述行为。我将启动命令的行简化为:
start /b /low cmd /c !cmd!>"%lock%!nextProc!"
虽然可以用,但是还是不明白什么意思1^>"filename" 2^>^&1 'command'
。我知道这与在文件名中写入文本有关,否则会显示给我。在这种情况下,它将显示所有 7z.exe 文本,但写入文件中。在 7z.exe 实例完成其工作之前,文件中不会写入任何内容,但该文件已经存在,但同时又不存在。当 7z.exe 实际完成时,文件就完成了,这次它存在用于脚本的下一部分。
现在我可以理解建议脚本的处理行为,并且我用我自己的一些东西来补充它 - 我试图将所有批次实现为“一批完成所有工作”脚本。在简化版本中,就是这样:
echo 8 threads - maxproc=1
for %%x IN (9) DO for %%t IN (8) DO for %%d IN (900k) DO for %%s IN (on) DO 7z.exe a teste.resultado\%%xx.bzip2.%%tt.%%dd.%%ss.7z .\teste.original\* -mx=%%x -ms=%%s -m0=BZip2:d=%%d:mt=%%t
for %%x IN (9) DO for %%t IN (8) DO for %%d IN (900k) DO 7z.exe a teste.resultado\%%xx.bzip2.%%tt.%%dd.zip .\teste.original\* -mx=%%x -mm=BZip2:d=%%d -mmt=%%t
for %%x IN (9) DO for %%t IN (8) DO for %%w IN (257 256 192 128 96 64 48 32 24 16 12 8) DO 7z.exe a teste.resultado\%%xx.deflate64.%%tt.%%ww.zip .\teste.original\* -mx=%%x -mm=deflate64:fb=%%w -mmt=%%t
for %%x IN (9) DO for %%t IN (8) DO for %%w IN (258 256 192 128 96 64 48 32 24 16 12 8) DO 7z.exe a teste.resultado\%%xx.deflate.%%tt.%%ww.zip .\teste.original\* -mx=%%x -mm=deflate:fb=%%w -mmt=%%t
for %%x IN (9) DO for %%t IN (8) DO for %%d IN (256m 128m 64m 32m 16m 8m 4m 2m 1m) DO for %%w IN (16 15 14 13 12 11 10 9 8 7 6 5 4 3 2) DO 7z.exe a teste.resultado\%%xx.ppmd.%%tt.%%dd.%%ww.zip .\teste.original\* -mx=%%x -mm=PPMd:mem=%%d:o=%%w -mmt=%%t
echo 4 threads - maxproc=2
for %%x IN (9) DO for %%t IN (4) DO for %%d IN (256m) DO for %%w IN (273 256 192 128 96 64 48 32 24 16 12 8) DO for %%s IN (on) DO 7z.exe a teste.resultado\%%xx.lzma2.%%tt.%%dd.%%ww.%%ss.7z .\teste.original\* -mx=%%x -ms=%%s -m0=lzma2:d=%%d:fb=%%w -mmt=%%t
echo 2 threads - maxproc=4
for %%x IN (9) DO for %%t IN (2) DO for %%d IN (512m) DO for %%w IN (273 256 192 128 96 64 48 32 24 16 12 8) DO for %%s IN (on) DO 7z.exe a teste.resultado\%%xx.lzma.%%tt.%%dd.%%ww.%%ss.7z .\teste.original\* -mx=%%x -ms=%%s -m0=LZMA:d=%%d:fb=%%w -mmt=%%t
for %%x IN (9) DO for %%t IN (2) DO for %%d IN (512m) DO for %%w IN (273 256 192 128 96 64 48 32 24 16 12 8) DO for %%s IN (on) DO 7z.exe a teste.resultado\%%xx.lzma2.%%tt.%%dd.%%ww.%%ss.7z .\teste.original\* -mx=%%x -ms=%%s -m0=lzma2:d=%%d:fb=%%w -mmt=%%t
for %%x IN (9) DO for %%t IN (2) DO for %%d IN (512m) DO for %%w IN (273 256 192 128 96 64 48 32 24 16 12 8) DO 7z.exe a teste.resultado\%%xx.lzma.%%tt.%%dd.%%ww.zip .\teste.original\* -mx=%%x -mm=lzma:d=%%d:fb=%%w -mmt=%%t
echo 1 threads - maxproc=8
for %%x IN (9) DO for %%t IN (1) DO for %%d IN (512m) DO for %%w IN (273 256 192 128 96 64 48 32 24 16 12 8) DO for %%s IN (on) DO 7z.exe a teste.resultado\%%xx.lzma.%%tt.%%dd.%%ww.%%ss.7z .\teste.original\* -mx=%%x -ms=%%s -m0=LZMA:d=%%d:fb=%%w -mmt=%%t
for %%x IN (9) DO for %%t IN (1) DO for %%d IN (512m) DO for %%w IN (273 256 192 128 96 64 48 32 24 16 12 8) DO for %%s IN (on) DO 7z.exe a teste.resultado\%%xx.lzma2.%%tt.%%dd.%%ww.%%ss.7z .\teste.original\* -mx=%%x -ms=%%s -m0=lzma2:d=%%d:fb=%%w -mmt=%%t
for %%x IN (9) DO for %%d IN (1024m 768m 512m 384m 256m 192m 128m 96m 64m 48m 32m 24m 16m 12m 8m 6m 4m 3m 2m 1m) DO for %%w IN (32 28 24 20 16 14 12 10 8 7 6 5 4 3 2) DO for %%s IN (on) DO 7z.exe a teste.resultado\%%xx.ppmd.%%dd.%%ww.%%ss.7z .\teste.original\* -mx=%%x -m0=PPMd:mem=%%d:o=%%w -ms=%%s
for %%x IN (9) DO for %%t IN (1) DO for %%d IN (512m) DO for %%w IN (273 256 192 128 96 64 48 32 24 16 12 8) DO 7z.exe a teste.resultado\%%xx.lzma.%%tt.%%dd.%%ww.zip .\teste.original\* -mx=%%x -mm=lzma:d=%%d:fb=%%w -mmt=%%t
简而言之,我想以尽可能最有效的方式处理所有这些。通过决定一次可以运行多少个进程来实现这一点是一种方法,但同时还有每个进程所需的内存,因此这些进程所需的所有内存总和不会超过 8192 MB。我让这部分工作了。
@echo off
setlocal enableDelayedExpansion
set "maxMem=8192"
set "maxThreads=8"
:cycle1
set "cycleCount=4"
set "cycleThreads=1"
set "maxProc="
set /a "maxProc=maxThreads/cycleThreads"
set "cycleFor1=for %%x IN (9) DO for %%t IN (1) DO for %%d IN (512m) DO for %%w IN (273 256 192 128 96 64 48 32 24 16 12 8) DO for %%s IN (on) DO ("
set "cycleFor2=for %%x IN (9) DO for %%t IN (1) DO for %%d IN (512m) DO for %%w IN (273 256 192 128 96 64 48 32 24 16 12 8) DO for %%s IN (on) DO ("
set "cycleFor3=for %%x IN (9) DO for %%d IN (1024m 768m 512m 384m 256m 192m 128m 96m 64m 48m 32m 24m 16m 12m 8m 6m 4m 3m 2m 1m) DO for %%w IN (32 28 24 20 16 14 12 10 8 7 6 5 4 3 2) DO for %%s IN (on) DO ("
set "cycleFor4=for %%x IN (9) DO for %%t IN (1) DO for %%d IN (512m) DO for %%w IN (273 256 192 128 96 64 48 32 24 16 12 8) DO ("
set "cycleCmd1=7z.exe a teste.resultado\%%xx.lzma.%%tt.%%dd.%%ww.%%ss.7z .\teste.original\* -mx=%%x -ms=%%s -m0=LZMA:d=%%d:fb=%%w -mmt=%%t"
set "cycleCmd2=7z.exe a teste.resultado\%%xx.lzma2.%%tt.%%dd.%%ww.%%ss.7z .\teste.original\* -mx=%%x -ms=%%s -m0=lzma2:d=%%d:fb=%%w -mmt=%%t"
set "cycleCmd3=7z.exe a teste.resultado\%%xx.ppmd.%%dd.%%ww.%%ss.7z .\teste.original\* -mx=%%x -m0=PPMd:mem=%%d:o=%%w -ms=%%s"
set "cycleCmd4=7z.exe a teste.resultado\%%xx.lzma.%%tt.%%dd.%%ww.zip .\teste.original\* -mx=%%x -mm=lzma:d=%%d:fb=%%w -mmt=%%t"
set "tempMem1=5407"
set "tempMem2=5407"
set "tempMem3=1055"
set "tempMem4=5378"
rem set "tempMem1=5407"
rem set "tempMem2=5407"
rem set "tempMem3=1055 799 543 415 287 223 159 127 95 79 63 55 47 43 39 37 35 34 33 32"
rem set "tempMem4=5378"
set "memSum=0"
if not defined memRem set "memRem=!maxMem!"
for /l %%N in (1 1 %cycleCount%) DO (set "tempProc%%N=")
for /l %%N in (1 1 %cycleCount%) DO (
set memRem
set /a "tempProc%%N=%memRem%/tempMem%%N"
set /a "memSum+=tempMem%%N"
set /a "memRem-=tempMem%%N"
set /a "maxProc=!tempProc%%N!"
call :executeCycle
set /a "memRem+=tempMem%%N"
set /a "memSum-=tempMem%%N"
set /a "maxProc-=!tempProc%%!
)
goto :fim
:executeCycle
set "lock=lock_%random%_"
set /a "startCount=0, endCount=0"
for /l %%N in (1 1 %maxProc%) DO set "endProc%%N="
set launch=1
for %%x IN (9) DO for %%t IN (1) DO for %%d IN (512m) DO for %%w IN (273 256 192 128 96 64 48 32 24 16 12 8) DO for %%s IN (on) DO (
set "cmd=7z.exe a teste.resultado\%%xx.lzma.%%tt.%%dd.%%ww.%%ss.7z .\teste.original\* -mx=%%x -ms=%%s -m0=LZMA:d=%%d:fb=%%w -mmt=%%t"
if !startCount! lss %maxProc% (
set /a "startCount+=1, nextProc=startCount"
) else (
call :wait
)
set cmd!nextProc!=!cmd!
echo !time! - proc!nextProc!: starting !cmd!
2>nul del %lock%!nextProc!
start /b /low cmd /c !cmd!>"%lock%!nextProc!"
)
set "launch="
:wait
for /l %%N in (1 1 %startCount%) do (
if not defined endProc%%N if exist "%lock%%%N" (
echo !time! - proc%%N: finished !cmd%%N!
if defined launch (
set nextProc=%%N
exit /b
)
set /a "endCount+=1, endProc%%N=1"
) 9>>"%lock%%%N"
) 2>nul
if %endCount% lss %startCount% (
1>nul 2>nul ping /n 2 ::1
goto :wait
)
2>nul del %lock%*
echo ===
echo Thats all folks!
exit /b
:fim
pause
我有麻烦cycleFor1
and cycleCmd1
位于:cycle1
部分 - 他们应该更换for
线和第一条线cmd
里面的变量:executeCycle
,使其按照我的意愿工作。我怎么做?
我遇到的另一个问题是关于tempMem3
。我已经记录了命令时所需的所有内存cycleCmd3
将会运行。它依赖于字典。 tempMem3 和 CycleCmd3 的关系如下:
for %%d IN (1024m 768m 512m 384m 256m 192m 128m 96m 64m 48m 32m 24m 16m 12m 8m 6m 4m 3m 2m 1m) DO
set "tempMem3=1055 799 543 415 287 223 159 127 95 79 63 55 47 43 39 37 35 34 33 32"
因此 1024m 将使用 1055,768m 将使用 799,依此类推,直到 1m 使用 32。我不知道如何将其转换为脚本。
任何帮助表示赞赏。