分页: 1 / 1

MP_Pipeline教程

发表于 : 2012-09-27 21:18
dgwxx
前言
本文是使用SAPikachu开发的MP_Pipeline工具过程中的一些笔记和心得。欢迎回帖讨论、纠正补充。
阅读本文不需要有大量视频知识,但需要有一定AviSynth基础。因此建议中级以上用户阅读本文。完全不了解AviSynth的初学者请先学习AviSynth入门。
MP_Pipeline的发布贴 http://www.nmm-hd.org/newbbs/viewtopic.php?f=7&t=498
里面有下载链接、更新日志和一些原作者的说明。

一、MP_Pipeline基础知识
  1. MP_Pipeline是什么
    MP_Pipeline是由SAPikachu开发的多线程运行AviSynth(以下略作AVS)脚本的辅助工具。
    通常,在下面这种经典场景中,AVS占用的系统资源相对有限。

    代码: 全选

    mpeg2source("01.d2v")
    tfm(pp=3,mode=5,chroma=true,slow=2)
    tdecimate()
    fft3dfilter(sigma=1)
    crop(8,0,-8,0)
    lanczosresize(640,480)
    
    但当脚本中使用LSF、MCTD、TGMC等很复杂的脚本滤镜的时候,就会面临AVS消耗大量内存导致脚本无法启动或在压制过程中崩溃的风险。
    为了解决此类问题,SAPikachu开发了MP_Pipeline这个工具,通过将avs脚本分成多个部分、并将每个部分分别放在子进程中运行,来避免一个avs进程占用过多内存最终崩溃的问题。
    理论上,由于进程间的数据传递和同步,会造成轻微的性能下降。但因为SAPikachu对性能进行了大量优化,将性能损失控制在“理论”范围内,所以实际应用过程中几乎不用考虑性能因素。
    但是,MP_Pipeline的使用是以大量消耗内存为代价的。请参照下图:
    20120927222851.png
    20120927222851.png (8.71 KiB) 查看 10082 次
    请准备至少8G内存。
    能实现类似功能的还有leiming开发的sora系列工具(sorasmsource、sorasmserver、sorathread等)。
  2. MP_Pipeline的版本
    本文以编写时的最新版本MP_Pipeline 0.14为准。MP_Pipeline的版本号稍显个性,区别版本高低不是看小数本身大小,而是看小数点后面的数字大小。比如0.14版就比0.3和0.2都新(14 > 3 > 2)。刚刚接触的人请注意不要搞混。
二、MP_Pipeline的基本应用
使用MP_Pipeline首先需要根据正在使用的avs的版本选择正确的MP_Pipeline版本。使用x86版avs,需要使用对应的x86版MP_Pipeline。x64版亦然。本文将使用x86版作例子。
确定版本之后,首先要将对应版本目录中的全部文件解压并放在一起。比如将x86版的文件全部解压到c:\AviSynthPlugins:

代码: 全选

c:\AviSynthPlugins\MP_Pipeline_readme.avs            # 说明文件,不需要import。
c:\AviSynthPlugins\MP_Pipeline.dll.win64             # x64版插件,需要时自动调用无需loadplugin。
c:\AviSynthPlugins\MP_Pipeline.dll.slave.exe         # x86子进程容器,需要时后台自动运行。
c:\AviSynthPlugins\MP_Pipeline.dll.win64.slave.exe   # x64子进程容器,需要时后台自动运行。
c:\AviSynthPlugins\MP_Pipeline.dll                   # 32bit插件,使用MP_Pipeline唯一需要loadplugin的文件。
在avs中,需要首先加载MP_Pipeline.dll,然后在MP_Pipeline(""" """)结构中按通常方式书写avs代码,用### ###标记来分割进程。

代码: 全选

loadplugin("c:\AviSynthPlugins\MP_Pipeline.dll")

MP_Pipeline("""

    #段落1
    avisource("01.avi")

### ###

    #段落2
    QTGMC()

### ###

    #段落3
    MCTD(settings="high")

""")
这样,MP_Pipeline就会创建三个进程,分别运行avisource、QTGMC和MCTD。每加入一个### ###就会分出一个进程。
在这里希望读者记住一个概念:在本文中,我们将### ###分出的每个部分称作一个“段落”,每个“段落”一个进程。


