スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
拍手コメントを見る

TypeScript って…

えーと、ほぼ1年ぶりのブログ書いてみようと思いまして。

こんな不精なところでも、30/日くらいのアクセスがあるんですね。ありがたいことです。
Excelの別ウィンドウ化や SQLのエラーの対処法が人気のようです。いや、人気っつっても分母は小さいわけですが…

それでも、誰かの役に立ってんだなぁと思うと、書いて(過去形)良かったなぁと思ったりします。

さてさて、話は TypeScriptに入ります。入るっていってもまだ何もないので、覚悟の宣言みたいなものです。

いろいろ情報をあさってみたのですが、あちこちで「すごい!」とか「こりゃいい!」などの賛辞をよく目にしました。最大のメリットは「javascript で静的型付けができる!!」なようです。

…(javascriptって)できないんだ!??!!??

というくらい、javascript に素人な僕なわけで、TypeScriptのその秘めたるポテンシャルがさっぱり判らんのですよ。
それでも、食扶ち確保のため、これから勉強してときどきここにフィードバックしていこうと思っております。

それではまた。
スポンサーサイト
拍手コメントを見る

テーマ : プログラミング
ジャンル : コンピュータ

tag : TypeScript

DataView.ToTable() の distinct 指定が遅い

以前のエントリ「.NET DataTable の集計速度を計測してみた」でも書きましたが、DataView.ToTable() メソッドにおける、distinct 指定がパフォーマンス上ボトルネックとなるので、使うとしても場所限定って事になるだろうなと思ってました。

実際のところ、どんだけ遅いんだろ?って事で確認してみたのですが、対象を複数列にすると極端にパフォーマンスが落ちることがわかりました。もはや安心して使えるレベルじゃないなぁと。

オリジナルの DataTable に COL1 ~ COL5 まで列があったとします。
ToTable() メソッドを利用すれば、例えば COL1 を除いた残りのレコードで、重複しないテーブルを生成させる事ができます。
例1)
001 : void Example1()
002 : {
003 : DataTable dtTgt = dtOrg.DefaultView.ToTable(true,
004 : "COL2", "COL3", "COL4", "COL5");
005 : }

対して、for で回して、Rows.Find() で検索してみる方法は次の様なコードです。
例2)
001 : void Example2()
002 : {
003 : DataTable dtTgt = dtOrg.Clone();
004 : dtTgt.PrimaryKey = new DataColumn[]
005 : {
006 : dtTgt.Columns["COL2"], dtTgt.Columns["COL3"],
007 : dtTgt.Columns["COL4"], dtTgt.Columns["COL5"]
008 : };
009 : foreach (DataRow drOrg in dtOrg.Rows)
010 : {
011 : object [] keys = new object []
012 : {
013 : drOrg["COL2"], drOrg["COL3"],
014 : drOrg["COL4"], drOrg["COL5"]
015 : };
016 : DataRow drTgt = dtTgt.Rows.Find(keys);
017 : if (null == drTgt)
018 : dtTgt.Rows.Add(drOrg.ItemArray);
019 : }
020 : dtTgt.Columns.Remove("COL1");
021 : }

どうみても、例1の方がシンプルで速そうな感じなんですが、果たしてその結果は・・・!?

全く重複しないレコードを用意して、確認してみました。
           +--------------------------------+
| 対象件数 |
+-------+-------+-------+--------+
| 100 | 1000 | 10000 | 100000 |
+----------+-------+-------+-------+--------+
| Example1 | 0 | 0.063 | 6.828 | 692.584|
+----------+-------+-------+-------+--------+
| Example2 | 0 | 0.016 | 0.188 | 2.391|
+----------+-------+-------+-------+--------+
※単位:秒


どうでしょうか?
これみて、DataView.ToTable() メソッドを利用したいと全く思わなくなりました。
Example1の場合、10万件で、12分近くかかるってことで、100万行やると、単純計算で19時間もかかりそうなので、実験は中止しました。
ちなみに、Example2 は、28秒掛かりました。

※今回は公平となるように、抽出対象となる列を全て PrimaryKey としましたが、通常、PrimaryKey は1つか2つです。数が少なくなればなるほど、より高速になります。 拍手コメントを見る

テーマ : プログラミング
ジャンル : コンピュータ

.NET DataTable の集計速度を計測してみた

.NET の DataTable では Group By 的な事をやろうとしても、そういったメソッドは用意されていません。
実現方法としていくつか思いついたのですが、本格実装に入る前に、どの方法が一番パフォーマンスに優れているのか計測してみました。
※ちなみに汎用的な方法ではないので期待せぬよう。

例として次のようなテーブルを集計対象とします。
+--------+--------+-------+--------+
| Shop | Item | Price | Number |
+--------+--------+-------+--------+
| Odakyu | Apple | 200 | 500 |
| Odakyu | Melon | 600 | 100 |
| Odakyu | Orange | 150 | 300 |
| Seibu | Apple | 180 | 400 |
| Seibu | Orange | 160 | 600 |
| Tokyu | Melon | 700 | 50 |
| Tokyu | Orange | 170 | 200 |
+--------+--------+-------+--------+

