2024年9月25日水曜日

小ネタ



 【#栞葉の誕生日】ライバーのすなる凸待ちをしてみんとてする犬【栞葉るり/にじさんじ】 - YouTube

 なぜか北海道に詳しい犬。北海道の某社に頼んでアクスタ打ってもらおうぜ。

 宇宙に行く(カーマンラインを超える)だけなら、MOMOの質量単価で考えるならアクスタ+GoProで200万円くらいで打てるのかな? まだちょっと気軽に乗れるような値段ではないけど、質量単価で一番金がかかるのはGoProだから、アクスタは1個でも10個でも総額(打上げ質量)は大して変わらないはずだし、数を増やせば1個あたりの値段は単純に分割できる。10人集めれば一人20万くらいで打てる。人数の多い箱だし「宇宙行きたい人~!」ってチャット投げれば人は集まりそう。

 乗客としてお金だけ出して乗るのがコンテンツとして面白いかというのはまた別の話。宇宙(カーマンライン)までははるかに届かないとしても、自分で気球にヘリウムを充填するほうがコンテンツとしては面白いよな。


 The Most Advanced 5th Gen Cockpit? | F-35 Lightning - YouTube

 F-35のシミュレータに乗った動画。スクリーンはピンボケが多いけど、ヒューマンマシンインターフェースはいろいろなパターンが見れる。/* 最近F-35 COCKPIT DEMONSTRATORの動画ってあんまり見かけない気がする */

 TDS1のヒートマップって何の表示なんだろう?



 19日に見かけたヘリ。

 Mode 3/Aで1200と同時にMode CでQNE2000ft(Mode 3/A 0520)くらいのリプライが入ってた。当時旭川空港のMETARでQNH1013mbar程度なのでQNH2000ft、このあたりの標高を差っ引くとQFE約400mくらいかな。結構高いところを飛んでる感じだけど、それでも400m程度なんだな。

 いくつか機体を見て高度を確認していけば、そその機体がおおよどれくらいの高度を飛んでいるのか推測できるようになるはず。ということでMode Cのデコーダを作ってみたんだけど。夏前後は部屋が暑いので窓を開けていたので、飛行機やヘリの音も聞こえやすいし、音がしたらすぐ外を見れたんだけど、もう昼間もかなり涼しくなってきて、一日中ほとんど窓を締め切っているので、これからの季節はヘリや飛行機を見るのはちょっと大変。また来シーズン、覚えていたら、という感じかな。まあ、受信は飽きない限り続けておく予定だけど。

 受信は1日に1回ログファイルを切り替える(放っておけばログファイルが延々と肥大化していくから、1日くらいで区切りたい)くらいの手間で、あとはPCをWindows Updateとかで再起動したときに起動するのを忘れないようにすれば良いから、それほどの手間でもない。


***


 C#のループ速度の実験。

 試しに

var foo = Enumerable.Range(1, 10000000).ToArray();
var bar = Enumerable.Range(2, foo.Length).ToArray();

var result1 = new int[foo.Length];
var result2 = new int[foo.Length];
var result3 = new int[foo.Length];
var result4 = new int[foo.Length];

List<double> list1 = [];
List<double> list2 = [];
List<double> list3 = [];
List<double> list4 = [];

Console.CursorVisible = false;
for (int loop = 0; loop < 10000; loop++)
{
    Console.Write($"\r{loop}");

    var stopwatch1 = Stopwatch.StartNew();
    for (int i = 0; i < foo.Length; i++)
    { result1[i] = foo[i] * bar[i]; }
    stopwatch1.Stop();

    var stopwatch2 = Stopwatch.StartNew();
    for (int i = 0; i < foo.Length && i < bar.Length; i++)
    { result2[i] = foo[i] * bar[i]; }
    stopwatch2.Stop();

    var stopwatch3 = Stopwatch.StartNew();
    for (int i = 0, iEnd = foo.Length; i < iEnd; i++)
    { result3[i] = foo[i] * bar[i]; }
    stopwatch3.Stop();

    var stopwatch4 = Stopwatch.StartNew();
    for (int i = 0, iEnd = int.Min(foo.Length, bar.Length); i < iEnd; i++)
    { result4[i] = foo[i] * bar[i]; }
    stopwatch4.Stop();

    list1.Add(stopwatch1.Elapsed.TotalSeconds);
    list2.Add(stopwatch2.Elapsed.TotalSeconds);
    list3.Add(stopwatch3.Elapsed.TotalSeconds);
    list4.Add(stopwatch4.Elapsed.TotalSeconds);
}
Console.WriteLine();
Console.CursorVisible = true;

