XML ドキュメント コメント(英語)を日本語で読む方法を考える

(ソフトウェア開発者向け広告)
豚汁単品を合計5,000円以上お買い上げいただいた方に
先着60オーダー限定で「Technology Company」ステッカーをプレゼント!
f:id:masawan-guitar:20190511134112p:plain

Visual Studioでの開発では NuGet パッケージをよく利用する。
NuGetでインストールしたライブラリは、オブジェクトブラウザで確認するとコメントが英語であることは多い。
外国人が作成されたライブラリなら英語。

◆ (例)英語のコメント
Visual Studioオブジェクトブラウザで「StackExchange.Redis」を確認

f:id:masawan-guitar:20190506185009p:plain

外国産の便利なライブラリを日本人が使いやすい状態にしてみたいものだ。
ライブラリの使い方が分かりやすくなれば、実現できることも多くなるはずだ。
そこで、XMLコメント(英語)の日本語訳を一括で追記するツールを作成してみた。
日本語訳を追記する手順は以下の通り。
<手順>
 ①作成したツールを使って「必要なXMLコメント」を全てhtmlファイルに出力する。
  → summary、param、remarks、returns のコメントを全てhtmlファイルに出力する。
 ②出力したhtmlファイルをGoogle Chromeで開き、日本語翻訳させる。
 ③Google Chromeで日本語翻訳した文章をコピー&ペーストして新規テキストファイルを作成する。
 ④作成したテキストファイルの日本語の文章をツールで読み込み、XMLファイルに追記する。
 ※①と④をツールで行う。②と③は手動で行う。

◆(例)日本語訳を追記したコメントー下図の赤枠が追記した箇所
Visual Studioオブジェクトブラウザで「StackExchange.Redis」を確認

f:id:masawan-guitar:20190506185025p:plain

だいぶ分かりやすくなった。初めましての外国産ライブラリを扱う際のハードルが下がった。
しかし、英語も読めた方がよいので、英語を日本語に置き換えるのではなく、
英語をそのまま残して、日本語を追記するようにした。
Google Chromeの翻訳はおかしなところも多少あったが、雰囲気で読める日本語

◆(例)summary(概要)のコメント 1,335箇所 ーExcelに貼り付けて確認。
f:id:masawan-guitar:20190506212049p:plain
 ・
 ・
 ・
f:id:masawan-guitar:20190506212128p:plain

Redis 処理時間計測

Redisは永続化可能なインメモリデータベース。
連想配列、リスト、セットなどのデータ構造を扱えるキーバリューストア(KVS)。
ターンアラウンドタイム重視(オペレーション用途)、スケールアウト可能という位置付け。
Read/Writeが高速。キャッシュとして利用されることが多い。memchacedよりも機能は多い。
どのくらい高速なのか。大量データ登録の処理時間を計測してみよう!

図.データベースを分類する2つの軸、4つのエリア
f:id:masawan-guitar:20160812225135p:plain

f:id:masawan-guitar:20190501141631p:plain

<サーバー情報>
CPU = Intel(R) Core(TM)2 Duo CPU P8600 @ 2.40GHz
  →2008年頃に発売されていたCPU?けっこう古いやつです。
メモリ = 8 GB
OS = Ubuntu 18.04.2 LTS

◆処理時間計測結果&簡単に考察を記載
f:id:masawan-guitar:20190501181506p:plain
f:id:masawan-guitar:20190502201147p:plain

<処理時間計測結果(1回目)>
  10,000件  : 123ミリ秒
 100,000件 : 908ミリ秒
 200,000件 : 1秒 791ミリ秒
 500,000件 : 4秒 176ミリ秒
 1,000,000件 : 9秒 409ミリ秒

<処理時間計測結果(2回目)>
  10,000件  : 111ミリ秒
 100,000件 : 1秒 77ミリ秒
 200,000件 : 2秒 213ミリ秒
 500,000件 : 4秒 586ミリ秒
 1,000,000件 : 8秒 948ミリ秒

Redis Desktop Manager でデータ確認。
 → 100万件データ登録されている。
f:id:masawan-guitar:20190501182007p:plain

