RiderのHeap Allocations Viewerについて
コーディング規則をみていると「フィールドにアンスコつけるな!」「privateは冗長だ!」とReSharperにコーディング規則を支配された私には哀しみしかないのですが、ふと「全員がReSharperを使えば問題ないんじゃね」と思ったのでRiderを使ってもらうべく記事書きたいと思います。
というわけで次回のビルドからPublic EAP(になったらいいな)らしいですが、その前にRiderに新しいPluginができました。
ほとんどの人は真ん中のReSharper Unityに目が行くと思いますが、残念ながら私はUnityやってないので今回は一番右の紹介です。(まあHeap Allocations ViewerもどちらかというとUnity向け)
なお私はメモリ等については詳しくないので解説部分とかは軽く読み流してください
Heap Allocations Viewerとは
直訳すると「ヒープ割り当てビューア」(訳:Google先生)
ようするにヒープに割り当てられる処理をわかりやすく表示してくれる拡張機能です。
ヒープに割り当てられるとGCゴミが発生して場合によってはパフォーマンスに大きく影響するのでなるべく表示されなくしたいですね。
例
まあHeap Allocations Viewerを入れるとすぐわかるのですが、表示される例を出していきたいと思います。
new
一番わかりやすいヒープ確保する処理はnew
です。つまり参照型のインスタンス作成。
なおintやdoubleなどの構造体はnewしても表示されません。まあ構造体はスタックなので当たり前ですが。
LINQ
次はみんな大好きLINQです。LINQはメソッドチェインで連続して処理を書けるのでとても見やすく書きやすいですが、実行性能は・・・どうなんでしょう? WhereやSelectなどよく使われるメソッドは最適化がかけられていると風のうわさで耳にしましたがよくわかりません。
Heap Allocations ViewerですとLINQメソッドというだけで表示されます。(ちなみにToList()やFirstOrDefault()などのよく終端で使われるメソッドは表示されません。)
まあLINQは繋げた分だけEnumeratorを撒き散らかしてこれがGCゴミになるとどこかで読んだ気がしますが出典不明なので「LINQはなるべく繋げないほうがいいよー」とだけ覚えておけばいいと思います。
デリゲート
デリゲートというかメソッドグループというかなんというか。ようするにメソッドの引数にメソッドを渡すやつです。 具体的に見てみましょう。
private int InvokeSampleMethod(Func<int,int> func)=>func.Invoke(10); private int OnSampleMethod(int i) => ++i;
というメソッド(例なので全く意味のないメソッドです)に対して、
InvokeSampleMethod(x=>++i); InvokeSampleMethod(OnSampleMethod); InvokeSampleMethod(i=>OnSampleMethod(i));
という3つの書き方をします。メソッドの実行結果としてはほぼ同じですが、この中に1つだけHeap Allocations Viewerで表示されないものがあります。
それは・・・
です。どうやら正解は自己完結しているラムダ式の模様。詳しくは
neue cc - Unityでのボクシングの殺し方、或いはラムダ式における見えないnewの見極め方
をお読みください。
なおメソッドを直接引き渡すやりかたは他にイベントがあります。ということはイベントの購読も自己完結しているラムダ式で書くことで無駄なGCゴミを生成しないと思いきやラムダ式だとイベントの購読を解除できません!残念無念。
クロージャ
デリゲートやったのでついでによくあるパターンとしてこんなのがあると思います。
int i=0; InvokeSampleMethod(x=>x*i);
これはHeap Allocations Viewerだとこうなります。
上の大先生のブログにも書いてありましたが、外の変数をキャプチャする場合、クラスが新しく作られるのでGCゴミが生まれる模様。ただこのパターンはよく見かけるというか、このパターンが便利すぎるので大人しく諦めましょう。しょせんクラス1個分(でもクラス1個分・・・)
見た感じ外の変数を取り込むほどクラスにフィールドが生成されるのでなるべく外の変数を取り込まないようにすると幸せになれるかもしれません。
可変長引数
他にHeap Allocations Viewerが表示される例として可変長引数(params)があります。
例としてDebug.WriteLine(string format, params object[] args)
を書いてみると、
こうなります。
省略可能ですしnew object[]{"hoge","bar"}
などと書かなくてよいので個人的には好きですが、結局配列を生成しちゃうので引数が少ない想定のときはオーバーロードで対応するのがスピードアップへの近道ですかね。(でも配列1個分なら別にゴニョゴニョ)
ボックス化
ところでさっきのスクショを見ていると、引数のところに赤いアンダーラインが引かれてると思います。
わざと赤いラインが引かれるように書いたのですが、objectの引数に構造体を渡すと表示されます。 ようするに構造体(Value Type)からobject(Reference Type)に変更されてせっかくスタックのはずがヒープになってしまってGCゴミガーということです。
ボックス化もパフォーマンスに大きく影響するので(なんとボックス化するのとしないのでは1億回ループを回したときに0.2秒の差がつく!らしい?)できるだけ避けていきたいですね。
他のボックス化の例として、構造体でGetType()を使ったり、自分で定義した構造体でToString()をオーバーライドしないで使ったり、””+1
と書くとボックス化が発生します。
まとめ
以上、Riderの新しいプラグインの解説(?)でした。このプラグインを使ったからといって別にコーディングが楽になったりしないし、表示されるのを気にしてコードを複雑にするのは個人的にアンチパターンだと思います。ただ、チームでやっている場合、誰かがこれを使っていれば、もしかしたら無駄な処理に気付けるかもしれません。なのでMacでC#やっている人は積極的に使ってみてほしいですね!Riderも一緒に!!
Xamarin SutdioでNuGetパッケージを簡単に作る
NuGetパッケージは依存関係を自動(?)で解消してくれたりリポジトリを検索してインストールしたりで利用する分には簡単(??)なのですが、いざ作成しようとすると.nuspecを編集したりコマンド叩かなきゃいけなかったりで結構めんどくさいです。
なので簡単にNuGetパッケージを作成するXamarin Studio Addinを作成しました。
なお簡単っていっても個人運用向けで、ちゃんと公開する場合はしっかり.nuspecを編集しないといけないので結局面倒くさいです。
使い方
一番簡単な使い方。
1
からdllをダウンロードして
/Applications/Xamarin Studio.app/Contents/Resources/lib/monodevelop/AddIns
とかに入れる。
2 Xamarin Studio起動してプロジェクト開く。
3 ソリューションダブルクリックしてDescriptionに適当な文字を入れる。(ビルドしてなかったらビルドする)
4 MainMenu→Edit→Make Nuspecで.nuspecファイル作成
5 MainMenu→Edit→Make PackageでNuGetパッケージ作成
もっと詳しく
一応、上記のやり方でパッケージは作れるのですが大抵残念仕様です。なのでしっかり.nuspecを編集します。
ソリューションに追加された.nuspecファイルを見てみましょう。
<?xml version="1.0" encoding="utf-8"?> <package> <metadata> <id>$id$</id> <version>$version$</version> <authors>$author$</authors> <licenseUrl>http://LICENSE_URL_HERE_OR_DELETE_THIS_LINE</licenseUrl> <projectUrl>http://PROJECT_URL_HERE_OR_DELETE_THIS_LINE</projectUrl> <iconUrl>http://ICON_URL_HERE_OR_DELETE_THIS_LINE</iconUrl> <requireLicenseAcceptance>false</requireLicenseAcceptance> <description>$description$</description> <releaseNotes>Summary of changes made in this release of the package.</releaseNotes> <copyright>$copyright$</copyright> <tags>Tag1 Tag2</tags> <dependencies> <dependency id="Xamarin.Forms" version="2.3.1.114" /> <dependency id="Xamarin.Android.Support.Animated.Vector.Drawable" version="23.3.0" /> <dependency id="Xamarin.Android.Support.Design" version="23.3.0" /> <dependency id="Xamarin.Android.Support.v4" version="23.3.0" /> <dependency id="Xamarin.Android.Support.v7.AppCompat" version="23.3.0" /> <dependency id="Xamarin.Android.Support.v7.CardView" version="23.3.0" /> <dependency id="Xamarin.Android.Support.v7.MediaRouter" version="23.3.0" /> <dependency id="Xamarin.Android.Support.v7.RecyclerView" version="23.3.0" /> <dependency id="Xamarin.Android.Support.Vector.Drawable" version="23.3.0" /> </dependencies> </metadata> <files> <file src="../Sample/bin/$Configuration$/Sample.dll" target="lib/" /> <file src="../Sample.iOS/bin/$Configuration$/Sample.iOS.dll" target="lib/" /> <file src="../Sample.Android/bin/$Configuration$/Sample.Android.dll" target="lib/" /> </files> </package>
Xamarin Studioで開くとシンタックスハイライトが全く効かないのでつらいですが見てみると$id$とか$$で囲われたのがあると思います。
これはMake Package
を実行すると対応した値に変換されます。
以下がその対応表になります。
From | To | Description |
---|---|---|
$id$ | solution.Name | ソリューションの名前 |
$title$ | solution.Name | ソリューションの名前 |
$version$ | solution.Version | ソリューションの名前 |
$author$ | solution.AuthorInformation.Name | ソリューションの製作者情報の名前 |
$description$ | solution.Description | ソリューションの詳細 |
$copyright$ | solution.AuthorInformation.Copyright | ソリューションの製作者情報のコピーライト |
$Configuration$ | IdeApp.Workspace.ActiveConfigurationId | 現在のコンフィグ(DebugとかReleaseとか) |
ただし、$hoge$はdependenciesでは使えません。そこで使うことを想定してないので書くととても残念な事になります。
また、dependencyがいっぱいありますがこれは各プロジェクトのpackages.configの中身をそのまま持ってきた感じになります。
こんな感じ↓
private static IEnumerable<XElement> GetDependencies() => ProjectService.CurrentSolution.GetAllProjects() .Where(x => File.Exists(x.BaseDirectory.Combine("packages.config"))) .SelectMany(x => XElement.Load(x.BaseDirectory.Combine("packages.config")).Elements("package")) .Select(x => new XElement("dependency", new XAttribute("id", x.Attribute("id").Value), new XAttribute("version", x.Attribute("version").Value))) .Distinct(x => x.Attribute("id")?.Value);
また、filesも同じくプロジェクトをごそっと持ってきた感じになります。 こんな感じ↓
private static IEnumerable<XElement> GetFiles() => ProjectService.CurrentSolution.GetAllProjects() .Select(x => new XElement("file", new XAttribute("src", Path.Combine("..", x.GetOutputFileName(IdeApp.Workspace.ActiveConfiguration) .ToRelative(ProjectService.CurrentSolution.BaseDirectory)).Replace( IdeApp.Workspace.ActiveConfigurationId, "$Configuration$")), new XAttribute("target", "lib/")));
テストプロジェクトがあった場合はfilesに含まれてしまったり、全部lib/なので全プラットフォーム対象になってしまったりなのでfileは要編集です。
そしてそれぞれよしなに編集した.nuspecファイルがこちらになります。
<?xml version="1.0" encoding="utf-8"?> <package> <metadata> <id>$id$</id> <version>$version$</version> <authors>$author$</authors> <licenseUrl>https://opensource.org/licenses/MIT</licenseUrl> <projectUrl>https://github.com/crocus7724</projectUrl> <requireLicenseAcceptance>false</requireLicenseAcceptance> <description>$description$</description> <releaseNotes>First Release.</releaseNotes> <copyright>$copyright$</copyright> <tags>Xamarin, Xamarin.Forms</tags> <dependencies> <dependency id="Xamarin.Forms" version="2.3.1.114" /> </dependencies> </metadata> <files> <file src="../Sample/bin/$Configuration$/Sample.dll" target="lib/portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10" /> <file src="../Sample.iOS/bin/$Configuration$/Sample.iOS.dll" target="lib/Xamarin.iOS10" /> <file src="../Sample.Android/bin/$Configuration$/Sample.Android.dll" target="lib/MonoAndroid10" /> </files> </package>
.nuspecファイルを編集した場合は⌘+Sで保存してください。残念ながらオートセーブは未実装です。
また、一応申し訳程度の設定があります。
適当英語で申し訳ないのですが、
- 出力先ディレクトリ(Defaultだと.slnと同じ階層とか)
- Releaseビルドを使うか($Configuration$が常時Releaseになります
Debugって直接書いてたら意味ないオプション) Make Package
前にビルドするか- 作ったパッケージを
nuget push
するか
を設定できます。
ただ、Auto Publishをする場合は事前にTerminalでnuget setapikey <key> -source <url>
をする必要があります。
今回はローカルのサーバーにDockerでNuGet ServerをApiKey=nuget
、Publish URL=http://192.168.1.11:5001
で立ててそれを使ってみました。
この場合のAPIキーの設定はこんな感じ。
nuget setapikey nuget -source http://192.168.1.11:5001
それではMake Package
をしてみましょう。
上記のようにエラーっぽいメッセージがなければ成功です。 あとはNuGetのSourcesにURLを足して
NuGetパッケージマネージャーを見てみると
追加するとちゃんとクラスも表示されているので大成功です!(もっと特徴的な名前にすればよかった)
最後に
実はこれソースコードを見ればわかるのですが裏で結構がちゃがちゃやってます(だいたいnugetのせい)。
なので結構バギーです。もしよろしければ使ってみて動かなかった場合は@crocus7724までお知らせください。
Rider Build 11でソリューションが読み込めない
追記
10/22のBuild 12にて日本語でも問題なくソリューションを開けることを確認致しました。
追記ここまで
現在(2016/9/27)Riderの最新のバージョンであるビルド11でソリューションが開けなくなるバグがあります。
具体的に書くとソリューションを開こうとするとエラーっぽいのが表示されてその後もう一度開こうとすると永遠にソリューションの読み込みをします。初期の頃を思い出す・・・
このバグに対する対処が下記に載っています。
https://youtrack.jetbrains.com/issue/RIDER-2431
対処法はなんと・・・
_人人人人人人人人人人人人人人人人人_
> システム言語を英語にする!!! <
 ̄Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y ̄
