ルヌプフラッキングに察応

画像

この䜜業の目的は、サむクルを最適化するための別の手法を指定するこずです。 同時に、既存のアヌキテクチャに焊点を合わせおも問題はありたせんが、反察に、䞻に垞識に頌っお、できるだけ抜象的に行動しようずしたす。

著者は、この手法を、「 loops unrolling 」や「loops nesting 」などの類掚によっお「ルヌプをフラッキングする」ず呌びたした。 さらに、この甚語は意味を反映しおおり、忙しくない。

サむクルは最適化の䞻芁なオブゞェクトであり、ほずんどのプログラムがほずんどの時間を費やすサむクルです。 十分な数の最適化手法がありたす。 ここでそれらに粟通するこずができたす 。

最適化のための䞻芁なリ゜ヌス


  1. サむクルを終了するロゞックの節玄。 ルヌプを終了するための基準をチェックするず、分岐が発生し、分岐が「パむプラむンを壊したす」ので、あたり頻繁にチェックしないようにしたしょう。 結果は、 Dufのdeviceなどの玠敵なコヌドサンプルです。

    void send(int *to, int *from, int count) { int n = (count + 7) / 8; switch (count % 8) { case 0: do { *to = *from++; case 7: *to = *from++; case 6: *to = *from++; case 5: *to = *from++; case 4: *to = *from++; case 3: *to = *from++; case 2: *to = *from++; case 1: *to = *from++; } while (--n > 0); } } 

    珟時点では、プロセッサの遷移の予枬子 存圚する堎合により、このような最適化は無効になっおいたす。
  2. ブラケットからルヌプ䞍倉量を削陀する 巻き䞊げ 。
  3. メモリのキャッシュのより効果的な䜜業のためのメモリを䜿甚した䜜業の最適化。 サむクル内で明らかにキャッシュサむズを超えるメモリ量の呌び出しがある堎合、これらの呌び出しの順序で重芁になりたす。 明らかな堎合に加えお、コンパむラヌがこれに察凊するこずは困難です;時々、効果を達成するために、別のアルゎリズムが実際に曞かれおいたす。 したがっお、この最適化は、適甚されるプログラマの肩にかかっおいたす。 そしお、コンパむラ/プロファむラは統蚈を提䟛し、ヒントを䞎えたす...フィヌドバック。
  4. 明瀺的たたは暗黙的プロセッサ䞊列凊理を䜿甚したす。 最新のプロセッサは、コヌドを䞊行しお実行できたす。

    明瀺的な䞊列アヌキテクチャ EPIC 、 VLIW の堎合、1぀の呜什に、異なる機胜ブロックに圱響を䞎える耇数の呜什䞊列で実行されるを含めるこずができたす。

    スヌパヌスカラヌプロセッサは、独立しお呜什のフロヌを解析し、䞊列凊理を探し出し、可胜な限りそれを䜿甚したす。

    画像
    コマンドのスヌパヌスカラヌ実行の抂略図

    別のオプションは、ベクトル挔算SIMDです。

珟圚、プロセッサの䞊列凊理を最倧限に掻甚する方法を探しおいたす。

䜕がありたすか


はじめに、実隓のためにIntel Core-i7 2600プロセッサヌでMSVS-2013x64を䜿甚したいく぀かの簡単な䟋を芋おみたしょう。 ちなみに、GCCはいずれにしおも、このような単玔な䟋でも同じこずを行うこずができたす。

最も単玔なルヌプは、敎数配列の合蚈を蚈算するこずです。

 int64_t data[100000]; 
 int64_t sum = 0; for (int64_t val : data) { sum += val; } 

コンパむラが䜜成するものは次のずおりです。

  lea rsi,[data] mov ebp,186A0h ;100 000 mov r14d,ebp ... xor edi,edi mov edx,edi nop dword ptr [rax+rax] ;  loop_start: add rdx,qword ptr [rsi] lea rsi,[rsi+8] dec rbp jne loop_start 

ダブルず同じAVX、 / fp正確 / fp厳栌-ANSI互換性

  vxorps xmm1,xmm1,xmm1 lea rax,[data] mov ecx,186A0h nop dword ptr [rax+rax] loop_start: vaddsd xmm1,xmm1,mmword ptr [rax] lea rax,[rax+8] dec rcx jne loop_start 

このコヌドは、85秒で100䞇回実行されたす。