1万件データ登録 111ミリ秒、123ミリ秒。
100万件データ登録 9秒 409ミリ秒、8秒 948ミリ秒。
パフォーマンス良いと思う。
今回は、Key=string Value=stringで大量データ登録を試しただけ。
次回は、他の処理も試したい。
あと、大量データを「参照する処理」の時間計測をしてみようとは思う。
<追記>
後になって、本当にそんなに高速なのかなと思ってしまった部分があったので。
データ登録処理が終了した直後に「登録済データ件数」を取得して、
その登録済データ件数を画面に出力するように、クライアント側アプリのプログラムを改造してみたが。
ちゃんと全件がデータ登録できていたし、本当に高速だった。
f:id:masawan-guitar:20190503201800p:plain
<処理時間計測結果(3回目)>
  10,000件  : 133ミリ秒
 100,000件 : 1秒 133ミリ秒
 200,000件 : 1秒 950ミリ秒
 500,000件 : 4秒 698ミリ秒
 1,000,000件 : 9秒 23ミリ秒

<クライアント側で使用したアプリについて>
Redis処理時間計測のためにクライアント側のアプリを作成してみた。
Windows フォーム アプリケーション(.NET Framework 4.7.2)、言語は C#
StackExchange.Redis というライブラリを使用。
プログラムは参考までに。

◆処理時間計測に使用したC#プログラム
github.com

FrmMain.cs

using StackExchange.Redis;
using Redis.DataAccess.Client;
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Threading.Tasks;

namespace RedisClient
{
    public partial class FrmMain : Form
    {
        public FrmMain()
        {
            InitializeComponent();
        }

        private static bool processingFlg = false;

        private async void btnExecSet_Click(object sender, EventArgs e)
        {
            processingFlg = true;

            RedisConnection conn = null;
            this.ChangeControlEnabled(false);
            txtResult.Text = string.Empty;

            try
            {
                conn = new RedisConnection();
                conn.Open();
                RedisCommand cmd = new RedisCommand(conn);
        
                //  (´・ω・`)ノ「令和(reiwa)」
                //フォームが無反応にならないようにするため
                //OutputProcessTime_Setの処理は、
                //別スレッドで実行して処理終了まで待つ(await)。
                await OutputProcessTime_Set(cmd,  10000);
                await OutputProcessTime_Set(cmd, 100000);
                await OutputProcessTime_Set(cmd, 200000);
                await OutputProcessTime_Set(cmd, 500000);
                await OutputProcessTime_Set(cmd,1000000);

            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "システムエラー", 
                                MessageBoxButtons.OK, 
                                MessageBoxIcon.Error);
            }
            finally
            {
                if(conn!=null)
                { 
                    conn.Close();
                }

                this.ChangeControlEnabled(true);
                txtResult.Select(this.txtResult.Text.Length, 0);
                processingFlg = false;
            }
        }

        private void ChangeControlEnabled(bool value)
        {
            btnExecSet.Enabled = value;
            txtResult.Enabled = value;
        }

        private Task OutputProcessTime_Set(RedisCommand cmd, int dataCount)
        {
            return Task.Run(() => {

                const char HALF_SPACE = ' ';
                Dictionary<RedisKey, RedisValue> dic;
                var sw = new System.Diagnostics.Stopwatch();
                TimeSpan ts;

                //データベース初期化
                cmd.FlushDatabases();

                //登録データ取得(データ件数分)
                dic = FrmMain.getInsertData(dataCount);

                // (´・ω・`)ノ  Let's Go!!
                //処理時間計測 スタート
                sw.Restart();

                //Redis Set実行
                cmd.Set(dic);

                //処理時間計測 ストップ
                sw.Stop();

               //処理時間を出力
                ts = sw.Elapsed;
                long dbSize = cmd.DbSize(); //登録済のデータ件数を取得
                string outputText
                    = $"{txtResult.Text}" +
                      $"{dataCount.ToString().PadLeft(7, HALF_SPACE)}件" +
                      $"{HALF_SPACE}" +
                      $"処理時間 = " +
                      $"{ts.Minutes.ToString().PadLeft(2, HALF_SPACE)}[m] " +
                      $"{ts.Seconds.ToString().PadLeft(2, HALF_SPACE)}[s] " +
                      $"{ts.Milliseconds.ToString().PadLeft(3, HALF_SPACE)}[ms]" +
                      $"{HALF_SPACE}" +
                      $"(登録データ件数 = " +
                      $"{dbSize.ToString().PadLeft(7, HALF_SPACE)}件)" +
                      $"\r\n";
                this.SetText(txtResult, outputText);

            });
        }

