MT
摘要 | |
作者 | tsp |
バージョン | 0.7 |
ダウンロード | AviSynth Wiki - スクリプト関数を参照。 |
カテゴリ | メタフィルタ |
必要条件 | なし |
ライセンス | GPL v2 |
MT 0.7 †
MT(clip clip,string filter,int threads,int overlap,bool splitvertical) MTi(clip clip,string filter) MTsource(string filter,int delta,int threads)
摘要 †
MT は、他のフィルタをマルチスレッドで走らせることを可能にするフィルタです。うまく行けば、ハイパースレッド/マルチコアのプロセッサやマルチプロセッサのシステム上で処理を高速化するはずです。
重要: CPU の使用率ではなくスピードの向上を見て結果を判定することをつねに忘れないでください。
質問する前に、詳しくは MT support page も参照してください。
技術情報 †
MT は、フレームを個々のスレッドで処理されるより小さな断片に分割して、マルチプロセッサまたはハイパースレッドが使用可能なコンピュータの最大限の利用を可能にするフィルタです。Celeron 400 MHz を 2 基搭載した古い ABIT BP6 というマザーボードでテストしたところ、40% 速度が向上しました。AVS スクリプト処理時(すなわち DivX や XviD にエンコードしている場合)の CPU 使用率がすでに 100% なら、このフィルタを使う必要はありません。
このフィルタは、次の AVS 関数のように動作します:
function PseudoMT(clip c,string filter) { a=eval("c.crop(0,0,src.width/2,src.height)."+filter) b=eval("c.crop(src.width/2,0,src.width/2,src.height)."+filter) stackhorizontal(a,b) }
唯一の違いは、a と b が並行して実行され、フレームを 3 つ以上の断片に分割することが可能であるということです。上記のスクリプトで動作するフィルタなら、そのフィルタスレッドセーフであれば MT でも動作するはずです。Dust プラグインは上記のスクリプトでは動作しません。そのため、もし iip 関数を使いたい場合は別のノイズ除去フィルタを使うか、Steady*1 にバグを修正してもらってください。
制限
MT で走らせるフィルタは、入力クリップを 1 つのみ(すなわち last*2 を)受け入れるものでなければなりません。またそのフィルタは、(スマートデインターレースフィルタのように)フレーム全体の内容に依存するものであってはいけません。さもなければ、フレームの一部分だけが処理される恐れがあります。またそのフィルタは、スレッドセーフでもなければなりません。ほとんどのフィルタはスレッドセーフですが、中には間違った結果を生じさせたりクラッシュしたりするものもあります。
インストール
mt.dll を AviSynth プラグインディレクトリにコピーし、同梱されている avisynth.dll を windows\system32 ディレクトリまたは avisynth.dll が置かれているところにコピーしてください。もし AviSynth 2.6 をインストールしているのでなければ、古い avisynth.dll を(リネームするなどして)バックアップするのを忘れないでください。
バージョン 0.7 から、2 つの別のフィルタも収録されています:
- MTi(): スレッドを 2 つ作成し、次の AVS 関数のようにフィールドを組み合わせる前に各スレッドに 1 つのフィールドを処理させます。
function PseudoMTi(clip c,string filter) { a=eval("c.AssumeFieldBased().SeparateFields.selecteven()."+filter) b=eval("c.AssumeFieldBased().SeparateFields.selectodd()."+filter) interleave(a,b).weave() }
別の疑似スクリプトのように、a と b は並行して実行されます。スレッドは 2 つのみ生成されるため、2 つの(仮想)コアのみ使用します。
- MTsource(): ソースフィルタをマルチスレッドで走らせるために使われます。次のように動作します:
function PseudoMTsource(string filter) { SetMTmode(2) eval(filter) SetMtmode(0) }
他の 2 つのフィルタとは異なり、前もってフレームを取得し、高速な検索のためにキャッシュに格納する時間軸フィルタです。
シンタックス †
MT †
MT(clip clip,string filter,int threads,int overlap,bool splitvertical)
すべてのパラメータは名前付けされています。関数のパラメータは以下のとおりです:
clip clip = last
入力クリップ
filter string = デフォルトなし
マルチスレッドで走らせるフィルタ。フレームの高さと幅の両方を変更してはなりません(色空間は可)。入力クリップは 1 つのみ許容されます。これらの制限が守られる限り、どんな内蔵フィルタやユーザー定義関数や外部プラグインフィルタでもかまいません。
threads int = 2
走らせるスレッドの数。あなたのコンピュータで同時に走らせることが可能なスレッドの数に設定してください。
overlap int = 0
上下の縁や左右の縁に付け足されるピクセルの数。フレームが分割されるところで人工ノイズが見られる場合は、この値を増やしてください。
splitvertical bool = false
true なら、フレームは垂直に分割されます(filter は高さを変更することが可能になります)。さもなければ、水平に分割されます(filter は幅を変更することが可能)。
MTi †
MTi(clip clip,string filter)
すべてのパラメータは名前付けされています。関数のパラメータは以下のとおりです:
clip clip = last
入力クリップ。RGB と YUY2 色空間の場合は高さが 2 の倍数、YV12 色空間の場合は高さが 4 の倍数でなければなりません。
filter string = デフォルトなし
マルチスレッドで走らせるフィルタ。同時に幅と高さの両方を変更することは可能ですが、許容される入力クリップは 1 つのみです。これらの制限が守られる限り、どんな内蔵フィルタやユーザー定義関数や外部プラグインフィルタでもかまいません。
MTsource †
MTsource(string filter,int delta,int threads,int max_fetch)
すべてのパラメータは名前付けされています。関数のパラメータは以下のとおりです:
filter string = デフォルトなし
マルチスレッドで走らせるソースフィルタ。現時点では、(DirectShowSource、Avisource、MPEG2Source のような)AviSynth 内蔵のソースフィルタと外部プラグインのソースフィルタのみサポートされています。ユーザー定義関数やソースフィルタ以外のフィルタを使用することもできますが、クラッシュしたり、フレームの破損を引き起こすかもしれません。
delta int = 1
各フレームリクエストの間に何フレーム存在するかを指定します。そのため、もし 2 フレームごとに読み込むつもりなら、2 に設定してください。あるいは、過去方向にさかのぼってフレームを読み込むなら -1 に設定してください。SelectEvery(10,3,6,7) のようにより複雑なフレームアクセスパターンはサポートされていません(しかし、リクエストされたフレームがキャッシュの中にあるときはとにかく動作するかもしれません。キャッシュにリクエストされていないフレームからの無駄なメモリが存在するだけです)。
threads int = 2
走らせるスレッドの数。あなたのコンピュータが同時に走らせることが可能なスレッドの数に設定してください。
max_fetch int = 30
現在リクエストされているフレームより先に MTsource が取得するフレーム数の最大値です。低く設定すると、ほとんどの時間スレッドをアイドル状態にします。高く設定すると、多くのメモリを無駄にします。
使用例 †
通常の blur:
MT("blur(1)",2,2)
ユーザー定義関数も(variableblur 使用):
MT("unsharp(2,0.7)",2,2) function unsharpen(clip c,float variance,float k) { blr=binomialBlur(c,vary=variance,varc=2,Y=3,U=2,V=2) return yv12lutxy(blr,c,"y x - "+string(k)+" * y +",y=3,u=2,v=2) }
これは意図した結果を生みませんが、トリプルクウォートの使い方を示しています:
MT(""" subtitle("Doh") """,4,0)
MTi の使用例
MTi("fft3dfilter()")
次のスクリプトとほぼ同じ結果になります
MT("fft3dfilter(interlaced=true)",threads=2)
ネイティブにインターレースのコンテンツをサポートしていなフィルタに関しては、MTi() を使ったほうが簡単です。
MTsource() の使用例
ir=MTSource(""" imagereader("c:\test.png") """,delta=1,threads=2,max_fetch=10) as=MTSource(""" avisource("c:\test.avi") """,delta=-1) # reverse() のために delta を負の値に ms=MTSource(""" MPEG2Source("c:\test.d2v") """,delta=9) # selectevery(9,1) のために delta は 9 stackhorizontal(ir.trim(0,100),as.reverse().trim(0,100),ms.selectevery(9,1).trim(0,100))
更新履歴 †
- 0.1 最初のリリース。
- 0.2 よりスレッドセーフになったはず。
- 0.21 Sleep(0) をコメントアウトするのを忘れた。
- 0.25 splitvertical オプションを追加。
- 0.3 より安定(そしてより低速)
- 0.4 速度をアップさせる avisynth 2.56 beta のカスタム版を同梱。
- 0.41 わずかな速度向上。
- 0.5 同梱されている修正版 avisynth 2.5.6 または avisynth 2.6 が必要となる。
- 0.6 バグ修正: クラッシュせずに splitvertical=true で高さが変更可能に。修正版 avisynth MT 2.5.7.3 も同梱。
- 0.7 2 つの新しいフィルタ: MTi()、 MTsource() と Avisynth MT 2.5.7.5
修正版 avisynth MT 2.5.7.5 †
摘要 †
2 つの新しい関数 SetMTMode() と GetMTMode() を含み、MT.dll からも必要とされる。c:\windows\system32 にある avisynth.dll を上書きすることによってインストールしてください(まず古いファイルのバックアップを取ることを忘れないでください)。
技術情報 †
これらの関数は、フィルタを処理するときに、AviSynth に 2 つ以上のスレッドを使うことを可能にします。2 つ以上の CPUかコア、またはハイパースレッディングを持っている場合に役立ちます。この機能はまだ実験的です。
シンタックス †
GetMTMode(bool threads)
threads bool = false
true なら、GetMTMode は使用されるスレッドの数を返します。さもなければ、現在のモードが返されます(下記参照)。
SetMTmode(int mode,int threads)
時間軸の(すなわち 2 フレーム以上が同時に処理される)マルチスレッディングを有効にするために AVS ファイルの 1 行目にこれを置いてください。これ以降のフィルタのモードを変更するためにはスクリプトの後半で使用してください。
mode int (デフォルト 2。1-6)
1 から 6 までの 6 つのモードが存在します。
- モード 1 は最も高速ですが、ごくわずかなフィルタでのみ動作します。
- モード 2 はほとんどのフィルタで動作しますが、より多くのメモリを使用します。
- モード 3 はモード 2 で動作しないフィルタのいくつかで動作するはずですが、より低速です。
- モード 4 はモード 2 とモード 3 の組み合わせで、さらに多くのフィルタで動作するはずですが、より低速でかつより多くのメモリを使用します。
- モード 5 は最も低速(SetMTMode を使わない場合よりも遅い)ですが、直線的なフレームサービング(すなわち、フレームが 0,1,2 ... 最終といった順番に来る)を要求しないすべてのフィルタで動作するはずです。
- モード 6 はモード 5 を修正したもので、わずかに速いかもしれません。
さまざまなモードのさらに詳しい説明はこちらで読むことができます: MT modes explained - Avisynth(Internet Archive)
threads int = 0
使用するスレッドの数。利用可能なプロセッサの数に設定するには 0 に設定してください。スレッドの数を最初の SetMTMode で指定した以外の値に変更することはできません。
使用例: †
SetMTMode(2,0) # 「thread = 利用可能なプロセッサの数」かつモード 2 を使ってマルチスレッディングを有効化 LoadPlugin("...\LoadPluginEX.dll") # avisynth 2.0 プラグインの読み込みに必要 LoadPlugin("...\DustV5.dll") # Pixiedust の読み込み import("limitedsharpen.avs") src=AVIsource("test.avi") SetMTMode(5) # 以下の行のモードを 5 に変更 src=src.converttoyuy2().PixieDust() # Pixiedust を機能させるにはモード 5 にする必要がある SetMTMode(2) # モードを 2 に再変更 src.LimitedSharpen() # なぜなら、LimitedSharpen はモード 2 のときにうまく動作するから subtitle("Number of threads used: "+string(GetMTMode(true))+" Current MT Mode: "+string(GetMTMode())) # モードと使用中のスレッドの数を表示
How to development threadsafe filters(未訳) †
Filter construction/destruction is single threaded. Only calls to GetFrame are multithreaded. No linear frame order is assured(unless MT is used instead of setmtmode) and for each mode there are different restrictions:
- Mode 1: all access to class variables, global variables and static variables most be threadsafe by using appropriate locking(Enter/LeaveCriticalSection etc, no locking need for readonly variables) because more than 1 thread may access a class instance at a time.
- Mode 2: access to class variable doesn't have to be threadsafe because there is only 1 instance of the class per thread. All global/static variable access must be threadsafe. Because each class instance only process every other frame internal caches(that is a cache inside the filter) wouldn't work well. I have created PClipLocalStorage to share a pointer between different filter instances.
- Mode 3: Only 1 thread is allowed to execute code from the filter at the same time.When child->GetFrame is called another thread can enter the filter and execute code. That means that class variables/global variables/static variables shouldn't be assigned to any values before after the lastchild->GetFrame has been called. Instead local function variables should be used like this:
PVideoFrame __stdcall AdjustFocusV::GetFrame(int n, IScriptEnvironment* env) { PVideoFrame frame = child->GetFrame(n, env); //Assigned to a local variable so this will work in mode 3 env->MakeWritable(&frame); if (!line) line = new uc[frame->GetRowSize()+32]; uc* linea = (uc*)(((int)line+15) & -16);// Align 16 uc* buf = frame->GetWritePtr(); int pitch = frame->GetPitch(); int row_size = vi.RowSize(); int height = vi.height; memcpy(linea, buf, row_size); // First row - map centre as upper if ((pitch >= ((row_size+7) & -8)) && (env->GetCPUFlags() & CPUF_MMX)) { AFV_MMX(linea, buf, height, pitch, row_size, amount); } else { AFV_C(linea, buf, height, pitch, row_size, amount); } return frame; }
But not like this:
PVideoFrame TemporalSoften::GetFrame(int n,IScriptEnvironment* env) { __int64 i64_thresholds = 0x1000010000100001i64; int radius = (kernel-1) / 2 ; int c= 0; // Just skip if silly settings if((!luma_threshold)&& (!chroma_threshold) || (!radius)) return child->GetFrame(n,env); for(int p= 0;p<16;p++) planeDisabled[p]=false; for(p= n-radius;p<=n+radius;p++) { frames[p+radius-n] = child->GetFrame(min(vi.num_frames-1,max(p,0)), env); //GetFrame assigned to class variable frames. This wouldn't work with Mode 3 //because the next thread that enters this getframe will overwrite the result // from the last thread } //do stuff }
but when using mode 3 there is no need for threadsafe access to class variables. And because there is only 1 instance of the class that process all frames internal caches will work much better. The bad thing is only 1 thread can execute the filter at a time so if it's the only slow filter in the script the speed increase wouldn't be that big.
- Mode 4: a combination of mode 2 and 3 so it's okay to assign class variables before the lastchild->getframe has been called because there is a class instance per thread but the problem with internal cache is the same as mode 2
- Mode 5: No restrictions.
- Mode 6: A slightly modified version of mode 5 that might be a little faster.
PClipLocalStorage
Here is an example on how the PClipLocalStorage can be used to share a cache between multiple instances(that are created with mode=2,4):
class Cache { public: //These function should be threadsafe. The most simple way is to use a //critical section like this PVideoFrame GetCachedFrame(int framenumber) { EnterCriticalSection(&cs); //Code //... LeaveCriticalSection(&cs); return retval; } SetCachedFrame(PVideoFrame frame); private: CRITICAL_SECTION cs; } class Sample : public GenericVideoFilter{ public: Sample(PClip _child, IScriptEnvironment* env); ~Sample(); PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env); protected: PClipLocalStorage cls; Cache* FrameCache; } Sample::Sample(PClip _child, IScriptEnvironment* env) : GenericVideoFilter(_child),cls(env) { //if the cache has not been created yet GetValue will return 0 if(cls->GetValue()==0) { //create the cache and save the address in the PClipLocalStorage FrameCache = new Cache();cls->SetValue(static_cast(FrameCache)); } // The cache has been created so assign the address to FrameCache else { FrameCache=static_cast(cls->GetValue()); } } Sample::~Sample() { //only delete FrameCache if it is not delete yet. if(cls->GetValue()!=0) { delete FrameCache; cls->SetValue(0);//Signal that the cache is deleted } }
リンク †
Doom9's Forum 上の公式スレッド。助けを求める前にこのページと下記のサポートページを読んでください。どうもありがとう。
MT support page - Avisynth(Internet Archive)(日本語訳)
註: このページは、MT - Avisynth wiki の日本語訳です。翻訳時点における原文の最終更新日時は、20:41, 1 March 2007 です。