ここでは、䞊列凊理を識別するためのコンパむラヌの䜜業は芋られたせんが、タスクでは明らかなように芋えたす。 コンパむラはデヌタの䟝存関係を怜出し、それを回避できたせんでした。

同じ、ただしAVX、/ fp高速-ANSI互換性なし

  vxorps ymm2,ymm0,ymm0 lea rax,[data] mov ecx,30D4h ; 12500, 1/8 vmovupd ymm3,ymm2 loop_start: vaddpd ymm1,ymm3,ymmword ptr [rax+20h] ; SIMD vaddpd ymm2,ymm2,ymmword ptr [rax] lea rax,[rax+40h] vmovupd ymm3,ymm1 dec rcx jne loop_start vaddpd ymm0,ymm1,ymm2 vhaddpd ymm2,ymm0,ymm0 vmovupd ymm0,ymm2 vextractf128 xmm4,ymm2,1 vaddpd xmm0,xmm4,xmm0 vzeroupper 

26秒かかり、ベクトル挔算が䜿甚されたす。

同じルヌプですが、埓来のCスタむルの堎合

 for (i = 0; i < 100000; i ++) { sum += data[i]; } 

/ fpprecisionで予期せずに取埗したす。

  vxorps xmm4,xmm4,xmm4 lea rax,[data+8h] lea rcx,[piecewise_construct+2h] vmovups xmm0,xmm4 nop word ptr [rax+rax] loop_start: vaddsd xmm0,xmm0,mmword ptr [rax-8] add rax,50h vaddsd xmm1,xmm0,mmword ptr [rax-50h] vaddsd xmm2,xmm1,mmword ptr [rax-48h] vaddsd xmm3,xmm2,mmword ptr [rax-40h] vaddsd xmm0,xmm3,mmword ptr [rax-38h] vaddsd xmm1,xmm0,mmword ptr [rax-30h] vaddsd xmm2,xmm1,mmword ptr [rax-28h] vaddsd xmm3,xmm2,mmword ptr [rax-20h] vaddsd xmm0,xmm3,mmword ptr [rax-18h] vaddsd xmm0,xmm0,mmword ptr [rax-10h] cmp rax,rcx jl loop_start 

䞊列凊理はありたせん。メンテナンスサむクルを節玄するための詊みです。 このコヌドは87秒間実行されたす。 / fpの堎合高速コヌドは倉曎されおいたせん。

ルヌプのネストを䜿甚しおコンパむラヌに䌝えたしょう。

 double data[100000]; 
 double sum = 0, sum1 = 0, sum2 = 0; for (int ix = 0; i < 100000; i+=2) { sum1 += data[i]; sum2 += data[i+1]; } sum = sum1 + sum2; 

芁求したずおりの結果が埗られ、コヌドは/ fpfastおよび/ fpexactオプションず同じです。 䞀郚のプロセッサヌAMD BulldozerでのVaddsd操䜜は、䞊行しお実行できたす。

  vxorps xmm0,xmm0,xmm0 vmovups xmm1,xmm0 lea rax,[data+8h] lea rcx,[piecewise_construct+2h] nop dword ptr [rax] nop word ptr [rax+rax] loop_start: vaddsd xmm0,xmm0,mmword ptr [rax-8] vaddsd xmm1,xmm1,mmword ptr [rax] add rax,10h cmp rax,rcx jl loop_start 

このコヌドは43秒で数癟䞇回実行され、「単玔で正確な」アプロヌチの2倍の速床です。

4぀の芁玠のステップで、コヌドは次のようになりたすコンパむラオプション/ fpfast/ fppreciseでも同じです

  vxorps xmm0,xmm0,xmm0 vmovups xmm1,xmm0 vmovups xmm2,xmm0 vmovups xmm3,xmm0 lea rax,[data+8h] lea rcx,[piecewise_construct+2h] nop dword ptr [rax] loop_start: vaddsd xmm0,xmm0,mmword ptr [rax-8] vaddsd xmm1,xmm1,mmword ptr [rax] vaddsd xmm2,xmm2,mmword ptr [rax+8] vaddsd xmm3,xmm3,mmword ptr [rax+10h] add rax,20h cmp rax,rcx jl loop_start vaddsd xmm0,xmm1,xmm0 vaddsd xmm1,xmm0,xmm2 vaddsd xmm1,xmm1,xmm3 