        private void SetText(TextBox textBox, string text)
        {
            if (textBox.IsDisposed) return;
            if (textBox.InvokeRequired)
            {
                this.Invoke((MethodInvoker) delegate {
                    SetText(textBox, text);
                });
            }
            else
            {
                textBox.Text 
                    = text;
            }
        }

        private static Dictionary<RedisKey,RedisValue>getInsertData(int dataCount)
        {
            var dic = new Dictionary<RedisKey, RedisValue>(dataCount);
            var sbKey = new StringBuilder();
            var sbValue = new StringBuilder();

            for (int count = 1; count <= dataCount; ++count)
            {
                sbKey.Clear();
                sbKey.Append("key");
                sbKey.Append(count.ToString().PadLeft(7, '0'));

                sbValue.Clear();
                sbValue.Append("value");
                sbValue.Append(count.ToString().PadLeft(7, '0'));

                dic.Add(sbKey.ToString(), sbValue.ToString());
            }

            return dic;
        }

        private void FrmMain_FormClosing(object sender, FormClosingEventArgs e)
        {
            if(processingFlg)
            {
                MessageBox.Show("処理実行中のため、画面を閉じることはできません。"
                               ,"情報"
                               , MessageBoxButtons.OK
                               , MessageBoxIcon.Information);
                e.Cancel = true;
            }
        }
    }
}

RedisConnection.cs

using StackExchange.Redis;
using Redis.DataAccess.Client.Properties;

namespace Redis.DataAccess.Client
{
    public class RedisConnection
    {
        private static ConnectionMultiplexer redisStore;
        private static IDatabase db;

        private const string CONFIGURATION_OPTIONS = "allowAdmin=true";

        private static readonly string configurationStringsDefault
            = Settings.Default.IPAddress
              + ":" + Settings.Default.PortNo.ToString()
              + "," + CONFIGURATION_OPTIONS;

        public RedisConnection()
        {
        }

        public void Open()
        {
            redisStore
                 = ConnectionMultiplexer.Connect(configurationStringsDefault);
            db = redisStore.GetDatabase();
        }

        public void Close()
        {
            if(redisStore!=null)
            {
                redisStore.Close();
                redisStore.Dispose();
            }
        }

        public IServer Server
        { get => redisStore.GetServer(Settings.Default.IPAddress, 
                                       Settings.Default.PortNo); }

        public string ConfigurationStrings
        { get => configurationStringsDefault; }

        public IDatabase Database { get => db; }

        public ConnectionMultiplexer RedisStore { get => redisStore; }

    }
}

RedisCommand.cs

using StackExchange.Redis;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Redis.DataAccess.Client
{
    public class RedisCommand
    {
        private static IDatabase db { get; set; }
        private static IServer server { get; set; }

        public RedisCommand(RedisConnection conn)
        {
            server = conn.Server;
            db = conn.Database;
        }
        public long DbSize()
        {
            return server.DatabaseSize();
        }
        public void FlushDatabases()
        {
            server.FlushAllDatabases();
        }
        public bool Set(RedisKey key, RedisValue value)
        {
            return db.StringSet(key, value);
        }
        public void Set(Dictionary<RedisKey, RedisValue> dic)
        {
            var setTasks = new List<Task>();
            foreach (KeyValuePair<RedisKey, RedisValue> kvp in dic)
            {
                Task<bool> setAsync = db.StringSetAsync(kvp.Key, kvp.Value);
                setTasks.Add(setAsync);
            }
            Task[] tasks = setTasks.ToArray();
            setTasks.Clear();
            setTasks.TrimExcess(); // (´・ω・`)ノ Clear! and TrimExcess !
            Task.WaitAll(tasks);
        }
    }
}

◆メモ「StackExchange.Redis pipelining」
optimization - Pipelining vs Batching in Stackexchange.Redis - Stack Overflow
Redis for .NET Developers – Redis Pipeline Batching | Taswar Bhatti

"シン・ニホン" AI×データ時代における日本の再生と人材育成。

教育再生実行会議技術革新WG
November 27 , 2018
 Kaz Ataka(安宅 和人)
 慶應義塾大学 環境情報学
 Yahoo! 株式会社CSO

<資料>
『"シン・ニホン" AI×データ時代における日本の再生と人材育成』
https://www.kantei.go.jp/jp/singi/kyouikusaisei/jikkoukaigi_wg/kakusin_wg4/siryou1.pdf