我有一个带有 8GB RAM 的 8 核 CPU,我正在创建一个批处理文件来自动化 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
和mem
是参数,每个参数后面是 7z.exe 将使用的变量。x
在这种情况下,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 内存使用量。
我对部分使用线程的意思是,虽然我为每个 7.exe 实例分配了 8 个线程,但该算法可以以随机方式执行可变 CPU 使用率,这是我无法控制的,不可预测的,但限制设置在那里- 不超过 8 个线程。在 8 个充分利用的线程的情况下,这意味着在我的 8 核 CPU 上,每个实例都在使用 100% 的 CPU。
最复杂的——7z.LZMA、7z.LZMA2、zip.LZMA——需要详细解释,但我现在时间不多了。每当我有更多空闲时间时,我都会回来编辑 LZMA 部分。
再次感谢。
编辑:添加 LZMA 部分。
7z.LZMA - 每个实例都是 n 线程的,范围从 1 到 2:
- 1 个充分利用的线程,依赖于字典,64k 到 512m:
- 64k 字典使用 32m 内存
- ...
- 512m 字典使用 5407m 内存
- 排除范围:768m 到 1024m(超过 8192m 可用内存的限制)
- 2 个部分使用的线程,依赖于字典,64k 到 512m:
- 64k 字典使用 38m 内存
- ...
- 512m字典使用5413m内存
- 排除范围:768m 到 1024m(超过 8192m 可用内存的限制)
- 1 个充分利用的线程,依赖于字典,64k 到 512m:
7z.LZMA2 - 每个实例都是 n 线程的,范围从 1 到 8:
- 1 个充分利用的线程,依赖于字典,64k 到 512m:
- 64k 字典使用 32m 内存
- ...
- 512m 字典使用 5407m 内存
- 排除范围:768m 到 1024m(超过 8192m 可用内存的限制)
- 2 或 3 个部分使用的线程,取决于字典,64k 到 512m:
- 64k 字典使用 38m 内存
- ...
- 512m字典使用5413m内存
- 排除范围:768m 到 1024m(超过 8192m 可用内存的限制)
- 4 或 5 个部分使用的线程,取决于字典,64k 到 256m:
- 64k 字典使用 51m 内存
- ...
- 256m 字典使用 5677m 内存
- 排除范围:384m 到 1024m(超过 8192m 可用内存的限制)
- 6 或 7 个部分使用的线程,取决于字典,64k 到 192m:
- 64k 字典使用 62m 内存
- ...
- 192m字典使用6965m内存
- 排除范围:256m到1024m(超过8192m可用内存的限制)
- 8 个部分使用的线程,依赖于字典,64k 到 128m:
- 64k 字典使用 72m 内存
- ...
- 128m字典使用6717m内存
- 排除范围:192m 到 1024m(超过 8192m 可用内存的限制)
- 1 个充分利用的线程,依赖于字典,64k 到 512m:
zip.LZMA - 每个实例都是 n 线程的,范围从 1 到 8:
- 1 个充分利用的线程,依赖于字典,64k 到 512m:
- 64k 字典使用 3m 内存
- ...
- 512m字典使用5378m内存
- 排除范围:768m 到 1024m(超过 8192m 可用内存的限制)
- 2 或 3 个部分使用的线程,取决于字典,64k 到 512m:
- 64k 字典使用 9m 内存
- ...
- 512m 字典使用 5384m 内存
- 排除范围:768m 到 1024m(超过 8192m 可用内存的限制)
- 4 或 5 个部分使用的线程,取决于字典,64k 到 256m:
- 64k 字典使用 82m 内存
- ...
- 256m字典使用5456m内存
- 排除范围:384m 到 1024m(超过 8192m 可用内存的限制)
- 6 或 7 个部分使用的线程,取决于字典,64k 到 256m:
- 64k 字典使用 123m 内存
- ...
- 256m 字典使用 8184m (虽然非常接近极限,我可以考虑排除它)
- 排除范围:384m 到 1024m(超过 8192m 可用内存的限制)
- 8 个部分使用的线程,依赖于字典,64k 到 128m:
- 64k 字典使用 164m 内存
- ...
- 128m字典使用5536m内存
- 排除范围:192m 到 1024m(超过 8192m 可用内存的限制)
- 1 个充分利用的线程,依赖于字典,64k 到 512m:
我试图了解其中带有 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(29-10-2013):添加当前情况。
经过反复试验研究,并辅以对正在发生的事情的逐步说明,我能够理解上述行为。我将带有 start 命令的行简化为:
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
并且部分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。我不知道如何将其转换为脚本。
任何帮助表示赞赏。