このコヌドは34秒で100䞇回実行されたす。 ベクトルコンピュヌティングを保蚌するには、次のようなさたざたなトリックを䜿甚する必芁がありたす。

  1. プラグマの圢匏でコンパむラヌにヒントを蚘述したす。 pragma ivdep  #pragma loopivdep 、 pragma GCC ivdep 、pragma vector always、pragma omp simd ...
  2. 組み蟌み 'ず-䜿甚する呜什をコンパむラに指瀺したす。たずえば、2぀の配列を合蚈するず次のようになりたす 。

どういうわけか、これはすべお「高氎準蚀語」の明るいむメヌゞにはあたり合いたせん。

䞀方では、必芁に応じお、結果を埗るために、これらの最適化はたったく負担になりたせん。 䞀方、移怍性の問題が発生したす。 4぀の加算噚を備えたプロセッサ甚にプログラムが䜜成され、デバッグされたずしたす。 次に、6個の加算噚を備えたプロセッサバヌゞョンで実行しようずするず、期埅どおりのゲむンが埗られたせん。

たた、3぀のバヌゞョンでは、4分の1ではなく2倍の速床䜎䞋が発生したす。

最埌に、平方の合蚈を蚈算したす/ fp正確

  vxorps xmm2,xmm2,xmm2 lea rax,[data+8h] ; pdata = &data[1] mov ecx,2710h ; 10 000 nop dword ptr [rax+rax] loop_start: vmovsd xmm0,qword ptr [rax-8] ; xmm0 = pdata[-1] vmulsd xmm1,xmm0,xmm0 ; xmm1 = pdata[-1] ** 2 vaddsd xmm3,xmm2,xmm1 ; xmm3 = 0 + pdata[-1] ** 2 ; sum vmovsd xmm2,qword ptr [rax] ; xmm2 = pdata[0] vmulsd xmm0,xmm2,xmm2 ; xmm0 = pdata[0] ** 2 vaddsd xmm4,xmm3,xmm0 ; xmm4 = sum + pdata[0] ** 2 ; sum vmovsd xmm1,qword ptr [rax+8] ; xmm1 = pdata[1] vmulsd xmm2,xmm1,xmm1 ; xmm2 = pdata[1] ** 2 vaddsd xmm3,xmm4,xmm2 ; xmm3 = sum + pdata[1] ** 2 ; sum vmovsd xmm0,qword ptr [rax+10h] ; ... vmulsd xmm1,xmm0,xmm0 vaddsd xmm4,xmm3,xmm1 vmovsd xmm2,qword ptr [rax+18h] vmulsd xmm0,xmm2,xmm2 vaddsd xmm3,xmm4,xmm0 vmovsd xmm1,qword ptr [rax+20h] vmulsd xmm2,xmm1,xmm1 vaddsd xmm4,xmm3,xmm2 vmovsd xmm0,qword ptr [rax+28h] vmulsd xmm1,xmm0,xmm0 vaddsd xmm3,xmm4,xmm1 vmovsd xmm2,qword ptr [rax+30h] vmulsd xmm0,xmm2,xmm2 vaddsd xmm4,xmm3,xmm0 vmovsd xmm1,qword ptr [rax+38h] vmulsd xmm2,xmm1,xmm1 vaddsd xmm3,xmm4,xmm2 vmovsd xmm0,qword ptr [rax+40h] vmulsd xmm1,xmm0,xmm0 vaddsd xmm2,xmm3,xmm1 ; xmm2 = sum; lea rax,[rax+50h] dec rcx jne loop_start 

コンパむラヌは、サむクルのロゞックを節玄するためにサむクルを10個の芁玠に分割したすが、5぀のレゞスタ合蚈1぀ず乗算の2぀の䞊列分岐ごずのペアがかかりたす。

たたは/ fpの堎合fast

  vxorps ymm4,ymm0,ymm0 lea rax,[data] mov ecx,30D4h ;12500 1/8 loop_start: vmovupd ymm0,ymmword ptr [rax] lea rax,[rax+40h] vmulpd ymm2,ymm0,ymm0 ; SIMD vmovupd ymm0,ymmword ptr [rax-20h] vaddpd ymm4,ymm2,ymm4 vmulpd ymm2,ymm0,ymm0 vaddpd ymm3,ymm2,ymm5 vmovupd ymm5,ymm3 dec rcx jne loop_start vaddpd ymm0,ymm3,ymm4 vhaddpd ymm2,ymm0,ymm0 vmovupd ymm0,ymm2 vextractf128 xmm4,ymm2,1 vaddpd xmm0,xmm4,xmm0 vzeroupper 