三、MP_Pipeline的进阶应用
除了将avs脚本分进程消化以外,MP_Pipeline还提供了诸多附加命令,来实现众多高级功能。需要注意的是,每个命令都只对加入了该命令的段落有效,对以外的段落无效。
  1. 使用外置滤镜
    1. 载入外置滤镜需要注意
      若使用外置滤镜,需要在使用该滤镜的段落中载入。就算在不同段落中使用同一个滤镜,也要在每个使用该滤镜的段落中都载入。
      错误的例子:

      代码: 全选

      loadplugin("c:\AviSynthPlugins\MP_Pipeline.dll")
      loadplugin("c:\AviSynthPlugins\DGDecode.dll")
      loadplugin("c:\AviSynthPlugins\TIVTC.dll")
      import("c:\AviSynthPlugins\SomeScript.dll")
      
      MP_Pipeline("""
      
          mpeg2source("01.d2v")
          SomeScript()
      
      ### ###
      
          tfm(pp=3,mode=5,chroma=true,slow=2)
          tdecimate()
          SomeScript()
      
      """)
      
      这样写会报告找不到滤镜。
      正确例子:

      代码: 全选

      loadplugin("c:\AviSynthPlugins\MP_Pipeline.dll")
      
      MP_Pipeline("""
      
          loadplugin("c:\AviSynthPlugins\DGDecode.dll")
          import("c:\AviSynthPlugins\SomeScript.avsi")
      
          mpeg2source("01.d2v")
          SomeScript()
      
      ### ###
      
          loadplugin("c:\AviSynthPlugins\TIVTC.dll")
          import("c:\AviSynthPlugins\SomeScript.avsi")
      
          tfm(pp=3,mode=5,chroma=true,slow=2)
          tdecimate()
          SomeScript()
      
      """)
      
    2. 通过全局代码功能载入外置滤镜(inherit命令)
      少量滤镜分别写还好,但如果需要用到LSF、MCTD、TGMC这样需要载入大量依存滤镜的脚本的话,分开写会让代码变得混乱且难以维护。
      MP_Pipeline提供了全局代码段功能来解决这个问题。全局代码段通过inherit命令:

      代码: 全选

      ### inherit start ###
          SomeCode
      ### inherit end ###
      
      来指定,包含在这个区间的代码会自动在主段落、当前段落、和当前段落以下的全部段落开始处执行。
      上面的例子使用全局代码功能重写的话:

      代码: 全选

      MP_Pipeline("""
      
      ### inherit start ###
          loadplugin("c:\AviSynthPlugins\MP_Pipeline.dll")
          loadplugin("c:\AviSynthPlugins\DGDecode.dll")
          loadplugin("c:\AviSynthPlugins\TIVTC.dll")
          import("c:\AviSynthPlugins\SomeScript.dll")
      ### inherit end ###
      
          mpeg2source("01.d2v")
          SomeScript()
      
      ### ###
      
          tfm(pp=3,mode=5,chroma=true,slow=2)
          tdecimate()
          SomeScript()
      
      """)
      
      这里需要注意的是,全局部分只对主进程段落、当前段落、当前段落以下的段落有效。比如在段落2中插入了全局代码,只会在主进程段落、段落2、段落3、段落4...有效,段落1是无效的。
  2. 分支(branch命令)
    不少情况下,一个很慢的滤镜无法将CPU跑满。那么就可以将影片分成多个部分,同时运行来最大限度地提高系统资源的利用效率。
    MP_Pipeline提供了branch命令来实现这个功能。branch命令的用法如下:

    代码: 全选

    ### branch: <number of branch processes>[, <thunk size>]
    branch命令有两个参数,第一个参数指定分割的进程数,是必选参数;第二个参数指定每段大小,是可选参数,默认为1。
    比如### branch: 3,5代表该段滤镜分为3个进程同时执行,每5帧为一组进行分配。

    代码: 全选

    进程 A 处理:  0, 1, 2, 3, 4, 15,16,17,18,19, ...
    进程 B 处理:  5, 6, 7, 8, 9, 20,21,22,23,24, ...
    进程 C 处理: 10,11,12,13,14, 25,26,27,28,29, ...
    
    以此类推。原作者的例子:

    代码: 全选

    MP_Pipeline("""
    
        loadplugin("c:\AviSynthPlugins\DGDecode.dll")
        mpeg2source("01.d2v")
    
    ### ###
    
        loadplugin("c:\AviSynthPlugins\TNLMeans.dll")
        TNLMeans()
    
    ### branch: 4
    ### ###
    
    """)
    
    TNLMeans()将会被分成四个进程运行。
    需要注意的是,branch功能只适用于2D滤镜(空间滤镜),将branch用于3D滤镜(时间轴滤镜)将会极大地降低效率,并且会给滤镜效果带来不可知的影响。
  3. 跨x86/x64使用滤镜(platform命令)
    在同时装有x86和x64 AviSynth的64bit系统中,该命令允许在64bit进程中使用x86滤镜,或在32bit进程中使用x64滤镜。

    代码: 全选

    ### platform: <win32|win64>
    该命令有一个参数,只能是win32或win64。用法:

    代码: 全选

    MP_Pipeline("""
    
        loadplugin("c:\AviSynthPlugins\DGDecode.dll")
        mpeg2source("01.d2v")
    
    ### ###
    
        loadplugin("c:\AviSynthPlugins\fft3dfilter_x64.dll")
        fft3dfilter(sigma=1)
    
    ### platform: win64
    ### ###
    
    """)
    
    就能在x86版x264压制时使用x64滤镜了,反之在x64版x264中使用x86滤镜也可以(其实这个功能的主要用途是后者)。
  4. 使用预读(prefetch命令)
    MP_Pipeline提供了prefetch命令进行预读,提高滤镜执行效率。

    代码: 全选

    ### prefetch: <max frames in cache per clip>, <frames to keep behind current frame>
    prefetch命令有两个参数,第一个是每个clip提前当前帧预读的帧数;第二个是当前帧后保留的帧数。
    需要注意的是,当使用会random access(随机访问)的滤镜的时候不要使用该功能。
    例:

    代码: 全选

    MP_Pipeline("""
    
        aviource("source.avi")
        QTGMC()
    
    ### prefetch: 16, 0
    ### ###
    
    """)
    
    MCTD()
    
    
    在第一段落中预读16帧。
  5. 跨段落传递clip
    1. 在MP_Pipeline中,默认状态下段落和段落之间能传递的clip只有last,但如果需要传递其他的clip的话,可以用传递clip的功能。MP_Pipeline提供了export clip和pass clip两种传递方式,他们使用场合各不相同。
      需要注意的是,这两种方式都仅支持clip型变量,其他类型的变量无法跨段落传递。
    2. 使用export clip
      作用是将一个段落中的某个clip型变量的最终值(=整个段落全部执行完毕之后的值)传递到下一个段落。无论export clip语句写在段落的哪个位置,传递的都将是该clip的最终值。比如:

      代码: 全选

      MP_Pipeline("""
      
          ColorBars().ConvertToYV12
          a = last
          b = last
          ### export clip: a, b
          a = a.Invert()
      
      ### ###
      
          return a
      
      """)
      
      尽管export语句写在了段落中间,但在下一个段落中得到的a仍将是经过了Invert()的。
    3. 使用pass clip
      pass clip命令的作用,是将上一个段落中export出来的clip原封不动地传递给下一个段落。比如:

      代码: 全选

      MP_Pipeline("""
      
          ColorBars().ConvertToYV12
          a = last
          b = last
          ### export clip: a, b
          a = a.Invert()
      
      ### ###
      
          a = a.Greyscale()
          ### pass clip: a
      
      ### ###
      
          return a
      
      """)
      
      尽管在段落2中对a加了Greyscale(),但由于用的是pass clip,所以a将以段落1中export出来的状态,被原封不动地传递给段落3。段落3中得到的a不受段落2中的处理影响。
      要注意的是,pass clip必须用在export clip的段落的下一个段落中。
  6. 锁定线程到单核心
    在多核心/超线程(Hyper-Threading)系统中,在段落中加入:

    代码: 全选

    ### lock threads to cores
    可以将该段落的进程锁定到某个CPU核心/线程中。具体分配到哪个核心/线程由轮替(round-robin)决定。
  7. MP_Pipeline的内置常量:MP_PIPELINE_BRANCH_ID
    值为当前BRANCH的编号。可以这样使用:

    代码: 全选

    Subtitle(string(MP_PIPELINE_BRANCH_ID), y=30)
    有助于调教调试脚本。