私は英語苦手マンなのでOSの言語はもちろん日本語なのですがこれが原因でした・・・。
あと中国語もアウトっぽいですね。
この手のバグはいつになったら駆逐されるのでしょうか・・・。
Public EAP!?
そして上記のyoutrackを見て気づきました。
なんとラベルに Public EAPが!!!!!!!!!!
他のやつ見てみるとついていたりついてなかったりまちまちで判断に困るのですがもうすぐPublic EAPがくると思うと感慨深いですね。。
とりあえずBug FixされたBuild 12の公開はよ
macOS SierraでKarabinerが使えないときの応急処置
9/21にmacOS Sierraが正式リリースされました。
私も勇んでアップデートしたのですがやはりいろいろ問題があったり(ログインパスワード勝手に変えられたりGoogle Chromeがバグっぽい挙動したり)
その中でも特に困ったのが Karabinerが使えない 。
macOSのUSキーボードユーザーにとっては致命的です。
これの回避方法は他のアプリを使うなりすればいいのですがKarabinerの公式サイトを見たら単純なキーの変更を行えるKarabiner-Elementsというものを開発中らしい。
今回はそのKarabiner-Elementsを使ってとりあえずかなを使えるようにしてみました。
インストール
まずはインストール。
https://github.com/tekezo/Karabiner-Elements
のページにある
https://pqrs.org/latest/karabiner-elements-latest.dmg
からイメージファイルを落としてきます。
インストール自体は他と変わらなくハイハイ押せばいいのですが、最後に外付けキーボード使ってないのにキーボードが見つかりません的なウィンドウが出たら何も押さず閉じちゃって大丈夫です。
設定ファイル
インストールが終了したら設定ファイルを書きます。
設定ファイルの置き場所は~/.karabiner.d/configuration
です。なかったらたぶんKarabiner-Elementを起動すれば作られるはず。
最初はconfigurationの中身は空っぽなので適当にターミナルから
vi ~/.karabiner.d/configuration/karabiner.json
で設定ファイルを作成します。そして試しに
{ "profiles": [ { "name": "Default profile", "selected": true, "simple_modifications": { "caps_lock": "japanese_kana", "right_shift":"japanese_eisuu" } } ] }
と編集して保存してみてください。
そうするとcaps_lock
で日本語入力が、右Shiftキー
で英字入力ができると思います。
現在はまだ開発途中で単純なキーの入れ替えしかできないようなのでKarabinerのようにCommandキー1回押すと日本語入力みたいな器用な真似はできません(たぶん)。
なので個人的に使わないキーであるcaps_lockと右Shiftキーに英語日本語キーを割り当てました。
他のキーに割り当てたい場合は
https://github.com/tekezo/Karabiner-Elements/blob/master/src/share/types.hpp
にキーの割当一覧が乗っているので適宜変更してください。
MacでC#ユーザーのRider入門〜便利機能〜
今回はRiderの便利機能の紹介
getonlyから変更通知プロパティへの変換
Xamarin.Formsをやっていると変更通知プロパティが結構出てくるのですが、もしもともとあるプロパティを変更通知可能にしようとする場合バッキングフィールド書いて〜プロパティのsettergetter書いて〜は結構手間だと思います。
Riderならそんな面倒な変換も一瞬でやってくれます! 例としてgetonlyプロパティをregion付きの変更通知プロパティに変えてみました。
Autoプロパティからバッキングフィールドのプロパティへの変換がAlt+Enterで簡単にできます!しかもバッキングフィールドの名前はプロパティの名前のアンダーバー+ロワーキャメルケースへ自動的に変換。めっちゃ便利。
Currentメソッド作成
また、よくあるかわからないけどシングルトンパターンを作るときに初回はインスタンス作成して次回以降はそれを使いまわすみたいな感じになると思いますがこれもRiderならスラスラ書けます。
Sample Current{get;}=new Sample();
でいいだろという声は聞こえません
なんとnullチェックで冗長だった場合短い構文に書き換えてくれます!!素晴らしい。
Linqサポート
冗長だったやつを短いのに変えるのはnullチェックだけではありません。もしforeachで長い文を書いていた場合Linqに変換してくれます。
個人的に+=じゃなくて=が嬉しいですがまあ問題ないんでしょう。
リネーム
リネームのためだけにRiderを開いてもいいほど強力です。どのくらい強力かというと条件があうと別プロジェクトのコメントアウトした部分もリネームするか聞いてきます。
例としてバッキングフィールド付きプロパティをリネームしたとき
ちゃんとバッキングフィールドも一緒にリネームするか聞いてきます。地味に凄いですね。
Search Everywhere
これはIntelliJを使っている人ならよく知っていると思いますがShiftキーを2回押すことでいろんなものを検索します。
なんとこれクラスやシンボルだけでなく設定まで検索できます。
これでいちいちあの設定はどこだっけと悩む必要はなくなりますね!
ほかにもいろいろ便利な機能がありますがこの辺で。
とりあえずAlt+EnterとSearch Everywhereが便利すぎです。
MacでC#ユーザーのRider入門〜インストール〜
RiderとはJetBrainsが開発中のCross-platform C# IDEです。 詳しくはこちらを御覧ください。
Project Rider – 新しい C# IDE #jetbrainsrider | JetBrains ブログ
これは使うしかありません。さっそくインストールしましょう!!
注意
まずRiderは2016/9/17現在Private EAPです。上のリンクにはリリースは8月を予定と書いてある通り、当初の予定では2月にPrivate EAP、6月ごろにPublic EAP、8月か9月ころに正式リリースだった記憶があるのですが、Private EAPもギリギリまで遅れ(日本時間で2/29の24時頃だったのでギリギリセーフ(?))それから未だにPrivate EAPです。なので
- Xamarinのプロジェクトは作れません。Xamarin Studioをお使いください。
- Xamarinのプロジェクトを実行できません。Xamarin Studioをお使いください。
- プロジェクト操作がかなり怪しいです(ファイル削除してもファイルが残る)。Xamarin Studioでも怪しいです。
ただし、現在のPrivate EAP10ではだいぶ使い勝手もよくC#でコーディングする分にはほぼ問題ありません。むしろReSharperが素敵すぎます。
インストール手順
まずはPrivate EAPのサブスクライバーに登録します。
以下のページの下の方で申し込みが出来ます。
入力が終わったらSubscribeを押します。すると2-3分以内にメールがくるのでそのメールにあるOS Xのダウンロードリンクを押します。
するとdmgファイルが落っこちてくるのでインストールします。
インストールが終了したら早速起動してみましょう。
最初にこんな画面が出てきました。
以前のバージョンなんて無いので下を選択します。
次にどちらのUIを使うかの選択肢が出てきました。
LightかDarkですね。私は断然Dark派です。
次にどのエディターカラーを使うかを選択します。左がReSharper(違和感あるけど)、真ん中がVisualStudio(違和感あるけど)、右がDarcula(というかIntelliJ)ですね。
私は初期のRiderがReSharper一択で慣れてしまったのでReSharperです。
お次はKeymapです。
Visual StudioライクかReSharperライクかIntelliJIDEAライクかが選択できます。個人的にはあとで設定から変えられるReSharper(macOS)が好きですが初期設定でも結構キーが衝突しているのでmacOSユーザーならIntelliJのキーマップがいいのかな?
お次はターミナルからRiderを起動できるようにするかです。
私は結構ターミナルを使うことがあるのでとりあえずチェックを付けときました。
最後はチュートリアルやバグ報告する場所などのリンクです。
では早速Start using Riderを押しましょう!
IntelliJプラットフォームユーザーなら見慣れた画面が出てきました。
これで準備は完了です。お疲れ様でした!!
SynchronizationContext.Currentでnullが返ってくる
躓きかけたのでメモ
C#で以下のようなメソッドを書いたとします。
//Task.Run(()=>Hoge())などで呼び出す public void Hoge() { //ネイティブUI操作 }
これを非同期(バッググラウンドスレッド)で触ろうとすると死にます。いわゆるUIスレッド以外でUI要素に触ってはいけない問題です。これを回避するにはUIスレッドで実行してやればいいだけです。
PCLの場合、呼び出し側が自分で制御できる場合は例えばTaskSchedulerクラスのFromCurrentSynchronizationContext
を使えば問題ないわけですが
//var sheduler=TaskScheduler.FromCurrentSynchronizationContext(); //Task.Factory.StartNew(()=> { Hoge(); },CancellationToken.None,TaskCreationOptions.None, sheduler)で呼び出す public void Hoge() { //ネイティブUI操作 }
ライブラリ等で呼び出し側を自分で制御できない場合SynchronizationContext.Current.Post
メソッドを使えばいいです。
しかしこれにも少し問題があって例えば以下のようなコードを書くと死にます。
//Task.Run(()=>Hoge())などで呼び出す public void Hoge() { SynchronizationContext.Current.Post((_=> { //ネイティブUI操作 },null)); }
何故かと言うとSynchronizationContext.Current
はUIスレッド以外で呼び出すとnullが返ってきてヌルポるからです。
これを回避するには必ずメインスレッド中で変数などに確保しておくのが手っ取り早いです。もしHoge()のクラスがバックグラウンドスレッド以外で初期化されるならフィールドに確保しておくのが一番簡単でしょう。
SynchronizationContext context=SynchronizationContext.Current; //Task.Run(()=>Hoge())などで呼び出す public void Hoge() { context.Post((_=> { //ネイティブUI操作 },null)); }
しかしクラスの初期化自体がバックグラウンドスレッドの場合はフィールドで初期化でもヌルポるかもしれません。
これを回避するには絶対にUIスレッドで実行されるところで初期化してstaticにもたせてあげればよさそうなのですが少し面倒ですね・・・