芁玄衚

MSVC、/ fp厳密、/ fp正確、秒MSVC、/ fp高速、秒
foreach8526
Cスタむルのルヌプ8726
CスタむルのネストX24343
CスタむルのネストX43434

これらの数字を説明するには

プロセッサの開発者だけが䜕が起こっおいるかの本圓の状況を知っおおり、掚枬しかできないこずに泚意する䟡倀がありたす。

加速はいく぀かの独立した加算噚によるものであるずいう最初の考えは明らかに誀りです。 i7-2600プロセッサには、独立したスカラヌ挔算を実行できないベクトル加算噚が1぀ありたす。

プロセッサのクロック速床は最倧3.8 GHzです。 85秒の単玔なサむクル100䞇回、100,000回の远加で、反埩あたり3クロックサむクルが埗られたす。 これは、vaddpdベクトル呜什の実行の3クロックサむクルのデヌタ 1、2 ずよく䞀臎しおいたすスカラヌを远加した堎合でも。 デヌタに䟝存しおいるため、3クロックサむクルより速く反埩を完了するこずはできたせん。

ネストX2の堎合、反埩内のデヌタに䟝存せず、サむクルの違いで加算噚パむプラむンをロヌドできたす。 しかし、次のむテレヌションでは、デヌタの䟝存関係もサむクルの違いで珟れたす。その結果、加速が2倍になりたす。

ネスティングX4の堎合、加算コンベダヌもビヌト単䜍でロヌドされたすが、コンベダヌの長さによる3倍の加速は発生せず、远加の芁因が介圚したす。 たずえば、ルヌプの反埩がキャッシュラむンL0mに収たりなくなり、空きクロックサむクルsを受け取りたす。

だから


コンパむラに぀いお少し


レゞスタアヌキテクチャは、高レベル蚀語のポヌタブルテキストから受け入れ可胜なコヌドを取埗するためのシンプルで普遍的な方法を提䟛したす。 コンパむルは条件付きでいく぀かのステップに分割できたす。

  1. 解析 この段階で、構文的に制埡された倉換が実行され、静的チェックが実行されたす。 出力には、解析ツリヌ DAG がありたす。
  2. 䞭間コヌド生成。 オプションで、䞭間コヌド生成を解析ず組み合わせるこずができたす。
    たた、 3アドレス呜什を䞭間コヌドずしお䜿甚する堎合、「 3アドレスコヌドは構文ツリヌたたはDAGの線圢化された衚珟であり、明瀺的な名前はグラフの内郚ノヌドに察応する 」ため、この手順は簡単になりたす 。

    本質的に、3アドレスコヌドは、無限の数のレゞスタを持぀仮想プロセッサ甚です。

  3. コヌド生成。 このステップの結果は、タヌゲットアヌキテクチャ甚のプログラムです。 レゞスタの実際の数は限られおいるため、この段階で、各䞀時レゞスタにどの䞀時倉数を含めるかを決定し、特定のレゞスタに分散する必芁がありたす。 玔粋な圢匏であっおも、このタスクはNP完党であり、さらに、レゞスタの䜿甚には通垞さたざたな制限があるため、問題は耇雑です。 ただし、この問題を解決するために、蚱容可胜なヒュヌリスティックが開発されたした。 さらに、3アドレスたたは同等のコヌドは、デヌタストリヌムの分析、最適化、䞍芁なコヌドの削陀などの正匏な装眮を提䟛したす。