四、Q&A
  • Q:最后一个Block写在MP_Pipeline(""" """)里面和外面有什么区别?
    A:貌似没有区别。经实测:

    代码: 全选

    MP_Pipeline("""
    
        ColorBars().ConvertToYV12
    
    ### ###
    
        Invert()
    
    """)
    

    代码: 全选

    MP_Pipeline("""
    
        ColorBars().ConvertToYV12
    
    ### ###
    
    """)
    
    Invert()
    
    等效。
  • Q:如何锁定线程到指定核心?
    A:不知道。大概不能指定核心。
  • Q:是否有其他隐藏功能?
    A:不知道,或许有。
  • Q:是否有其他隐藏常量/变量?
    A:不知道,或许有。
  • Q:喂!这算哪门子Q&A啊!!
    A:╮(¬_¬)╭萨……

Re: MP_Pipeline教程

发表于 : 2012-09-27 22:28
sammysui
重量级文章 条理清晰 受益匪浅 对于很多初级 中级 压片爱好者来说太好了,造福大家。绝对的支持。。。TGMC的速度实在是巨慢。。。正好需要这个。。。

Re: MP_Pipeline教程

发表于 : 2012-09-28 1:00
lwjkk666
还能这样用呢,受教了,看了这个教程之后之前的那个脚本还得改改,支持原创教程。
PS:之前用了个AAD滤镜,不加里面Import("D:\x264_rev2208+677_tMod-Libav\tools\AAD_v0.1.5a\AAD_015a.avs")会报错。。看了教程果然除了LoadPlugin还需要输入avs。还有,16G内存。。膜拜

