C#

    考えたことがあたってるとちょっとうれしくなるよね

    PAYDAY2 Blacklist Assist

    最近更新中のPAYDAY2のBlacklist MODの補助ツール
    ※特に理由が無い場合は最新バージョンを使用してください。


    ━━━━━━━━━━━━━━━━━━━━━━━━━━━

    ちょうど一年前くらいにとある理由でプログラムを書くことになった。

    ・読み込むデータは用意されている
    ・解析手法も用意されている
    ・結果の見せ方も大体既存の参考資料がある

    ・使用言語は自由
    ・とりあえず動けばいいらしい

    というゆるい開発だったので、これまでの趣味の延長で作り始めた。

    やはりGUIはあったほうが便利だろうということ、グラフや表で結果を示したかったこともあり、言語はC#を選んだ。
    (というより割とまともに扱えるものがC#以外にな・・・)

    読み込むデータは大体以下のようになっていた

    data.csv
    #ヘッダー開始(ない場合もあり、ない場合はヘッダー終わりもなし)
    #ヘッダー終わり
    日時,データ1-1,データ2-1,...(複数列)
    日時,データ1-2,データ2-2,...
    日時,データ1-3,データ2-3,...
    以下数千から数百万行

    ・解析手法でのちにデータを消すことが分かっている
    ・とりあえず動けばいいらしい

    ということから、今までの経験から得た技術のListを用いてデータを格納していることにした。

    Step0
    List1-1(データ1-1を格納)
    List1-2(データ1-2)
    List1-3(データ1-3)
    List1-4(データ1-4)
    List1-5(データ1-5)

    と順番に格納していくわけである。(データ2-は表記省略)

    解析する間に

    Step1
    List1-1(データ1-1)
    List1-2(データ1-2)
    List1-3(データ1-3)

    List1-4(データ1-5)

    Step1
    List1-1(データ1-1)

    List1-2(データ1-3)

    List1-3(データ1-5)

    と歯抜けになっていく。

    Forで回していたと思うので、ここはList.RemoveAt(Int32)でデータを抜いていった。

    しかし、遅い、遅い、待てども待てども終わらない。何が悪い?

    既存技術の組み合わせということがあって、同様の処理をするプログラムがフリーで公開されている。
    それらを用いると時間がかかるものでも1分程度、自作のものは1時間以上かかる。

    明らかにおかしい!
    いろいろな場所にブレークポイントを設置した結果、上記の箇所が原因であると特定された。
    (ここまでに一通り作ろうと思っていた機能をすべて盛り込み済みで、自分の開発した中では最大規模のプログラムになっており、特定作業も大変であった)

    どうやら、要素を削除するたびに、要素を前に詰めているようだった。
    言葉にすると伝わりづらいだろうか、昔作った画像が出てきたので参考に

    270407_prog.png

    数百万*数列あるデータに対して、列ごとにデータを一つ消すたびに後ろからデータを詰めていたらしく、計算量は莫大な量になっていたということである。

    今までは扱うデータ量が多くとも数千程度だったので、詰め詰めしても大して気にならなかったようだが、今回は数百万*十数と今までとは全く違う規模のデータ量なので問題が浮き彫りになった。

    消すとダメ・・・ダメ・・・なら飛ばせばいいじゃない!とひらめいた。
    お風呂に入っていた時だったような気がする。ぽけーっとしてるとひらめくことあるよね。

    270407_prog2.png

    要素を作成する際に、前後関係と要素データを合わせて持つようにする。

    Step0
    List1-1(データ1-1を格納、前なし、後1-2)
    List1-2(データ1-2、前1-1、後1-3)
    List1-3(データ1-3、前1-2、後1-4)
    List1-4(データ1-4、前1-3、後1-5)
    List1-5(データ1-5、前1-4、後なし)

    といった具合である。
    すると

    Step1
    List1-1(データ1-1を格納、前なし、後1-2)
    List1-2(データ1-1、前1-1、後1-3)
    List1-3(データ1-2、前1-1、後1-5)
    List1-4(データ1-4、前なし、後なし)
    List1-5(データ1-5、前1-3、後なし)

    といった具合になり、詰め詰めしなくともすむ。
    これは!すごいのでは!

    と、独自でクラスを作り始めたのだが、途中で「もしかして、自分が思いつくのなら既にあるのでは?」と考え、検索してみると

    「連結リスト(片方向リスト、双方向リスト)」

    というものが、これにあたるようで、C#にももちろん標準で実装されていると分かった。

    こちらを利用したところ、ほかのフリーソフト並みの爆速で処理が終わるようになった。
    今度のボトルネックはストレージからの読み込み速度だが、ここはいまだに難儀している。

    何はともあれ、

    「何が悪い?」

    から

    「消すとダメ・・・ダメ・・・なら飛ばせばいいじゃない!とひらめいた。」

    「もしかして、自分が思いつくのなら既にあるのでは?」


    やっとタイトルである、「考えたことがあたってるとちょっとうれしくなるよね」

    先人たちも同じところで躓いて、同じことを考えていたと分かるとうれしい!

    もちろん、そんなところで時間を(完全ではないにせよ)無駄にしないためにも基礎知識をきちんとつけておくべきではある。

    でも、うれしいよね。

    と、なぜ今更こういうことを書いたかというと、先ほども同じ経験をしたからである。

    ・ユーザ登録型
    ・ユーザごとに個人ページを持ち、ブログのようなことができる

    といったものをRailsの勉強で作ろうとしてるのだが、何分データベースもまともに触ったことがないので、データベースをどう作ればいいのかとここ数日悩んでいた。

    初期の想像

    Table ユーザ情報
    ID Name Password など
    1 Taro ababa
    2 Hana gegeg

    Table Taroさんのブログ
    記事ID Title Text など
    1 te fefef
    2 fdf fefe

    Table Hanaさんのブログ
    記事ID Title Text など
    1 te fefef
    2 fdf fefe

    これではユーザが増えるごとにテーブルが増える・・・テーブルが増えるとパフォーマンスが悪いと、ではどうすれば・・・。

    と先ほど思いついたのが

    Table ユーザ情報
    ID Name Password など
    1 Taro ababa
    2 Hana gegeg

    Table ブログ情報
    記事ID 投稿者ID Title Text など
    1 1 fefe cdcd
    2 2 df cece

    ブログ記事情報を一つのテーブルにまとめ、中に投稿者IDを作ればいいのだ!なるほど。

    これが正解らしい。

    と、いうことで、ふと前の経験を思い出したわけである。
    スポンサーサイト

    [覚書] C# 非同期処理の例外処理外エラー個所の特定

    PAYDAY2 Blacklist Assist

    最近更新中のPAYDAY2のBlacklist MODの補助ツール
    ※特に理由が無い場合は最新バージョンを使用してください。


    ━━━━━━━━━━━━━━━━━━━━━━━━━━━

    最近非同期処理を実装したプログラムを書いているのだが、非同期で例外処理をきちんとしていないとエラーがとても厄介なことになると学んだ。
    この場合のエラー個所の特定方法についての覚書。

    まず、非同期処理内で例外処理(try catch)をしていない場合に表示されるエラーについて。

    非同期処理内、例外処理なし
    タイトル「TargetInvocationExceptionはハンドルされませんでした。」
    追加情報「呼び出しのターゲットが例外をスローしました。」
    rainflow_async_error1.jpg


    非同期処理内、例外処理あり
    try{ /// }catch(Exception ex){ ex.ToString(); } とりあえず例外表示するだけ
    rainflow_async_error2.jpg


    同期処理内、例外処理なし
    タイトル「ArgumentOutOfRangeExceptionはハンドルされませんでした。」
    追加情報「インデックスが範囲を超えています。負でない値で、コレクションのサイズよりも小さくなければなりません。」
    rainflow_async_error3.jpg


    今回引っかかったのは、いろいろといじって、いじりすぎて、いざデバッグを実行したところ、”非同期処理内、例外処理なし”のエラーが表示されたが、いじった個所が多すぎてどこが問題なのかわからなかったことだ。
    とりあえず、該当しそうな箇所に try{ /// }catch(Exception ex){ ex.ToString(); } を入れてみたが”非同期処理内、例外処理なし”のエラーしか出ない。

    困ってグーグル先生に聞いたところ
    「トレースログを出力する」
    とよいらしいという情報を得た。
    が、ログを出力するコードを追加したもののログを出力する前にエラーが出ているようで追加の情報は得られなかった。
    (付け焼刃でコードを追加したので追加箇所が正確ではない可能性がある。)

    これは困ったとだめもとで
    ”非同期処理内、例外処理なし”のエラーダイアログの「例外の詳細をクリップボードに追加」を実行した。


    System.Reflection.TargetInvocationException はハンドルされませんでした。
    HResult=-2146232828
    Message=呼び出しのターゲットが例外をスローしました。
    Source=mscorlib
    StackTrace:
    場所 System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
    場所 System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)
    場所 System.Delegate.DynamicInvokeImpl(Object[] args)
    場所 System.Windows.Forms.Control.InvokeMarshaledCallbackDo(ThreadMethodEntry tme)
    場所 System.Windows.Forms.Control.InvokeMarshaledCallbackHelper(Object obj)
    場所 System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
    場所 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
    場所 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
    場所 System.Windows.Forms.Control.InvokeMarshaledCallback(ThreadMethodEntry tme)
    場所 System.Windows.Forms.Control.InvokeMarshaledCallbacks()
    場所 System.Windows.Forms.Control.WndProc(Message& m)
    場所 System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
    場所 System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
    場所 System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
    場所 System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
    場所 System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 reason, Int32 pvLoopData)
    場所 System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
    場所 System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
    場所 System.Windows.Forms.Application.Run(Form mainForm)
    場所 Rainflow.Program.Main() 場所 C:\Users\xxxxxxx\Documents\Visual Studio 2015\Projects\Rainflow\Rainflow\Program.cs:行 19
    場所 System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
    場所 System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
    場所 Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
    場所 System.Threading.ThreadHelper.ThreadStart_Context(Object state)
    場所 System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
    場所 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
    場所 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
    場所 System.Threading.ThreadHelper.ThreadStart()
    InnerException:
    HResult=-2146233086
    Message=インデックスが範囲を超えています。負でない値で、コレクションのサイズよりも小さくなければなりません。
    パラメーター名:index
    ParamName=index
    Source=mscorlib
    StackTrace:
    場所 System.ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument argument, ExceptionResource resource)
    場所 System.Collections.Generic.List`1.get_Item(Int32 index)
    場所 Rainflow.DGV.d__3.MoveNext() 場所 C:\Users\xxxxxxx\Documents\Visual Studio 2015\Projects\Rainflow\Rainflow\Control.cs:行 316
    --- 直前に例外がスローされた場所からのスタック トレースの終わり ---
    場所 System.Runtime.CompilerServices.AsyncMethodBuilderCore.<>c.b__6_0(Object state)
    InnerException:


    Message=インデックスが範囲を超えています。負でない値で、コレクションのサイズよりも小さくなければなりません。
    この情報は大きい!そしてその下の
    場所 Rainflow.DGV.d__3.MoveNext() 場所 C:\Users\xxxxxxx\Documents\Visual Studio 2015\Projects\Rainflow\Rainflow\Control.cs:行 316
    ここが問題だそうだ。

    ということで、修正しました。
    例外処理はちゃんとしようね。
    メッセージ表示するだけでも効果あるある。

    近況報告

    PAYDAY2 Blacklist Assist

    最近更新中のPAYDAY2のBlacklist MODの補助ツール
    ※特に理由が無い場合は最新バージョンを使用してください。


    ━━━━━━━━━━━━━━━━━━━━━━━━━━━

    ●もうすぐ誕生日ー
    なので、ほしいものリスト作りました!
    http://www.amazon.co.jp/registry/wishlist/AHIDXQ3XIJNR

    気軽に送ってね★ ※お塩とかだめよ・・・だめだからね。フリじゃないからね。

    ●SSDと液晶買ったよー
    ○ついに手を出してしまったSSD
    ウルトラ II SSD SDSSDHII-240G-J26C
    J25からJ26になり、省電力を実現した代わりとして読み書き速度が若干低下したそうだが、5400rpmのHDDにシステム領域を置いていた自分からすれば世界が変わったことは言うまでもない。
    もっと早くに買っておけばよかった!でも大満足!

    ○ついでに買ってしまった液晶
    これはちょっと後悔中
    JAPANNEXT JN-IPS3200FHD フルハイビジョン液晶モニター IPS-ADSパネル
    発売前セール中!って書かれて通常価格よりも1万円程度安かった?ので衝動買いしてしまったが・・・届いてみて、やはり大きい。大きいがFHDなので今まで満足してみていた動画などのあらが目立つように笑
    そして、ノングレアだと思い買ったが実際はハーフグレア、画面が暗くなると自分の姿が映る。
    ノングレアにするフィルムがあるようなので購入検討中(ほしいものリストに入ってるから送ってくれてもいいのよ♥)

    発色が悪い
    視野角は非常に良い。

    支払方法を代引きにしていていたにもかかわらず、JAPANNEXTの手違いで運送業者から代引き料金を請求されなかったので購入先に問い合わせたところ銀行口座を指定され、入金した。
    対応は素早く言葉も丁寧だったが、うーん。慣れてないのかな?


    ●プログラミング
    ○PD2BLA
    相変わらず全然いじってない!
    適当にいじったらPD2BLの設定ファイルの設定掻き消しちゃうようになった・・・もうコードがごちゃごちゃして読みづらいので基礎部分から書き直したい。
    スキルも新しくなったらしいのでそれに対応するようコード書き換えないと(またXMLとのにらめっこ)

    ○その他プログラミング
    最近とある理由でRainflow法のプログラムを書いているが・・・Rainflowの基礎論文とASTM E1049-85のアルゴリズムで同じ結果にならないような気がすることに気が付いて困惑。
    E1049-85を手に入れて読んでからどうするか考えねば。

    プログラムを書くときに常々拡張性に考慮して書かなければと思っているのだが、今回もこの拡張性に考慮しない書き方をしてしまったので、独自クラスの宣言を書き換え、それがために関連する個所百数を書き換え・・・いや大変。

    収納するデータはこちら
    NoTime(Datatime)Range(double)Cycle(bool)
    1T1R1C1
    2T2R2C2
    3T3R3C3
    nTnRnCn



    最初に書いたクラスがこちら
    Public Class  Raindrop{
    public List<Datatime> time = new List<Datatime>();
    public List<double> range= new List<double>();
    public List<bool> cycle = new List<bool>();
    }

    このクラスの宣言は小文字で行うこととして
    Raindrop randrop = new Raindrop


    これでも初期段階の処理では問題なかったのだが、これであると、行ごとに関連したデータとして扱っているというよりは、列で関連したデータとして扱っているので、ソートした後にTとRの関係を見ようとすると困ったことになる。

    たとえば、Rangeでソートしたとする。すると・・・

    NoTime(Datatime)Range(double)Cycle(bool)
    1T1R2C1
    2T2R1C2
    3T3R3C3
    nTnRnCn


    こうなる。
    ただ、ソートは簡単。

    rainfrop.range.sort()


    これだけで昇順に並べ替えてくれる。
    しかし、これでは困る・・・raindrop.time[1]のrangeを取り出そうとraindrop.range[1]を指定するとR1ではなくR2が出てくる・・・。

    そもそも、関連しているデータは列ではなく行なので、行で割るように作り直す。
    ついでに、メモリ使用量を考慮してDatatime型をlong型に置き換えた。(が、あまり効果的ではなかった?)

    行で扱う
    Public Class  Raindrop{
    public long time { get; set; }
    public double range { get; set; }
    public bool cycle { get; set; }
    }

    List<Raindrop> raindrop = new List<Raindrop>();


    これで行を積み重ねていく形に変えられた、しかし、これでは先ほどとは違い、rangeでのソートができないので、独自クラスとして実装する。
    参考にしたサイトはこちらhttps://www.ipentec.com/document/document.aspx?page=csharp-list-sort-use-comparison
    public static int Compare(RainDrop x, RainDrop y)
    {
    if (x.range < y.range)
    {
    return -1;
    }
    else if (x.range > y.range)
    {
    return 1;
    }
    else
    {
    return 0;
    }
    }

    これを宣言し、

    raindrop.Sort(Raindrop.Compare);

    と、Sortの方法に自分で作ったクラスを指定してやるとうまくいく。
    ソートというのは結局、2つを比較し大小を返しているだけらしい。
    大小を返せばあとはSortクラスが勝手に入れ替えを行ってくれる。


    NoTime(Datatime)Range(double)Cycle(bool)
    1T2R2C2
    2T1R1C1
    3T3R3C3
    nTnRnCn


    と、そういう話は今回は結構どうでもいいことで、問題はアルゴリズム。
    どうしたものやら。

    PAYDAY2 Blacklist Assist v1.14.0.14

    PAYDAY2 Blacklist Assist

    最近更新中のPAYDAY2のBlacklist MODの補助ツール
    ※特に理由が無い場合は最新バージョンを使用してください。


    ━━━━━━━━━━━━━━━━━━━━━━━━━━━

    ■ダウンロード
    ZIP
    http://www.mediafire.com/download/asbraxe4ggwj84b/PAYDAY2BlacklistAssist1140.zip

    Ranking機能は試験運用中

    ●今回のアップデート要約
    * SteamAchievementDetailの絞込み機能を追加
    * クリップボードの監視機能を追加

    ●発見済みの不具合

    ●アップデート予定
    Ranking機能の改善

    20160215212514.png
    1_14_0_0_3.jpg



    2015/02/15
    v1.14.0.14
    追加
    * 実績の解除率の表示を追加
    * 難易度別クリア回数の表示を追加
    * スキル取得状況の表示を追加
    * 常に最前面に表示する設定の追加
    * 一部のエラー出力時にログを出力する機能を追加
    * クリップボードの監視機能を追加
    - 以下の対応形式文字列が新規にクリップボードに挿入されたときに実行
    種類 例 書式
    SteamID STEAM_0:1:37587949 STEAM_0:A:B
    SteamID3 [U:1:75175899] [U:1:B*2+A]
    SteamID64 76561198035441627 76561197960265728 + B * 2 + A
    B = (SteamID64 - 76561197960265728)/2
    A = (SteamID64 - 76561197960265728)%2

    * 自由なコピー形式に新しいコードを追加
    コード 対応する文字列
    [Ap] 実績解除率
    [Skill_M] MASTERMINDツリーの取得数
    [Skill_E] ENFORCERツリーの取得数
    [Skill_T] TECHNICIANツリーの取得数
    [Skill_G] GHOSTツリーの取得数
    [Skill_F] FUGITIVEツリーの取得数

    * SteamAchievementDetailに絞り込み機能を追加
    - 絞り込み方法は以下を参照
    - https://technet.microsoft.com/ja-jp/library/ms187489(v=sql.105).aspx


    改善
    * DataGridViewの表示速度の向上(したはず)
    * 不要なエラーログの表示を削除
    * 例外処理を変更
    * SteamAchievementDetailで実績名が正しく表示されていなかった不具合の修正
    * SteamAchievementDetailの時刻表示の初期値を変更
    - yyyy年MM月dd日 HH時mm分 から yyyy年M月d日 H時m分

    PAYDAY2 Blacklist Assist v1.13.2.0

    PAYDAY2 Blacklist Assist

    最近更新中のPAYDAY2のBlacklist MODの補助ツール
    ※特に理由が無い場合は最新バージョンを使用してください。


    ━━━━━━━━━━━━━━━━━━━━━━━━━━━

    ■ダウンロード
    ZIP
    http://www.mediafire.com/download/p35gcp5e6fcxa1w/PAYDAY2BlacklistAssist1132.zip

    Ranking機能は試験運用中

    ●今回のアップデート要約
    * SteamAchievementDetailの不具合の改善
    * SteamAchievementDetailの時刻表示をカスタマイズできるように変更

    ●発見済みの不具合
    AchievementDetailで解除時間が重複している場合実績が正しく表示されない。
    1_13_2_0detail.jpg



    ●アップデート予定
    Ranking機能の改善

    PD2BLA1_13_1_2_1.jpg
    PD2BLA1_13_1_2_2.jpg


    2015/01/13
    v1.13.1.2
    改善
    * SteamAchievementDetailの不具合の改善
    * SteamAchievementDetailの時刻表示をカスタマイズできるように変更
    - 設定例
    yyyy年MM月dd日 HH時mm分

    使用できる書式は以下を参照
    http://dobon.net/vb/dotnet/string/datetimeformat.html

    追加
    * SteamAchievementDetailにDataGridViewを追加
    * SteamAchievementDetailに表示する実績アイコンをダウンロードする機能を追加
    - Setting2のIcon Downloadを実行


    2015/01/13
    v1.13.2.0
    改善
    * SteamAchievementDetailの時刻表示の初期値を変更
    - yyyy年MM月dd日 HH時mm分 から yyyy年M月dd日 HH時mm分
    上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。