list1.Sort();
list2.Sort();
list3.Sort();
list4.Sort();

using StreamWriter sw = new("log.txt");
sw.WriteLine(string.Join('\t',
    "",
    "for (int i = 0; i < foo.Length; i++)",
    "for (int i = 0; i < foo.Length && i < bar.Length; i++)",
    "for (int i = 0, iEnd = foo.Length; i < iEnd; i++)",
    "for (int i = 0, iEnd = int.Min(foo.Length, bar.Length); i < iEnd; i++)"));
foreach (var (i, ((a, b), (c, d))) in list1.Zip(list2).Zip(list3.Zip(list4)).Select((a, i) => (i, a)))
{
    sw.WriteLine(string.Join('\t', i, a, b, c, d));
}

 みたいなコードを書いてグラフ化(Releaseビルド)。

 

 for(int i=0;i<foo.Length;i++)が一番早くて、それ以外は少し遅い(極端に遅いというほどでもない)。

 for(int i=0;i<foo.Length&&i<bar.Length;i++)が一番早いかな、と思っていただけに、ちょっと裏切られた結果。

 ループを早く回した場合、何も考えずに書きやすく書いたほうが良いのかな? 言語やランタイムはそういうコードに合わせて最適化しているだろうし。


 似たような処理で、ウインドウをずらす計算。畳み込み(相関処理とか)でよく見かける形。

var hoge = Enumerable.Range(1, 1000000).ToArray();
var fuga = Enumerable.Range(2, hoge.Length).ToArray();

var result1 = new int[hoge.Length];
var result2 = new int[hoge.Length];
var result3 = new int[hoge.Length];

List<double> list1 = [];
List<double> list2 = [];
List<double> list3 = [];

Random rand = new(1234);

Console.CursorVisible = false;
for (int loop = 0; loop < 10000; loop++)
{
    Console.Write($"\r{loop}");

    var lag = rand.Next(hoge.Length);

    var stopwatch1 = Stopwatch.StartNew();
    for (int i = 0, j = lag; i < hoge.Length; i++, j++)
    { result1[i] = hoge[i] * fuga[j % fuga.Length]; }
    stopwatch1.Stop();

    var stopwatch2 = Stopwatch.StartNew();
    for (int i = 0, j = lag; i < hoge.Length; i++, j++)
    { result2[i] = hoge[i] * fuga[j %= fuga.Length]; }
    stopwatch2.Stop();

    var stopwatch3 = Stopwatch.StartNew();
    {
        f(hoge.AsSpan(^lag..), fuga.AsSpan(..lag), result3.AsSpan(^lag..));
        f(hoge.AsSpan(..^lag), fuga.AsSpan(lag..), result3.AsSpan(..^lag));

        ; static void f(ReadOnlySpan<int> foo, ReadOnlySpan<int> bar, Span<int> dst)
        {
            for (int i = 0; i < foo.Length; i++)
            { dst[i] = foo[i] * bar[i]; }
        }
    }
    stopwatch3.Stop();

    list1.Add(stopwatch1.Elapsed.TotalSeconds);
    list2.Add(stopwatch2.Elapsed.TotalSeconds);
    list3.Add(stopwatch3.Elapsed.TotalSeconds);
}
Console.WriteLine();
Console.CursorVisible = true;

if (!result1.SequenceEqual(result2)) { throw new Exception(); }
if (!result1.SequenceEqual(result3)) { throw new Exception(); }

list1.Sort();
list2.Sort();
list3.Sort();

using StreamWriter sw = new("log.txt");
foreach (var (i, (a, (b, c))) in list1.Zip(list2.Zip(list3)).Select((a, i) => (i, a)))
{
    sw.WriteLine(string.Join('\t', i, a, b, c));
}


 必要な部分だけSpanに切り出して処理するのが最速。かなり早い(ラグ無しの処理とほぼ同じ速度が出る)。都度インデックスを求める通常のforは結構遅い。それと、array[index%=array.Length]みたいな書き方が極端に遅くなるのがちょっと予想外。一旦変数を経由すると長さで余剰を取った判定が消えるらしい。


 今回得られた経験

1) ループはシンプルに書くのが楽だし早い

2) ウインドウをずらしてループに取ってくるような処理を書く場合、Spanで切り出したほうが早い

3) array[index%=array.Length]はかなり遅くなる

 あと、Spanを使う場合は長さを揃えたほうが早い気がする?


0 件のコメント:

コメントを投稿