Re: MP_Pipeline教程

发表于 : 2012-09-28 17:49
gryphonheart
这太牛了,4核破A卡用MCTD、FFT3DGPU(sigma=2+)、NLMeansCL经常崩,直接分核心太好用了,感谢巨莴 {:cat_16}

XP压1080P的HI10P,MCTD占一核心 ,撸马碉堡占一核心,其他占一核心,CPU100%,内存89%,能跑3.3FPS,总比无限崩要强多了

Re: MP_Pipeline教程

发表于 : 2012-10-05 13:03
-o-o-304-o-o-
拜谢虾大,顺便爱特皮神把教程内置了吧……造福大众……

Re: MP_Pipeline教程

发表于 : 2012-10-05 13:06
dgwxx
球翻译成英语造福国际友人(噗

Re: MP_Pipeline教程

发表于 : 2013-06-30 23:51
kenneth104
不当摊手党!!
发现1pass的时候,avs4x264跑得不够x264块,CPU只用了60%

随便搞鼓了一下MP,发现比原来更慢了!!
还得继续优化XD

Re: MP_Pipeline教程

发表于 : 2013-07-01 10:35
dgwxx
kenneth104 写了:不当摊手党!!
发现1pass的时候,avs4x264跑得不够x264块,CPU只用了60%

随便搞鼓了一下MP,发现比原来更慢了!!
还得继续优化XD
MP_Pipeline在根本上是为了解决内存问题而设计的,客观上对某些滤镜和脚本有一定加速作用,不过对脚本、滤镜有一定选择,并非所有情况都能加速。
您没写x264的参数也不贴出来avs脚本就说MP_Pipeline优化不够,这是缺乏根据的,也无助于解决问题。

记得原作者多次提到过,判断速度的标准是fps,而非CPU占用率。比如压缩的时候跑到0.5fps,那说明这个脚本这个系统上只能跑到0.5fps。如果用了MP_pipeline之后CPU占用率从60%上升到90%而速度还不到0.5fps,那只能说多出来这30%的占用率无助于提速。