問題が迫っおいたす

  1. レゞスタ割り圓おのNP完党問題を解決するために、ヒュヌリスティックが䜿甚され、これにより蚱容可胜な品質のコヌドが埗られたす。 これらのヒュヌリスティックは、メモリたたはレゞスタの䜿甚に関する远加の制限を奜みたせん。 たずえば、むンタヌレヌスメモリ、呜什でのレゞスタの暗黙的な䜿甚、ベクトル挔算、レゞスタリング...ヒュヌリスティックが動䜜を停止し、最適に近いコヌドの構築を停止できる皋床には、普遍的な方法で解決できる問題はなくなりたす。

    その結果、ベクトルプロセッサ機胜は、コンパむラがトレヌニングされたセットから兞型的な状況を認識した堎合にのみ䜿甚できたす。
  2. スケヌリングの問題。 レゞスタの割り圓おは静的に行われたす。同じシステムの呜什を䜿甚しおプロセッサ䞊でコンパむルされたコヌドを実行しようずするず、倚数のレゞスタを䜿甚しおもゲむンは埗られたせん。

    これは、レゞスタりィンドりのスタックを備えたSPARCにも圓おはたりたす。レゞスタりィンドりの数が倚いほど、呌び出しフレヌムの数が倚くなり、メモリアクセスの頻床が枛るずいう事実になりたす。

    EPIC-スケヌリングの方向で詊みが行われたした-「耇数の呜什の各グルヌプはバンドルず呌ばれたす。 各バンドルには、次のグルヌプがこの結果に䟝存するこずを瀺すストップビットがありたす。 このビットを䜿甚するず、耇数のバンドルを䞊行しお実行できる機胜を備えた次䞖代のアヌキテクチャを䜜成できたす。 䟝存関係情報はコンパむラヌによっお蚈算されるため、機噚はオペランドの独立性の远加怜蚌を実行する必芁はありたせん。」独立したバンドルを䞊列で実行でき、システム内の実行デバむスが倚いほど、プログラムの内郚䞊列性が広くなるず想定されおいたした。 䞀方で、これらの機胜は垞に勝぀ずは限りたせん。いずれにせよ、配列の合蚈に぀いおは、著者にずっおは圹に立たないようです。

    スヌパヌスカラヌプロセッサは、「私たちのための登録」ず「私たち自身の登録」を導入するこずで問題を解決したす。 コンパむラヌは、レゞスタヌをペむント割り振りするずきの最初のコンパむラヌの数によっおガむドされたす。 2番目の数は任意で、通垞は最初の数よりも数倍倚くなりたす。 デコヌド䞭、スヌパヌスカラヌプロセッサは、プログラム本䜓のりィンドり内の実際の番号に基づいおレゞスタを再登録したす。 りィンドりサむズは、プロセッサが凊理できるロゞックの耇雑さによっお決たりたす。 もちろん、レゞスタの数に加えお、機胜デバむスもスケヌリングの察象ずなりたす。
  3. 互換性の問題。 特にX84-64ずテクノロゞヌラむンに泚目しおください-SSE-SSE2-SSE3-SSSE3-SSE4-AVX-AVX2-AVX512-...

    トップダりン互換性぀たり、コヌドは叀いテクノロゞヌ甚にコンパむルされおいたすが、より若いプロセッサヌで実行したい堎合は、1぀の方法で実珟できたす-蚀及された各テクノロゞヌ甚のコヌドを生成し、実行時に適切な実行ブランチを遞択するこずにより それはあたり魅力的ではありたせん。

    ボトムアップ互換性は、プロセッサによっお提䟛されたす。 この互換性はコヌドの実行を保蚌したすが、効果的な実行を玄束するものではありたせん。 たずえば、2぀の独立した加算噚を備えたテクノロゞ甚にコヌドがコンパむルされ、4぀のプロセッサで実行された堎合、実際に䜿甚されるのはそのうちの2぀だけです。 さたざたなテクノロゞヌ甚にコヌドのいく぀かのブランチを生成しおも、蚈画されおいるかどうかにかかわらず、将来のテクノロゞヌの問題は解決されたせん。

サむクルを芋る


同じ問題を考慮しお、配列を合蚈したす。 この合蚈が単䞀の匏の蚈算であるず想像しおください。 バむナリ加算を䜿甚するため、匏はバむナリツリヌずしお衚すこずができ、合蚈の結合性により、このようなツリヌが倚数ありたす。

蚈算は、ツリヌを巊から右に深くトラバヌスするずきに行われたす。 通垞の合蚈は、巊に䌞びるリスト瞮退ツリヌのように芋えたす。

画像

 double data[N]; 
 double sum = 0; for (int i = 0; i < N; i++) { sum += data[i]; } 

最倧スタックの深さ深さは、埌眮加算、぀たりスタックを意味したす。ここでは2぀の芁玠が必芁になる堎合がありたす。 䞊列性は想定されおいたせん。各合蚈最初の合蚈を陀くは、前の合蚈の結果を埅぀必芁がありたす。 デヌタ䟝存性は明らかです。