Shop には仕入先の名称が入り、Itemには品物、Price には品物の単価、Number には仕入数。
というテーブル構造です。

これらの商品を仕入れた店では Apple の仕入件数は何件?などを「集計」したいとします。
上記の表から Apple が入っている行(レコード)を対象として、その行数(レコード数)が分かれば、Apple の仕入件数が何件あるのかが導き出せます。
+--------+--------+-------+--------+
| Shop | Item | Price | Number |
+--------+--------+-------+--------+
| Odakyu | Apple | 200 | 500 |
| Seibu | Apple | 180 | 400 |
+--------+--------+-------+--------+

結果としてはこの様に2行あるので、仕入件数は2件ということになります。

SQL の場合は、これらの集計は簡単で、次のように記述することで結果を得る事ができます。
SELECT COUNT(*) FROM MyTable WHERE Item='Apple'

各Itemで集計したい場合は、次のように記述します。
SELECT Item, COUNT(Item) FROM MyTable GROUP BY Item

結果は次のようになります。
+--------+-------+
| Item | Count |
+--------+-------+
| Apple | 2 |
| Melon | 2 |
| Orange | 3 |
+--------+-------+

この様な結果を得られるよう、DataTable 上のレコードに対して処理を行う場合はそれなりの手順を踏む必要があります。

以下に、思いついた3つの例をあげますが、得られる結果は全て同じです。
コーディング上、一番スッキリするのは次の書き方だと思います。

■ DefaultView.ToTable() を利用する方法
001://
002:// Example1
003:// DefaultView.ToTable() を利用する方法
004://
005:private DataTable example1()
006:{
007: DataTable dtChild = dtSrc.DefaultView.ToTable("dtChild", false, "Item");
008: DataTable dtParent = dtSrc.DefaultView.ToTable("dtParent", true, "Item");
009: dtParent.Columns.Add(new DataColumn("Count", typeof(int)));
010:
011: DataSet ds = new DataSet();
012: ds.Tables.Add(dtParent);
013: ds.Tables.Add(dtChild);
014:
015: DataRelation rel = new DataRelation("rel",
016: dtParent.Columns["Item"],
017: dtChild.Columns["Item"],
018: false);
019:
020: ds.Relations.Add(rel);
021:
022: dtParent.Columns["Count"].Expression = "Count(Child.Item)";
023:
024: return dtParent.DefaultView.ToTable();
025:}

これは、DeafultView.ToTable() の引数に distinct が指定できる事を利用しています。
順不同ですが、やっていることは大体次のようなことです。
007行では、dtSrc の "Item" 列だけを採用し、全レコードを dtChild に代入しています。
008行では、dtSrc の "Item" 列だけを採用し、かつ重複しないレコードが dtParent に代入されます。
dtParent と dtChild を DataSet に加え、親子関係を設定します。
dtParent に集計用の列"Count"を追加し、そこに dtChild の集計値を代入するように計算式を指定します。

以上の処理手順により dtParent には各項目とその集計数が入ります。

------

次の方法は、元テーブルのレコードを逐一参照して、集計結果を別テーブルに代入するという方法です。

■ DataTable.Select() を利用する方法
001://
002:// Example2
003:// DataTable.Select() を利用する方法
004://
005:private DataTable example2()
006:{
007: DataTable dtRet = new DataTable();
008: dtRet.Columns.Add(new DataColumn("Item", typeof(string)));
009: dtRet.Columns.Add(new DataColumn("Count", typeof(int)));
010:
011: dtRet.PrimaryKey = new DataColumn [] { dtRet.Columns["Item"] };
012:
013: foreach(DataRow drSrc in dtSrc.Rows)
014: {
015: DataRow drRet;
016: string exp = string.Format("Item='{0}'", drSrc["Item"]);
017: DataRow[] drs = dtRet.Select(exp);
018:
019: if (0 == drs.Length)
020: {
021: drRet = dtRet.NewRow();
022: drRet["Item"] = drSrc["Item"];
023: drRet["Count"] = 1;
024:
025: dtRet.Rows.Add(drRet);
026: }
027: else
028: {
029: drRet = drs[0];
030: drRet["Count"] = (int)drRet["Count"] + 1;
031: }
032: }
033:
034: return dtRet;
035:}

集計結果は、dtRet テーブルに入ります。dtRet 上の Item 列の値は重複を許可していません。そのため、019行の Select() メソッドの呼び出しでは、結果が0行か1行しか返ってきません。
dtSrc テーブルの全ての行を対象として、指定列の値に一致するものがなければ新しい行を dtRet に追加(023行~)し、一致するものあれば、既存の"Count" 値を増加させています(029行~)。

------

残る最後の方法は、Example2 とほぼ同じですが、既存レコードの検索に DataTable.Rows.Find() を利用した方法です。
集計結果のテーブル dtRet には、重複するItemが許可されていないという事ならば、Select() メソッドで複数行返ってくる可能性を考慮せずに、単一行を処理した方がスマートなのでは?と考えたのと、Select() と Rows.Find() にどれだけ差がでるのか興味がわいたので、実装してみました。

