>  > [永井電子] ウルトラ シリコン パワープラグコード レッド ランチア イプシロン 176B9 (DOHC) 1.2 96~01

[永井電子] ウルトラ シリコン パワープラグコード レッド ランチア イプシロン 176B9 (DOHC) 1.2 96~01

(前回の1.3から)1年ぶりの更新です!2.0は諸事情でスキップされました。アセットストアには出したんですが内容的にもう少しやりたかったのでなかったこと扱いで。LINQ to GameObject自体の説明はVer 1.0リリース時のブログLINQ to GameObjectによるUnityでのLINQの活用を参照ください。

今回はパフォーマンスチューニングを徹底的にやりました。というのも以前の素朴な実装は、素朴な通りの性能で、いいとか悪いとかじゃなく素朴なので、やるのならいっそギチギチにやってみたらどうかな、と。性能面でここまでやってるものは絶対にないはず。

もう一つは追加系をより使いやすく。のためにガッツリと破壊的変更を入れています。破壊的変更が入った理由は、使いにくかったからです。うぇぇ……。使いにくいポイントは概ね分かっていたし、プルリク等も貰っていたのですが、API的にイマイチなもので乗り気になれず、かといってAPIを維持しているとオーバーロードの解決などの問題でうまく処理できなくて、モニョモニョしている間に一年が経ってしまった。互換性は残したくはあったんですが、使いにくいままであったり、微妙なオーバーロードの追加とかで解決するよりは良いかな、と。いう決断です。

[永井電子] ウルトラ シリコン パワープラグコード レッド ’01/7~ ランチア スカイライン イプシロン 176B9 (DOHC) 1.2 96~01

APIはほとんど変わってないです(但しnameでフィルターかけるオーバーロードは消しました、HTMLやXMLと違って名前でのフィルタの重要性がかなり低いので、むしろないほうがいいかな、と)。

ヒエラルキーをツリーに見立てて、「軸」の概念を元にして、必要となる全方向での列挙を満たしています。今回、コードを劇的に書き換えたパフォーマンスチューニングを施しました。一点目は、yield returnによるコードを、全部手書きの構造体の列挙子に書き換えてます。これにより列挙に伴うゴミ発生が理想的にはなくなっています、理想的には:)

残念ながら 、そのままforeachに流すと C#のGCゴミとUnity(5.5)のコンパイラアップデートによるListのforeach問題解決について によりboxingが発生しますが(ゴミ化)、それでも構造体のサイズや再帰的に処理される場合での内部処理は気を配っているので(特にDescendantsはエクストリームにチューニングしたコードに変えた(再帰を特化Stackで置き換えたり……))、以前よりも良くなっているのは間違いないです。

ちなみに、基本的にはmutableなstructは避けたほうがいいです。Enumeratorはまさにそれで、実装にも注意が必要なら、利用にも注意を要するため(これはList<T>.Enumeratorも同様で、直接触ろうとすると罠にはまるケースが出てくる)なんでもかんでもstructで、というのは止めたほうがいいでしょう、どうしてもということでなければ原則やらないほうがいい事案です。struct enumeratorを返すテクニック自体は今は亡きXNAでも使われていたので(EffectPassCollectionやModelMeshCollectionなど各種コレクションがstruct Enumeratorを返す)、まぁ最終テクニックとしては有効(但し現状Unityではどうせforeachではボックス化されるのでそこまで有効ではないので、基本やらなくていいでしょう)

LINQで繋げたら、当然普通にLINQの消費フローに入るので、そんな意味ないんですけどね!というだけなのもアレなので、改善二点目、頻出パターンについて特化した最適化を入れてます。(+ OfComponent) + First, FirstOrDefault, ToArray に関しては通常のLINQではなく、この構造体Enumeratorに特化した呼び出しをするため、所謂LINQで想像する性能劣化を受けません。社内調べによると、割と FirstOrDefault や ToArray が直接接続されてる場合が多いので、それだけでも6~7割はカバーできているのではないかな、と。

更に三点目 データシステム TV-KIT (切替タイプ) NTV187 (NTV187)【smtb-s】、ToArrayNonAllocというメモリ節約/GC防止メソッドが追加されています(IEnumerable<T>にも生やしてあるのでLINQ to GameObject関係ないシーンでも使えないこともない)

GameObject[] array = new GameObject[0];
 
// 毎フレーム走査していても余計なメモリ確保はしない!
void Update()
{
 var size = origin.Children().ToArrayNonAlloc(ref array);
 for (int i = 0; i < size; i++)
 {
 var element = array[i];
 }
}