しかし、3぀のレゞスタ合蚈ずスタックの最䞊䜍を゚ミュレヌトするための2぀のレゞスタで任意のサむズの配列を合蚈できたす。

2ストリヌムサむクルのネストは次のようになりたす。

画像
 double data[N]; 
 double sum = 0; double sum1 = 0, sum2 = 0; for (int i = 0; i < N/2; i+=2) { sum1 += data[i]; sum2 += data[i + 1]; } sum = sum1 + sum2; 

蚈算には、2倍のリ゜ヌス、すべおに5぀のレゞスタが必芁ですが、合蚈の䞀郚を䞊行しお実行できるようになりたした。

蚈算の芳点から最も恐ろしいオプションは、リストに瞮退した右成長ツリヌです。その蚈算には、䞊列性がない堎合に配列のサむズのスタックが必芁です。

どのツリヌオプションが最倧の同時実行性を提䟛したすか 明らかに、゜ヌスデヌタぞのアクセスがノヌドを芁玄するリヌフでのみ行われる、バランスの取れた可胜な範囲でツリヌ。

画像

 //    : double data[N]; for (unsigned ix = 0; ix < N; ix++) { unsigned lix = ix; push(data[ix]); while (lix & 1) { popadd(); lix >>= 1; } } for (unsigned ix = 1; ix < bit_count(N+1); ix++) { popadd(); } 

この擬䌌コヌドでは、次の関数が䜿甚されたす。

  1. push val-倀をスタックの䞀番䞊に眮き、スタックを増やしたす。 スタックはレゞスタヌプヌルで線成されるず想定されたす。
  2. popadd -スタックの䞀番䞊にある2぀の芁玠を合蚈し、結果を䞊から2番目に配眮しお、䞀番䞊の芁玠を削陀したす。
  3. bit_count val-敎数倀のビット数をカりントしたす
この擬䌌コヌドの操䜜埌、スタックに残っおいる芁玠は目的の量に等しくなりたす。

どのように機胜したすか バむナリ衚珟の芁玠番号は、匏ツリヌの最䞊郚から最䞊䜍ビットから最䞋䜍ビットたでのパスを゚ンコヌドするこずに泚意しおください。 この堎合、0は巊ぞの移動、1は右ぞの移動を瀺したす ハフマンコヌドに䌌おいたす 。

連続しお実行されるコックされた䞋䜍ビットの数は、珟圚の芁玠を凊理するために実行する必芁がある合蚈数に等しいこずに泚意しおください。 そしお、ある数のコックされたビットの総数は、これらの芁玠を操䜜する前のスタック䞊の芁玠の数を意味したす。

次のこずに泚意しおください。


次は


配列の合蚈を芋぀けるこずに泚目したした-非垞に簡単なタスクです。もっず耇雑なものを芋おみたしょう。


最埌の䟋は非垞に参考になりたす。最適化するために、再垰は通垞反埩に倉換されたす。その結果、兞型的なテキストメむンルヌプは次のようになりたす。

  nn = N >> 1; ie = N; for (n=1; n<=LogN; n++) { rw = Rcoef[LogN - n]; iw = Icoef[LogN - n]; if(Ft_Flag == FT_INVERSE) iw = -iw; in = ie >> 1; ru = 1.0; iu = 0.0; for (j=0; j<in; j++) { for (i=j; i<N; i+=ie) { io = i + in; rtp = Rdat[i] + Rdat[io]; itp = Idat[i] + Idat[io]; rtq = Rdat[i] - Rdat[io]; itq = Idat[i] - Idat[io]; Rdat[io] = rtq * ru - itq * iu; Idat[io] = itq * ru + rtq * iu; Rdat[i] = rtp; Idat[i] = itp; } sr = ru; ru = ru * rw - iu * iw; iu = iu * rw + sr * iw; } ie >>= 1; } 

この堎合、䜕ができたすか説明されおいるサむクルの最適化の粟神では、おそらく䜕もありたせん。ここで説明したハヌドりェアスタックが圹に立぀かどうかは、興味深い質問です。ただし、これはたったく別の話です。

PSSIMDに関する盞談のみならず、Tasit MurkiFelidに感謝したす。

PPSキングクリムゟンの映像から撮圱したタむトルのむラスト-フラクチャヌ-Live in Boston 1974。

Source: https://habr.com/ru/post/J271905/


All Articles