アクセス演算子とオブジェクトフィールドの比較

AS3(およびAS2とJS)の優れた機能の1つは、オブジェクトのフィールドに動的にアクセスできることです。 これにより、コンパイル時にフィールドの存在を知る必要がないため、より「動的な」コードが作成されます。 この機能は、動的言語の他の機能と同様に、アプリケーションのパフォーマンスに大きく影響する可能性があります。 今日は、フィールドへの「遅い」動的アクセスがどれほど遅いかを示す例を見ていきます。



ところで、オブジェクトのフィールドにアクセスする例を次に示します。



function foo(p:Point): void { px; // "dot" operator method of reading fields p["x"]; // "index" operator method of reading fields px = 1; // "dot" operator method of writing fields p["x"] = 1; // "index" operator method of writing fields }
      
      







ポイント演算子を使用してアクセスする場合、コンパイル時にアクセスするフィールドを知る必要があります。 対照的に、「インデックス」を介してフィールドにアクセスすると、コンパイル段階ですべてのエラーチェックが無効になり、文字列、プロパティ、計算結果などの式を使用してプロパティにアクセスできます。



これを知って、フィールドへのダイナミックアクセスをテストするために作成されたパフォーマンステスト用のアプリケーションを更新し、読み取りおよび書き込み演算子をその中に含めました。また、1サイクルあたりのテストを追加して、サイクルの負荷の「影響」を減らしました。 テスト結果と視覚情報付きのグラフが添付されています。



 package { import flash.display.*; import flash.events.*; import flash.utils.*; import flash.text.*; import flash.geom.*; public class FieldAccessMethods extends Sprite { public static var VAL:Number=0; private var __logger:TextField = new TextField(); private function row(...vals): void { __logger.appendText(vals.join(",")+"\n"); } public function FieldAccessMethods() { __logger.autoSize = TextFieldAutoSize.LEFT; addChild(__logger); var i:int; const REPS:int = 1000000; var beforeTime:int; var afterTime:int; var readDotTime:int; var writeDotTime:int; var readIndexTime:int; var writeIndexTime:int; var p:Point = new Point(0,0); var mp:MyPoint = new MyPoint(); var md:MyDynamic = new MyDynamic(); var d:Dictionary = new Dictionary(); dx=0; var a:Array = [0]; ax=0; var c:Class = DynamicAccess; var dc:Class = MyDynamic; var o:Object = {x:0}; row("Type", "Read (dot)", "Write (dot)", "Read (index)", "Write (index)"); beforeTime = getTimer(); for (i=0; i < REPS; ++i) { px;px;px;px;px; px;px;px;px;px; } afterTime = getTimer(); readDotTime = afterTime - beforeTime; beforeTime = getTimer(); for (i=0; i < REPS; ++i) { px=0;px=0;px=0;px=0;px=0; px=0;px=0;px=0;px=0;px=0; } afterTime = getTimer(); writeDotTime = afterTime - beforeTime; beforeTime = getTimer(); for (i=0; i < REPS; ++i) { p["x"];p["x"];p["x"];p["x"];p["x"]; p["x"];p["x"];p["x"];p["x"];p["x"]; } afterTime = getTimer(); readIndexTime = afterTime - beforeTime; beforeTime = getTimer(); for (i=0; i < REPS; ++i) { p["x"]=0;p["x"]=0;p["x"]=0;p["x"]=0;p["x"]=0; p["x"]=0;p["x"]=0;p["x"]=0;p["x"]=0;p["x"]=0; } afterTime = getTimer(); writeIndexTime = afterTime - beforeTime; row("Point", readDotTime, writeDotTime, readIndexTime, writeIndexTime); beforeTime = getTimer(); for (i=0; i < REPS; ++i) { mp.x;mp.x;mp.x;mp.x;mp.x; mp.x;mp.x;mp.x;mp.x;mp.x; } afterTime = getTimer(); readDotTime = afterTime - beforeTime; beforeTime = getTimer(); for (i=0; i < REPS; ++i) { mp.x=0;mp.x=0;mp.x=0;mp.x=0;mp.x=0; mp.x=0;mp.x=0;mp.x=0;mp.x=0;mp.x=0; } afterTime = getTimer(); writeDotTime = afterTime - beforeTime; beforeTime = getTimer(); for (i=0; i < REPS; ++i) { mp["x"];mp["x"];mp["x"];mp["x"];mp["x"]; mp["x"];mp["x"];mp["x"];mp["x"];mp["x"]; } afterTime = getTimer(); readIndexTime = afterTime - beforeTime; beforeTime = getTimer(); for (i=0; i < REPS; ++i) { mp["x"]=0;mp["x"]=0;mp["x"]=0;mp["x"]=0;mp["x"]=0; mp["x"]=0;mp["x"]=0;mp["x"]=0;mp["x"]=0;mp["x"]=0; } afterTime = getTimer(); writeIndexTime = afterTime - beforeTime; row("MyPoint", readDotTime, writeDotTime, readIndexTime, writeIndexTime); beforeTime = getTimer(); for (i=0; i < REPS; ++i) { md.x;md.x;md.x;md.x;md.x; md.x;md.x;md.x;md.x;md.x; } afterTime = getTimer(); readDotTime = afterTime - beforeTime; beforeTime = getTimer(); for (i=0; i < REPS; ++i) { md.x=0;md.x=0;md.x=0;md.x=0;md.x=0; md.x=0;md.x=0;md.x=0;md.x=0;md.x=0; } afterTime = getTimer(); writeDotTime = afterTime - beforeTime; beforeTime = getTimer(); for (i=0; i < REPS; ++i) { md["x"];md["x"];md["x"];md["x"];md["x"]; md["x"];md["x"];md["x"];md["x"];md["x"]; } afterTime = getTimer(); readIndexTime = afterTime - beforeTime; beforeTime = getTimer(); for (i=0; i < REPS; ++i) { md["x"]=0;md["x"]=0;md["x"]=0;md["x"]=0;md["x"]=0; md["x"]=0;md["x"]=0;md["x"]=0;md["x"]=0;md["x"]=0; } afterTime = getTimer(); writeIndexTime = afterTime - beforeTime; row("MyDynamic (existing)", readDotTime, writeDotTime, readIndexTime, writeIndexTime); beforeTime = getTimer(); for (i=0; i < REPS; ++i) { md.z;md.z;md.z;md.z;md.z; md.z;md.z;md.z;md.z;md.z; } afterTime = getTimer(); readDotTime = afterTime - beforeTime; beforeTime = getTimer(); for (i=0; i < REPS; ++i) { md.z=0;md.z=0;md.z=0;md.z=0;md.z=0; md.z=0;md.z=0;md.z=0;md.z=0;md.z=0; } afterTime = getTimer(); writeDotTime = afterTime - beforeTime; beforeTime = getTimer(); for (i=0; i < REPS; ++i) { md["z"];md["z"];md["z"];md["z"];md["z"]; md["z"];md["z"];md["z"];md["z"];md["z"]; } afterTime = getTimer(); readIndexTime = afterTime - beforeTime; beforeTime = getTimer(); for (i=0; i < REPS; ++i) { md["z"]=0;md["z"]=0;md["z"]=0;md["z"]=0;md["z"]=0; md["z"]=0;md["z"]=0;md["z"]=0;md["z"]=0;md["z"]=0; } afterTime = getTimer(); writeIndexTime = afterTime - beforeTime; row("MyDynamic (added)", readDotTime, writeDotTime, readIndexTime, writeIndexTime); beforeTime = getTimer(); for (i=0; i < REPS; ++i) { dx;dx;dx;dx;dx; dx;dx;dx;dx;dx; } afterTime = getTimer(); readDotTime = afterTime - beforeTime; beforeTime = getTimer(); for (i=0; i < REPS; ++i) { dx=0;dx=0;dx=0;dx=0;dx=0; dx=0;dx=0;dx=0;dx=0;dx=0; } afterTime = getTimer(); writeDotTime = afterTime - beforeTime; beforeTime = getTimer(); for (i=0; i < REPS; ++i) { d["x"];d["x"];d["x"];d["x"];d["x"]; d["x"];d["x"];d["x"];d["x"];d["x"]; } afterTime = getTimer(); readIndexTime = afterTime - beforeTime; beforeTime = getTimer(); for (i=0; i < REPS; ++i) { d["x"]=0;d["x"]=0;d["x"]=0;d["x"]=0;d["x"]=0; d["x"]=0;d["x"]=0;d["x"]=0;d["x"]=0;d["x"]=0; } afterTime = getTimer(); writeIndexTime = afterTime - beforeTime; row("Dictionary", readDotTime, writeDotTime, readIndexTime, writeIndexTime); beforeTime = getTimer(); for (i=0; i < REPS; ++i) { ax;ax;ax;ax;ax; ax;ax;ax;ax;ax; } afterTime = getTimer(); readDotTime = afterTime - beforeTime; beforeTime = getTimer(); for (i=0; i < REPS; ++i) { ax=0;ax=0;ax=0;ax=0;ax=0; ax=0;ax=0;ax=0;ax=0;ax=0; } afterTime = getTimer(); writeDotTime = afterTime - beforeTime; beforeTime = getTimer(); for (i=0; i < REPS; ++i) { a["x"];a["x"];a["x"];a["x"];a["x"]; a["x"];a["x"];a["x"];a["x"];a["x"]; } afterTime = getTimer(); readIndexTime = afterTime - beforeTime; beforeTime = getTimer(); for (i=0; i < REPS; ++i) { a["x"]=0;a["x"]=0;a["x"]=0;a["x"]=0;a["x"]=0; a["x"]=0;a["x"]=0;a["x"]=0;a["x"]=0;a["x"]=0; } afterTime = getTimer(); writeIndexTime = afterTime - beforeTime; row("Array", readDotTime, writeDotTime, readIndexTime, writeIndexTime); beforeTime = getTimer(); for (i=0; i < REPS; ++i) { c.VAL;c.VAL;c.VAL;c.VAL;c.VAL; c.VAL;c.VAL;c.VAL;c.VAL;c.VAL; } afterTime = getTimer(); readDotTime = afterTime - beforeTime; beforeTime = getTimer(); for (i=0; i < REPS; ++i) { c.VAL=0;c.VAL=0;c.VAL=0;c.VAL=0;c.VAL=0; c.VAL=0;c.VAL=0;c.VAL=0;c.VAL=0;c.VAL=0; } afterTime = getTimer(); writeDotTime = afterTime - beforeTime; beforeTime = getTimer(); for (i=0; i < REPS; ++i) { c["VAL"];c["VAL"];c["VAL"];c["VAL"];c["VAL"]; c["VAL"];c["VAL"];c["VAL"];c["VAL"];c["VAL"]; } afterTime = getTimer(); readIndexTime = afterTime - beforeTime; beforeTime = getTimer(); for (i=0; i < REPS; ++i) { c["VAL"]=0;c["VAL"]=0;c["VAL"]=0;c["VAL"]=0;c["VAL"]=0; c["VAL"]=0;c["VAL"]=0;c["VAL"]=0;c["VAL"]=0;c["VAL"]=0; } afterTime = getTimer(); writeIndexTime = afterTime - beforeTime; row("Static Class", readDotTime, writeDotTime, readIndexTime, writeIndexTime); beforeTime = getTimer(); for (i=0; i < REPS; ++i) { dc.VAL;dc.VAL;dc.VAL;dc.VAL;dc.VAL; dc.VAL;dc.VAL;dc.VAL;dc.VAL;dc.VAL; } afterTime = getTimer(); readDotTime = afterTime - beforeTime; beforeTime = getTimer(); for (i=0; i < REPS; ++i) { dc.VAL=0;dc.VAL=0;dc.VAL=0;dc.VAL=0;dc.VAL=0; dc.VAL=0;dc.VAL=0;dc.VAL=0;dc.VAL=0;dc.VAL=0; } afterTime = getTimer(); writeDotTime = afterTime - beforeTime; beforeTime = getTimer(); for (i=0; i < REPS; ++i) { dc["VAL"];dc["VAL"];dc["VAL"];dc["VAL"];dc["VAL"]; dc["VAL"];dc["VAL"];dc["VAL"];dc["VAL"];dc["VAL"]; } afterTime = getTimer(); readIndexTime = afterTime - beforeTime; beforeTime = getTimer(); for (i=0; i < REPS; ++i) { dc["VAL"]=0;dc["VAL"]=0;dc["VAL"]=0;dc["VAL"]=0;dc["VAL"]=0; dc["VAL"]=0;dc["VAL"]=0;dc["VAL"]=0;dc["VAL"]=0;dc["VAL"]=0; } afterTime = getTimer(); writeIndexTime = afterTime - beforeTime; row("Dynamic Class", readDotTime, writeDotTime, readIndexTime, writeIndexTime); beforeTime = getTimer(); for (i=0; i < REPS; ++i) { ox;ox;ox;ox;ox; ox;ox;ox;ox;ox; } afterTime = getTimer(); readDotTime = afterTime - beforeTime; beforeTime = getTimer(); for (i=0; i < REPS; ++i) { ox=0;ox=0;ox=0;ox=0;ox=0; ox=0;ox=0;ox=0;ox=0;ox=0; } afterTime = getTimer(); writeDotTime = afterTime - beforeTime; beforeTime = getTimer(); for (i=0; i < REPS; ++i) { o["x"];o["x"];o["x"];o["x"];o["x"]; o["x"];o["x"];o["x"];o["x"];o["x"]; } afterTime = getTimer(); readDotTime = afterTime - beforeTime; beforeTime = getTimer(); for (i=0; i < REPS; ++i) { o["x"]=0;o["x"]=0;o["x"]=0;o["x"]=0;o["x"]=0; o["x"]=0;o["x"]=0;o["x"]=0;o["x"]=0;o["x"]=0; } afterTime = getTimer(); writeDotTime = afterTime - beforeTime; row("Object", readDotTime, writeDotTime, readIndexTime, writeIndexTime); } private function foo(): void {} } } class MyPoint { public var x:Number=0; public var y:Number=0; } dynamic class MyDynamic { public var x:Number=0; public var y:Number=0; public static var VAL:Number=0; }
      
      







テストが実施された環境の技術的特性:

* Flex SDK(MXMLC)4.1.0.16076、リリースモードでのコンパイル

* Flash Player 10.2.154.27のリリースバージョン

* 2.4 Ghz Intel Core i5

* Mac OS X 10.6.7



得られた結果:

種類 読書(ドット) 記録(期間) 読み取り(インデックス) レコード(インデックス)
ポイント 2 12 816 892
マイポイント 2 9 812 890
MyDynamic(既存) 3 9 813 892
MyDynamic(追加) 555 369 855 1021
辞書 305 427 995 1168
配列 404 510 1207 1380
静的クラス 143 103 841 898
動的クラス 140 103 809 886
対象 831 1040 809 886




同じグラフ結果:



画像 images.jacksondunstan.com/articles/1179/performance_all.png



インデックスアクセス演算子は、オブジェクトのみを除き、すべてのタイプのオブジェクトのパフォーマンスの点でより「高価」でした。 もっと「高価」と言うことさえできます。 動的な配列および辞書オブジェクトの場合、アクセスは依然として「遅く」、生産性は「わずか」に3倍向上しました。 「高速」アクセス(動的クラスではない)を持つオブジェクトでは、パフォーマンスが400倍向上しました。 次に、ポイント演算子のみを使用してアクセステストの結果を見てみましょう。



画像 images.jacksondunstan.com/articles/1179/performance_dot.png



静的フィールドへのアクセスは、動的フィールドへのアクセスよりもはるかに高速であることがわかります。 ポイント演算子を使用したフィールドへのアクセスは、「インデックス」演算子を使用した同じフィールドへのアクセスよりもはるかに高速です(Objectクラスを除く)。 これは、動的フィールドへのアクセスと比較して、静的フィールドへのアクセスがはるかに高速であることを意味します。



次に、「インデックス」演算子を使用してアクセステストの結果を見てみましょう。



画像 images.jacksondunstan.com/articles/1179/performance_index.png



このグラフは、ほとんどのオブジェクトのパフォーマンスの違いが「消失」していることを示しています。 動的クラス、Dictionary、およびArrayのフィールドは、他のクラスよりも低速です。 奇妙なことですが、このステートメントはObjectにも適用されます。フィールドにアクセスする際にこのクラスを操作することに大きな違いはないようです。



読み取りフィールドと書き込みフィールドの比較に関しては、アクセス方法によってまったく異なるグラフが表示されます。 「インデックス」演算子を使用する場合、書き込みは常に読み取りよりも約10%遅くなります。 一方、「ポイント」演算子は矛盾する結果をもたらします。 「インデックス」演算子を使用したオブジェクト、辞書、および配列の動的クラスは、静的フィールドの場合でも、動的クラスおよびクラスオブジェクトのインスタンスである場合でも、書き込みの動作が遅くなります。 ただし、ほとんどすべてのクラスで、パフォーマンスの差はプラス/マイナス20%です。



結論として、コードのパフォーマンスを向上させるためにインデックスアクセス演算子を使用する必要はないと言えます。 Objectの「純粋な」インスタンスを操作する場合は例外かもしれませんが、それらは非常に遅いので、そもそもそれらを使用しない方が良いでしょう。



All Articles