Physics.RaycastNonAllocやGetComponentsInChildren[T](List[T] results) のようなものですね。どうしても走査頻度が高くて、という場合には使えるんじゃないかと思います。まぁ スズキ クロスビー MN71S フロアマット(前部・後部座席分)【ブラック&ベージュ】◆カーマット 車 フロアカーペット、Find系は極力使わないように、というのと同じ話で、走査系を頻繁にやること自体が全然よくはないのですけれど。

また、ToArray/ToArrayNonAlloc/Destroyには(Func<GameObject, T> selector), (Func<GameObject, bool> filter), (Func<GameObject, bool> filter, Func<GameObject, T> selector), (Func<GameObject, TState> let, Func<TState, bool> filter, Func<TState, T> selector) といった、Where().Select().ToArray() のような割とよくある状況に対する最適化オーバーロードを入れてます。

この辺を活用してもらえば、単純にインラインで自前実装するよりも、むしろ速い/効率的なことのほうが多いでしょう。

特化したものを速くなるのはある種当たり前で、しかしそうするとメソッドが雪だるま式に増えるのが良くなくて、そしてLINQのいいところは合成可能なことにより特化させずとも無限の組み合わせで機能を実現できるところにある。しかし、まぁ勿論、柔軟性とパフォーマンスが幾ばくかトレードオフなのは当然の話なわけで、LINQの雰囲気を保ったまま、裏側だけ特化実装にこっそり差し替わってる。というあたりが落とし所としては良いのかな、と思ってますし、なのでそういう風に実装しました。

再帰的なイテレータの罠

Children(子要素列挙)なんかは数が大したことないので問題はそんなないんですが、Descendants(子孫要素列挙)は性能差が大きく出てきます。そして、利用頻度で言ってもDescendants系が基本多い。これのパフォーマンスを改善することは、非常に意味のあることです。さて、これはシンプルなDescendantsの実装です。

static IEnumerable<GameObject> Descendants(GameObject root)
{
 yield return root;
 foreach (Transform item in root.transform)
 {
 foreach (var child in Descendants(item.gameObject))
 {
 yield return child.gameObject;
 }
 }
}

このコードには大きな問題があります!再帰的なイテレータ、つまり foreach (var child in Descendants(item.gameObject)) は危険です。Baaaaad Practice、デス。要警戒です。これ、子孫にあるGameObjectの数だけ、イテレータ作ってます。GetEnumerator祭り!これは、LINQがどうのとかそういう次元を超えています。LINQのコストというのはメソッドチェーン分のGetEnumeratorの加算とMoveNextの連鎖による一回の呼び出しコストの増加が基本的な話で、ようするに2~3増えるという話で大したことあるといえば大したことあるし、大したことないといえば大したことない。が、さすがに要素数分だけ無駄にEnumerator作るとなったら話は別だ。ちょっとね、かなり気になるよね。

解決策は2つあります。一つはstruct enumeratorで、struct生成コストはあるもののゴミにはなりません。↑で書いたように実装済みです。

もう一点は、内部イテレーター化。イテレーターには概ね二種類、内部イテレーターと外部イテレーターがあります。外部イテレーターはforeachで使える、つまりGetEnumerator経由のもので、内部イテレーターはListのForEachなどクラスに直接生えてるもの。それぞれ利点と欠点があります。外部イテレーターの利点は柔軟性(LINQ)と言語サポート(foreach/generator)、よって基本的にはこちらを選べばOKです。欠点はパフォーマンスが内部イテレーターほど稼げない。どうしても一つシーケンスを進めるのにMoveNextとCurrentの2つのメソッド呼び出しが必要になるので。内部イテレーターの利点はパフォーマンスで、内部構造に最適化したループを回せるので、基本最速です。欠点は柔軟性がないのと、それぞれのコレクションで独自実装になること。

LINQ to GameObjectでは両方実装しています。外部イテレーターは手書きで最適化したstruct enumerator(とStackPoolと、その他諸々の仕掛け)によって、遅延実行やLINQサポートなどの柔軟性を維持したまま、パフォーマンスとGC行きのゴミを全く出さないようにしています。内部イテレーターに関してはForEachとToArray(NonAlloc)に関しては、外部イテレーター版と全く異なる実行パスを通ることにより、最速を維持します。