■ DataTable.Rows.Find() を利用する方法
001://
002:// Example3
003:// DataTable.Rows.Find() を利用する方法
004://
005:private DataTable example3()
006:{
007: DataTable dtRet = new DataTable();
008: dtRet.Columns.Add(new DataColumn("Item", typeof(string)));
009: dtRet.Columns.Add(new DataColumn("Count", typeof(int)));
010:
011: dtRet.PrimaryKey = new DataColumn [] { dtRet.Columns["Item"] };
012:
013: foreach(DataRow drSrc in dtSrc.Rows)
014: {
015: object [] objs = new object[] { drSrc["Item"] };
016: DataRow drRet = dtRet.Rows.Find(objs);
017:
018: if (null == drRet)
019: {
020: drRet = dtRet.NewRow();
021: drRet["Item"] = drSrc["Item"];
022: drRet["Count"] = 1;
023:
024: dtRet.Rows.Add(drRet);
025: }
026: else
027: {
028: drRet["Count"] = (int)drRet["Count"] + 1;
029: }
030: }
031:
032: return dtRet;
033:}


以上の3例を使ってのパフォーマンスの計測結果です。
           +--------------------------------+
| 対象件数 |
+-------+-------+-------+--------+
| 100 | 1000 | 10000 | 100000 |
+----------+-------+-------+-------+--------+
| Example1 | 0.062 | 0.109 | 1.546 | 35.907 |
| Example2 | 0.015 | 0.031 | 0.406 | 5.203 |
| Example3 | 0.000 | 0.015 | 0.140 | 1.703 |
+----------+-------+-------+-------+--------+
※処理に掛った時間(秒)です。
※参考:Windows XP, Intel Core2 2GHz


Example1 が極端に遅い事がわかります。10万件のレコードに対しての集計結果におよそ36秒掛っています。
データ量が多くなってくると顕著ですが、この3例では、Example3 が最も優れている結果となりました。
ちなみに、100万行ほど処理させた結果は次の通りです。
Example1 : 421.258 (7m01s)
Example2 : 52.001 (0m52s)
Example3 : 16.766 (0m17s)


Example1 コードのどこに問題があるのか細部の計測を行ってみたところ、ボトルネックとなっていたのは、008行の DefaultView.ToTable(, true, ) の重複しないレコードを抽出する部分でした。
Example1 ではこの部分がキモであるにも関わらず、上記のようなパフォーマンスの悪さからすると、残念ながら使う場所を限定せざるを得ないレベルのようです。

また、Example2 と比較すると2~3倍程度高速なのが Example3 です。
単一行しかないと分かっているならば、Select を使うよりも Rows.Find() を利用するのが良い様です。

なお、これは、VisualStudio2005 で実験しています。
拍手コメントを見る

テーマ : プログラミング
ジャンル : コンピュータ

こりゃすごい(テトリス1時間)

コレ→日々是遊戯:テトリスは1時間強で作ることができるらしい

見よう見まねでやってみたら、確かに動く、、、ってそりゃそうか。
回転行列とか懐かしい!。

いやしかし、BLOCK 構造体の考え方には目からウロコ。

後半「ベーマガ」のくだりがあるんですが、僕は I/O 派だったのでベーマガは立ち読みくらいしかしてなかったなぁ。
I/O といえば、かの中村光一氏が活躍された場所。
彼のリバーレスキュー PC-8001版とか感動したもの。 拍手コメントを見る

テーマ : プログラミング
ジャンル : コンピュータ

圧倒されっぱなし

今日は朝から「わんくま同盟」さん開催の勉強会ってのに(初)参加してきました。

某大手社さんは来月発表予定の機能について、twitter による中継なし、ブログへの転載なし、ビデオやカメラによる撮影なし!とうのが条件で、その内容を発表されていたのですが、、、

あそこって、こういうことができる社風でしたっけ?

新機能のデモがあるたびに沸く会場。
何気にかゆいところに手が届く開発環境になっているようです。

ちなみに隣の席の女性参加者の方がおっしゃっていたのですが、その女史の会社では、Flash での開発がストップして、Siliverlight に全面移行したそうです。聞き間違いかも。彼女がいる部門だけなのかも。

全体的にいいもの見させていただきましたし、刺激も受けたのですが、いざ帰宅してみると、さて次のステップはどうしよう?と行き止まりそうな自分を感じて、「やばいなぁ」と思ってしまいました。

とりあえず、リフティング練習してから寝ることにします。 拍手コメントを見る

テーマ : プログラミング
ジャンル : コンピュータ

ブログ内検索
プロフィール

雷ぶ

Author:雷ぶ

最近の記事
最近のコメント
最近のトラックバック
カテゴリー
月間アーカイブ
ブロとも申請フォーム

この人とブロともになる

RSSフィード
リンク

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。