インフォテックの案件ではサーボモータなどをPCで制御することが良くあります。
今作成しているソフトもモータ制御を行うのですが、モータそのものやPCのモーションボードが無くてもソフトの動作検証が行えるように、エミュレート機能を社内ライブラリに組み込んであります。
今回はエミュレート機能をさらに強化し、移動速度に合わせてパルス出力数がカウントされたり、パルス出力状態や停止要因が正しく変化するようにしました。
この改造により、今までより細かく社内での動作検証が行えるようになりました。
先日大量のデータを処理集計するために Dictionaryに格納していたのですが
10万件を超えたあたりで急に速度が落ちることがありました。
Dictionaryのキーを任意作成のStructにしておりましたが 別にテストで作成したものでは1万、10万、100万はDictionaryの個数に比例して増加しておりました。
(1万件が80msecならば 10万件は800msecのような状態)
今回のデータ処理用に作成したものは10000件で100msec程度だったので 30万で3秒前後だと考えておりましたが実際には7時間でした。
遅い原因を調べるために.Netのソースを確認し結果的にはStructのHashCodeが重複しやすい物の場合は今回の様な結果になるのではないかと思いました。
DictionaryのContainKeyの内部で使用している部分
public bool TryGetValue(TKey key, out TValue value)
{
if (key == null) throw new ArgumentNullException("key");
int bucketNo, lockNoUnused;
// We must capture the m_buckets field in a local variable. It is set to a new table on each table resize.
Tables tables = m_tables;
IEqualityComparer comparer = tables.m_comparer;
GetBucketAndLockNo(comparer.GetHashCode(key), out bucketNo, out lockNoUnused, tables.m_buckets.Length, tables.m_locks.Length);
// We can get away w/out a lock here.
// The Volatile.Read ensures that the load of the fields of 'n' doesn't move before the load from buckets[i].
Node n = Volatile.Read(ref tables.m_buckets[bucketNo]);
while (n != null)
{
if (comparer.Equals(n.m_key, key))
{
value = n.m_value;
return true;
}
n = n.m_next;
}
value = default(TValue);
return false;
}
GetBucketAndLockNoはこの通り
private void GetBucketAndLockNo(
int hashcode, out int bucketNo, out int lockNo, int bucketCount, int lockCount)
{
bucketNo = (hashcode & 0x7fffffff) % bucketCount;
lockNo = bucketNo % lockCount;
Assert(bucketNo >= 0 && bucketNo = 0 && lockNo < lockCount);
}
上記Nodeの取り出しのバケットNoがhashcodeを使用しているのでカスタムのKeyを使用する場合はHashCode十分に分散するか考えて作成していかないといけないのかもしれません。
遅い原因はチェック中なのでHashCodeの件は間違っているかもしれません進展があれば再び書きたいと思います。
ちなみにコードはこちらで確認できます。
コードは理解しやすくなければならない。
「リーダブルコード」にもそう書かれています。
他の人が読みやすいかどうかというのはとても重要な事だと思います。
適切な変数名やメソッド名は理解を助けますし、
説明変数なども積極的に利用するようにしています。
書き方ひとつとっても、
例えば変数を宣言して anotherClass のみ遅延した初期化が必要な場合、
var myClass = new MyClass(); var yourClass = new YourClass(); AnotherClass anotherClass;
と書くよりは、
var myClass = new MyClass(); var yourClass = new YourClass(); var anotherClass = default(AnotherClass);
と書く方が、それぞれの変数の位置が揃っていて、
読みやすいのではないでしょうか。
気取らず、気の利いた美しいコードを目指したいと思います。
WindowsデスクトップアプリのGUIフレームワーク(の中でXAMLを使用しているもの)について、以下のサイトに現在の状況が記載されており参考になりました。
https://www.kekyo.net/2021/02/23/7230
インフォテックの利用状況は、過去の案件はWindowsForms、新規案件はWPF、まれにUWPといったところです。
実運用でAvaloniaが使い物になるなら試してみたいですね。
こんにちは、mtjです。
コロナによりリモート管理ツールが色々社会で流行るようになってきました。
文字ベースのやり取りができるチャットツールまでは良いのですが リモート会議ツール等に関しては扱う個人の慣れに大きく影響されてしまいます。
リモート会議ツールに関しては
・音声
・画面共有の仕方
・各自発言者の認識
等
オフラインでは気にしなくても良いような点で不都合が発生します。
会議ツールの選定+事前確認内容の資料等を準備したほうが慣れてない人達にも解りやすい気がしました。
ちなみにマイクはUSB接続がおすすめです ジャック式のでも動きますがPCのデバイスに依存してしまうため満足に動かない、ノイズが酷い等損をする場合があります。
C#9.0でrecord型が追加されました。
まだ実務では使用していませんが、
今後C#でとても重要な機能になると思っています。
最も活躍できるのが、DDDのValueObjectでしょうか。
今まではそれらしい事をするには等値性を表現する為に、
Equals()やGetHashCode()をoverrideする必要がありましたが、
record型はコンパイラが自動で行ってくれます。
そして定義がとても簡単に行えます。
Microsoftの説明を引用すると、
public record Person {
public string LastName { get; }
public string FirstName { get; }
public Person(string first, string last) => (FirstName, LastName) = (first, last);
}
と同じことが、
public record Person(string FirstName, string LastName) { }
と書けます。とても簡潔ですね。
C#で現在実行しているメソッドを取得するプロパティを作ってみました。
何かと役に立ちそう。
インライン化されないようにMethodImpl(MethodImplOptions.NoInlining)を指定するのがポイントです。
public static class Current {
public static MethodBase Method {
[MethodImpl(MethodImplOptions.NoInlining)]
get {
var stackFrame = new StackFrame(1);
return stackFrame.GetMethod();
}
}
}
mgcです。
気が付けば2020年も残り10日となってしまいました。
今年も多くの企業様に大変お世話になりました。
誠にありがとうございました。
ありがたいことに私自身はコロナ禍でも大変忙しくさせていただいておりました。
2021年にはコロナ収束を願い、より一層気を引き締めていこうと思いますので
宜しく願い致します。
それでは皆様くれぐれもお体にはご自愛くださいませ。
こんにちはmtjです
プログラミングは基本勉強し続けていくものというのは大体のプログラマが思っている事だと思います。
自分のメインとしているC#も2012年頃と比べるとかなり書き方が違います。(バージョンでいうと5.0から8.0or9.0)
しかし、.Netの互換性等もあり最新のOS向けのプログラムは最新Verで作らないといけないわけではないので
10年前のC#でも現代で動かすことが可能です。
勉強をやめてしまっている人でもプログラムを書くことは可能ですが古い書き方になってしまい
最新の書き方で書かれているものならば理解自体が難しくなってしまうと思います。
言語が進化していれば自ずと勉強していくものですが 言語自体が古く進化がない言語だと勉強が止まってしまうので
その場合はかなり努力が必要だと思います。
そういう意味でも若い人がCobol、VB6等を頑張って保守するのでなく現在の流行りの言語を行わせ
年配者が古い言語を保守すべきなのではないかと感じました。
古い言語を勉強すること自体は幅も広がり悪いことでは無いと思いますが 古い言語を専門にしては先細りの分野なので
将来性が薄いと感じてしまいます。
既存のソースコードに新しい機能や変更を加えようとするとき、
まず既存のコードをリファクタリングします。
そして切り出したり共通化したクラスやインターフェースなどを、
ソリューションやプロジェクト内のどこに記入するべきか考える必要があります。
全体の設計思想や、選択したフレームワークによって、
適切な場所があるはずですが、なかなか上手く収まらない事もあります。
そのような場合は、自分が全体の設計思想に対する理解が足りていないか、
ソリューションやプロジェクトが上手く構成できていない事がうかがえます。
リファクタリングは基本的には必要な時に行いますが、
例えばテストファースト開発のように、
開発サイクルの中で常時行う方法もあります。
年末の大掃除と同じように、まとめて後でやろうとすると大変です。
普段から少しずつ行っていればリスクも少なく、
ソリューションやプロジェクトの構成もより明確になってゆくものだと思います。
リファクタリングであまり悩むことが無いよう、
安心して開発できる環境を目指したいと思います。