ところで、Unityネイティブに用意されているものがある場合は、それを使ったほうが速くなります。例えば DescendantsAndSelf().OfComponent().ToArray() は GetComponentsInChildren(includeInactive:true) に概ね等しく(一つのオブジェクトに複数コンポーネントが貼り付けてある場合、LINQ to GameObjectではそれぞれのGameObjectに一つのみ、GetComponentsInChildrenは複数と、正確には挙動が異なります)、後者を使ったほうが断然速い。一応ですが、ネイティブだから常に速いとか、そういうことはなくて、ネイティブ-マネージド間の変換コストのほうが勝る場合もあります(たとえばUnityにおけるコルーチンの省メモリと高速化について、或いはUniRx 5.3.0でのその反映のような話)。けれど、この場合は、C#だけで走査すると、GameObject毎でのGetComponentが避けられません(GetComponentのコストはタダではないのだ)。なので、一発でネイティブ内でかき集めてきたほうが絶対的に速くなります。子孫を辿るだけならほとんど遜色ない、むしろ速いといっていいぐらいなので、本当にこれはGetComponentに対する処理効率の差だけですね。これだけはどうにもできませんでした。

追加系

変わってます。使い勝手的にはこっちの対応がメインです。

以前のAPIの何が不便かって、引数にGameObjectしか受け付けなかった!そして戻り値がGameObject!大抵の場合はComponentを入れてComponentを受け取りたいのに!これは酷い!いやほんと酷すぎでした……。なんでそうなってたかというと言い訳はそれなりにあって、まずGameObjectとComponentって継承階層が別のとこにいるんですよねー、のが困る。それをオーバーロードとして分けると Clazzio クラッツィオ クール EZ-0729 レッド×ブラック/レッドダブルステッチ CX-5 KFEP/KF5P/KF2P、IEnumerableを受け取るオーバーロードが存在していたため、どうやってもうまく型が解決できなかったのだ……。

もうどうにもならなかったので、API変えてます。IEnumerableを受け取るオーバーロードはXxxRangeという名前に分離。また、基本的には<T>を返すように、そして T:UnityEngine.Object を受け取れるようにしたので、引数としてやっとMonoBehaviourなComponentを素直に流し込めるようになりましたー。万歳。継承階層が別のとこにいて困ります問題は、

[永井電子] ウルトラ シリコン パワープラグコード レッド ランチア イプシロン 176B9 (DOHC) 1.2 96~01【最速最新2016モデル!!】


【店舗塗装サービス】【ロエン / トミーカイラ】【 AUDI A5 SPORTBACK facelift Half type DBA-8TCDN 2012.01~ 】 フロントスポイラー [材質] FRP+ウェットカーボン(素地)


[永井電子] ウルトラ シリコン パワープラグコード レッド ランチア イプシロン 176B9 (DOHC) 1.2 96~01 〈マジカルアートシート〉ピラースタンダードセット【N-BOX】

↓↓↓カーマット フロアマット マツダ アテンザ/アテンザ・スポーツ/アテンザ・スポーツワゴン 20年1月~24年11月 2WD-LUXループベージュ;18インチヴェルファイアハイブリッド30系ENKEI パフォーマンスライン PF01 スパークルシルバー 7.0Jx18トランパス LU2 235/50R18;SARGENT サージェント シート本体 フロント&リアシート パイピングカラー:メタリックシルバーウェルト MULTISTRADA [ムルティストラーダ], 16インチ サマータイヤ セット【適応車種:シルフィ(B17系)】WEDS ライツレー ZM ブラックメタリックポリッシュ 6.5Jx16NANOエナジー 3プラス 195/60R16, 16インチ サマータイヤ セット【適応車種:CX-3(DK系)】HOT STUFF Gスピード G01 メタリックシルバー 6.5Jx16LEMANS V LM5 215/60R16, カードでポイント最大34倍 3/21(木)20:00~3/26(火)1:59迄 グッドイヤー ベクター Vector 4Seasons Hybrid オールシーズンタイヤ 185/60R15 BLEST EUROMAGIC Type S-07 15 X 6 +45 5穴 114.3, 取付店直送可 限定 スタッドレス 17インチ 225/55R17 グッドイヤー アイスナビ6 KTW SW-7 SI タイヤホイール4本セット 新品 国産車;CRIMSON(クリムソン) CLUB LINEA L747S KING LABEL (クラブリネア L747S キングレーベル) 22インチ 12.5J PCD:112 穴数:5 inset:36 DISK TYPE:HIGH カラー:クローム [ホイール1本単位]/H 【関西、関東限定】取付サービス品17 マジェスタ シートカバー【オートウェア】クラウンマジェスタ 170系 (後部座席背もたれ一体型) シートカバー ポイント カラー:ブラック↓↓↓★色番号塗装発送50 プリウス サイドステップ【エイチスタイル】プリウス 50系 サイドスカート 素地, スタッドレスタイヤ ダンロップ ウインターマックス02 WM02 225/45R19 92Q & レオニス VX HSミラーカット 7.5-19 タイヤホイール4本セット 225/45-19 DUNLOP WINTER MAXX 02 WM02, X1211961 / 1253731 DIXCEL Xタイプ ブレーキパッド 1台分セット BMW F04 KX44L/YA30/YE30 2009/10~ ActiveHybrid 7/ActiveHybrid 7L, Clazzio/クラッツィオ シートカバー DIA (ダイヤ) スズキ ワゴンR H29.2~ グレード: HYBRID FX/FA 型式: MH55S/MH35S 定員: 4 ES-6042, K&H ケイアンドエイチ シート本体 アニバーサリーモデルA ステッチ <セミオーダー> カラー:イエロー カラー:メッシュ


↓↓↓↓↓↓



↓↓↓↓↓↓

ラフ&ロード 2018年春夏モデル ラフメッシュジャケット 黒 BLLサイズ RR7333BKB4 JP店, USフルブラ・USノーズブラ Colgan Front End Mask Bra 2pc. Fits Chevy Impala LS, LT& SS W/License 2006-16 コルガンフロントエンドマスクブラ2pc Chevy Impala LS、LT&amp; SS W /ライセンス2006-16, US YAMAHA 北米ヤマハ純正アクセサリー カジュアルウェア Yamaha Racing Hooded Fleece サイズ:LG, GLANZ(グラン) 輸入車用ハードブレーキローター[リア] BMW 3シリーズ M3 M3 2.3/2.5 [ブレーキローター] 123205, カードでポイント最大34倍 3/21(木)20:00~3/26(火)1:59迄 TOYOTIRES トーヨー ナノエナジー3プラス NANOENERGY3plus サマータイヤ 195/55R15 HotStuff WAREN ヴァーレン W04 4本 ホイールセット 15インチ 15 X 6 +43 5穴 114.3

245/40R20 99Y XL FALKEN ファルケン AZENIS FK453 アゼニス FK453 WORK LANVEC LD1 ワーク ランベック エルディーワン サマータイヤホイール4本セット;【送料無料】 245/40R19 19インチ FABULOUS ファブレス パンデミック LM-5 マルチピース 8J 8.00-19 YOKOHAMA ヨコハマ DNA エコス サマータイヤ ホイール4本セット フジコーポレーション 【送料無料】 275/55R20 20インチ RAYS レイズ フルクロス バイエイト 8.5J 8.50-20 BFG BFグッドリッチ オールテレーンT/A KO2 RBL サマータイヤ ホイール4本セット 送料無料(一部離島除く)WEDS REVSPEC レブスペック TC ブレーキパッド・リアニッサン フェアレディZ(2008~ Z34系 HZ34) フジコーポレーション;【ロードスター レスポンスリング】 NA6C 89.09- トルク感UP スムーズ感UP シエクル/ジェイロード (RZ02RS

【デジキャン DIGICAM】 アウトランダーPHEV 等にお勧め アルミ鍛造ワイドトレッドスペーサー+専用ハブリングセット (各2個入)30mm厚 5H-114.3 P1.5 ハブ67-67φ 型式等:GG2W 品番:15114530-676730;ブリヂストン ブリザック VRX 在庫 2018年製 スタッドレス スタッドレスタイヤ 〇 215/60R17 WEDS ウェッズ Leonis レオニス NAVIA02 ナビア02 ホイールセット 4本 17インチ 17 X 7 +55 5穴 114.3

、UnityEngine.Objectを受け取った上で、動的にGameObjectとComponentに仕分けすることで解決。

というわけで、やっと自信持って普通に使えるようになりました。単純な話なんですが、まず破壊的変更にする、ということに腰が重かったことと、それを踏まえても、うまいAPIを構築するのに手間取った。のせいでこんなに遅れてしまって、いやはや……。

その他、あとDestroyでデフォルトでヒエラルキから外さなくしました。このヒエラルキから外すというのは最低のアイディアで、配列ではなく列挙しながら(LINQ to GameObjectでやるような!)Destroyする場合に、ヒエラルキから外すせいで位置がずれて死ぬ。というのを防ぐためにToArrayでキャッシュしなければならない(無駄なオーバーヘッド!)。というしょうもない自体に陥りがちなので、やめました。わざわざ外すコストだってゼロじゃないので、二重に悪い。

まとめ

GameObjectBuilderというものがあったのですが、イラナイ子なので消しました。LINQ to XMLのFunctional Constructionを模した――ものなのですが、そういう、コピーに一生懸命なだけなのって悪趣味なんですよね。大事なのは、概念(LINQ to Tree)を対象環境(Unity)に最適化することであって、コピーすることではない。そういうの、分かっているつもりではいたのですが、やり始めるとついついやってしまうところがある。随時見切って、バッサリ切り落とせるようにならないとですね。

LINQ to GameObjectのオリジナルのデザインは2014/10/28だったんですが、その頃は今よりは全然遥かにUnityへの習熟度、知識が欠けていたなぁ、というのを改めて痛感しました。思い上がる、ということはないですが、環境への理解力が足らないとどこかイマイチなものになってしまうわけで、C#云々抜きに、常にUnityに真摯に向き合ってかないとダメですね。実際問題、愛情を持って突き詰めて考えられないと、本当の理想のところまでは行けない。小手先の知識だけで処理したようなライブラリは、まぁ使いたくないですねえ、そういうの実際どうしてもどこか独りよがりのしょうもないものになってしまうので。

LINQは遅い/GCキツくなるというのは絶対的な事実ではあるのですけれど、極力書き味を失わないようにしつつ、6, 7割ぐらいのシチュエーションには特化した最適化を施し、何も考えずともむしろ普通に書くよりも速くなる。それ以外のシチュエーションでも、速さを意識した使い方をすれば、やはり普通に書くよりも速くなる。という、私的には理想的かな BMW E60 5シリーズ Kerberos K'sスタイル 3D Matte Real Carbon マットカーボントランク 【AK-1-074】、というところで表現できたので、是非是非、機能を気にする人も、性能を気にする人も使ってみてください。どちらも満たせるものになっているはずです。

ところでしつこいですが BLITZ ブリッツ コアタイプエアクリーナー SUS POWER LM-RED code59187 スズキ ワゴンR 03/09-07/05 MH21S K6A、9/13にPhoton勉強会で「Photon Server Deep Dive - PhotonWireの実装から見つめるPhoton Serverの基礎と応用」というタイトルで話しますので、Photon興味ある人も、そうでなくてもUniRx興味ある人もどうぞ。LINQ to GameObject、或いはUnityとLINQについての話は、さすがにあんま関係ないのでセッション内容には含まれませんが懇親会ででも掴まえてもらえば何でも答えます。

[永井電子] レッド (DOHC) イプシロン ウルトラ 176B9 ランチア シリコン パワープラグコード レッド 1.2 96~01 ランチア

rook: (09/1000:34)

PhotonWIreはUnity側のコードからサーバー側のコードが自動生成出来たらより便利と思うのですが、そのような機能は無いのでしょうか?
開発時にはサーバーとのインターフェースを決めるのが、サーバーサイドからではなく、
クライアントから提案するというのが多々あるため、なかなかPhotonWIreの利便性を説明するのに難儀しております。。

neuecc: (09/1309:40)

Unity側からというのはあまり考えたことはなかった&実際のところ無理です。。。
これって「どちらを手書きするか」の問題になるわけですけれど、
Unity側のほうが生成量が圧倒的に多くて、実際のとこ手書きするのは不可能なので
もしクライアントからの提案であっても、
サーバー側でメソッド定義だけ書いて中身はNotImplementedException
でジェネレートすればいいんじゃないかなあ(実質的にはクライアントからの提案)、

[永井電子] ウルトラ シリコン パワープラグコード レッド ランチア イプシロン 176B9 (DOHC) 1.2 96~01

、と思います。

Name
WebSite(option)
Comment

Trackback(0) | http://neue.cc/2016/08/11_538.html/trackback

Search/Archive

Category

Profile


Yoshifumi Kawai
Microsoft MVP for Visual Studio and Development Technologies(C#)

April 2011
|
July 2019

Twitter:@neuecc
GitHub:neuecc
ils@neue.cc
{yahoojp}jpprem01-zenjp40-wl-zd-59004
{yahoojp}jpprem01-zenjp40-wl-zd-59004 {yahoojp}jpprem01-zenjp40-wl-zd-59004