手続き型音楽の日常

関数型音楽に乗り換えたい

AVT-C878 というキャプチャボードが認識してくれない話

ついに買いました。Splatoon2に向けて。

巷で “最強” とも称されるキャプチャボード

随分と物騒な噂ですが この程、Nintendo Switchをつなげて録画をしようとこんなものを購入しました。

こちら。USBでつないで、1080p/60fpsで録画ができるという素晴らしいもの。

という謳い文句を 鵜呑みにして 買いました。

前から何度も店に足を運んではいろんなキャプチャボードを見てきましたが、値段的にも扱い方的にもこれがいいのかなと思って購入しました。

ガチなボードだとパソコンを起動しないといけないという手間もありますしね、これがいいかなと。

何度も言います。 これがいいかな、と思って。

思い込みで買うのは本当に怖いものですね。

人生の99%は思い込み―――支配された人生から脱却するための心理学

人生の99%は思い込み―――支配された人生から脱却するための心理学

買ってつなげて初めて分かるもの

PC録画の推奨環境が 「Core i5-3xxx 以上 / NVIDIA GeForce GTX 650同等以上」 とあるので、結構なスペックが要求されるようです。

私が持っているPCの中ではメイン機だけが使えそう。

f:id:yuzutan_hnk:20170705010604p:plain:w300

OS: Windows10
CPU: Core i7-6700
メモリ: 16GB
グラボ: NVIDIA GTX 1050

使えそうというか、これ絶対動くでしょ、って感じで甘く見てました。

……。

認識しない

PCとUSBでつなぐと、電源が入る。HDMIパススルーが機能して、普通にゲームも遊べる。

しかし、PC側にはなんの反応もない。いや、実際には少しだけ認識したけど、認識できないデバイスと言われ、ドライバにも黄色い三角マーク。

仕方なくドライバを削除して再起動。とりあえず標準ドライバを使うとWebカメラとして認識するみたいなので、それでやってみようと試みる。

が、何分経っても青点滅(準備中)が消えない。何度抜き差ししてもだめ。全く認識しない。

死亡フラグが立ちました! (宝島社文庫) (宝島社文庫 C な 5-1)

死亡フラグが立ちました! (宝島社文庫) (宝島社文庫 C な 5-1)

Ubuntuならどうか

サブ機(サーバー)のUbuntuにつなげてみて、Webカメラとして使えたら使おうと考えた。

グラボが載っていないので、もし認識したら安めのものを買ってきてつけようかとも考えた。

しかし、全く同じ状態が続く。青点滅が消えない。

挿した瞬間、HDDアクセスを示すLEDランプが一瞬光るので繋がった認識はしてるんだろうけど、そこから先動いてるように見えない。

OBSとかも全く反応してくれない。

カードリーダも使えない

AVT-C878はカードリーダーモードも備えていて、普通にmicroSDの中身を読み書きできるそう。

なのに それも動いてくれない 。やっぱり青点滅が消えない。

完全にUSBでPCとやり取りができていない。AVT-C787側も初期化が終わらないってことは、完全にハンドシェイクが切れてる。万事休す。

無理やりFWアップデート走らせたせい?その前から現象はあるけど…

ファームウェアが古いせいなのかなとか思って、microSDにデータをダウンロードして無理やりアップデート。

マニュアルにはカードリーダモードで転送するよう書いてあったけど、前述の通り使えないので、普通に別のカードリーダで転送。

入れて起動すると自動的にアップデートが始まって、 ゆっくり青く点滅 するらしい。が、実際にやってみるとなぜか 素早く青く点滅 していた。

アップデートが終わると自動でランプが消灯すると書いてあって、実際しばらくしたら消えたので、たぶんアップデートできてるはず。

実際バージョン情報見るには、後述のソフトがAVT-C878を認識しないといけないから、結局どうなったのかわからないんだけど…。

ファーム・ルーツFirm Roots ?

ファーム・ルーツFirm Roots ?

RECentral すら起動しない

連携ソフトというか、無料で配布されているキャプチャソフト兼コンフィグレーションソフトの RECentral なるものがあるのですが。

これ、起動すると5秒もしないうちに落ちる。

ウィンドウは出てくるけど、全く何も使えない。

それどころかボードを認識している様子すらない(Windowsが認識してないから当たり前か…)

調べてみると確かに、同じような現象に陥っている人はいるみたい。再インストールすれば治る人もいるけど、私みたいに全く治らない人もいそう。

単体録画は正常

これだけPC録画がボロボロなのに、単体録画はしれっと何事もなかったかのようにできている。

空のmicroSDを挿して、PCのUSBから電源だけ拝借する。MOVなので画質は粗めだけど、解像度はちゃんと1080p出てる。

ハードエンコードなので圧縮とか高度なことはしてくれないのですぐ容量なくなってしまうけど、とりあえず当面は使えるレベル。

とはいえもともとPC録画を目的に買ったから本末転倒って感じがする。。。

結局

しばらく単体モードでやりますが、そのうちお金に余裕ができたら別のボード買うかもね…。うーん辛い。

お兄ちゃん!そこは MemoryStream の出番だよ!

タイトルは釣りです(お約束)

MemoryStream のススメ

みなさん、 System.IO.MemoryStream 使っていますか。私はよく使いますよ。

MemoryStream Class (System.IO) | Microsoft Docs

リアクティブプログラミングだったり、Java の Stream API だったり、いろんな Stream がありますが、今回はC#MemoryStream に注目してみます。

MemoryStream のイロハ

そもそもC#には Stream クラスがあり、 MemoryStream はその派生クラスです。同じような派生クラスには FileStreamCryptoStream があります。

似て非なるものですが、これらは共通して データを順次読み出したり、順次格納したりできる という特徴を持っています。

たとえば FileStream は、ディスク上のファイルを読み書きするクラスです。ファイルを開くと FileStream にはそのファイルサイズと カーソル位置 が保持されます。

カーソル位置は Stream のデータを操作する位置 を表します。これを使うと、例えばファイルの80バイト目から60バイト分読み出したい、といった場合に、カーソル位置を80バイト目に移動させ、そこから60バイト分読み出すことができます(同時にカーソルも60バイト動くため、次に60バイトを読み出すと140バイト目から60バイトを読み出します)。

これは、次のように書くことができます。

// ファイル名 を元に FileStream を作成
var stream = new FileStream("C:/hoge.txt", FileMode.Open);

// 80バイト目から60バイト読み出す
var puts = new byte[60];
stream.Position = 80;
stream.Read(puts, 0, 60);

同様に MemoryStream も、ある byte[] のサイズ(配列の長さ)とカーソル位置が保持されます。

そして、例えばある byte[] の80バイト目から60バイト分読み出したい、といった場合に、カーソル位置を80バイト目に移動させ、そこから60バイト分読み出すことができます。

これは、次のように書くことができます。

var array = new byte[300];

// --- ここに本来はデータの操作が入る --- //

// array を元に MemoryStream を作成
var stream = new MemoryStream(array);

// 80バイト目から60バイト読み出す
var puts = new byte[60];
stream.Position = 80;
stream.Read(puts, 0, 60);

なお、以下の配列へのアクセスをするコードで、同様の puts を得られます。

var array = new byte[300];

// --- ここに本来はデータの操作が入る --- //

// 80バイト目から60バイト読み出す
var puts = new byte[60];
for (int i = 0; i < 60; i++)
    puts[i] = array[80 + i];

え、じゃあ何に使うん

MemoryStream は byte[] へのアクセスを簡単にします。本当に?

ひどい夢を見ました。すべての byte[] 配列へのアクセスを、 MemoryStream を通じて行うようにするという、お達しが出たのです。まったく、とんだ災難です。このプロダクトは文字列や数値としては扱えないデータが山ほどあり(画像や音声、もしかしたら地球外生命体のDNAの解析結果かもしれない)、それらはすべて byte[] で表すことになっています。だから、それら全てのアクセスを、 MemoryStream に置き換えなければなりません。たった1要素の読み込みでさえ、長ったらしく2,3行を書き連ねなければならないのです。

実際にそんなことがあるはずはありません。安心してください。 ところで次の例を見てくれ、こいつをどう思う?

// AESで暗号化するためのオブジェクトを初期化
var aes = new AesManaged();
aes.GenerateIV();
aes.GenerateKey();

// MemoryStreamを作成
var memStream = new MemoryStream();

// CryptoStreamを作成
var cryStream = new CryptoStream(memStream, aes.CreateEncryptor, CryptoStreamMode.Write);

// CryptoStreamに書き込み
cryStream.Write(new byte[] { 1, 2, 3, 4, 5 }, 0, 5);

// MemoryStreamから読み出し
var read = byte[3];
memStream.Position = 1;
memStream.Read(read, 0, 3);

AesManaged Class (System.Security.Cryptography) | Microsoft Docs

CryptoStream Class (System.Security.Cryptography) | Microsoft Docs

ちょっと難解ですが、 ある MemoryStream を参照する CryptoStream にデータを書き込むと、 MemoryStream に暗号化されたデータが書き込まれる コードです。

上記の例では read に暗号化されたデータの一部が代入されることになります。

さてこれをちょっとだけ改変しましょう。

// AESで暗号化するためのオブジェクトを初期化
var aes = new AesManaged();
aes.GenerateIV();
aes.GenerateKey();

// FileStreamを作成
var filStream = new FileStream("C:/hoge.enc", FileMode.Create);

// CryptoStreamを作成
var cryStream = new CryptoStream(filStream , aes.CreateEncryptor, CryptoStreamMode.Write);

// CryptoStreamに書き込み
cryStream.Write(new byte[] { 1, 2, 3, 4, 5 }, 0, 5);

だいたい想像がつくと思いますが、これは ある FileStream を参照する CryptoStream にデータを書き込むと、 FileStream に暗号化されたデータが書き込まれる(暗号化されたデータがファイルに書き込まれる) コードです。

驚くべきことに、このコードを先ほどのコードと比べると、 MemoryStreamFileStream にすり替えただけなのです。

MemoryStream は世界を救う

つまり、 MemoryStream は、 byte[]FileStream 、すなわち 変数操作とファイル操作と同等に扱えるようにするクラス ということなのです。

C# では、とくにデータの変換系の処理を Stream で行うような風潮があるように見えます。

例えば暗号化や、巨大なバイナリファイルの符号化など。JSONシリアライズにも Stream クラスを引数に受けるメソッドを用います。

こうすることで、対象がファイルでもメモリでも、同じ操作でデータを扱えるという素晴らしい恩恵を享受することができます。

単体テストもコードの再利用もどんとこい、な機能ですね。

さいごに

素人が生意気にすみませんでした 。ちょっと魔がさして書き始めたら収拾がつかなくなってしまいました。申し訳ありません。反省はしていません。

この間 JSONC#のクラスにシリアライズしたりデシリアライズするときに MemoryStream を使う機会があったのですが、正直な話

なんでクラスで管理できる情報量をわざわざ MemoryStream で書く必要があるのか、変数でいいのではないか

と思いながらバリバリ書いてたんですね。

そしてふと、思いついたんですよね。JSON ってファイルの可能性があるよなぁ、と。

自分の中ではとても面白い発見だったので、ついつい長ったらしく書いてしまいました。本当に申し訳ありませんでした。

最後まで見てくださって本当にありがとうございました。

P.S.

本来書きたかったネタもとりあえず置いておきます。 MemoryStreamRead メソッドが超絶使いにくい件について。

public static class MemoryStreamExtention
{
    public static byte[] ReadAllBytes(this System.IO.MemoryStream target)
    {
        byte[] ret = new byte[target.Length];
        target.Position = 0;
        target.Read(ret, 0, (int)target.Length);
        return ret;
    }
}

.NET core コンソールアプリで文字化けするときの対処法

Main メソッドの一番頭にこれを書く。

Console.OutputEncoding = Console.OutputEncoding;

何をしているのか

.NET core コンソールアプリでは、規定で Console.OutputEncodingSystem.Text.UTF8Encodingインスタンスが入っています。

しかし、コンソール側(Windowsデバッグした場合は規定でコマンドプロンプト)がこれをまだ検知していない状態のため、UTF-8でないコードページで表示をしています。

そのため、一度プロパティのsetterを通す必要があります。

…という推測を立てただけです。すみません。

Ubuntuのtaskselを使って、ラクしてKVM環境を作る

Linuxで楽しよう。Linuxを楽しもう。(名言っぽく言ってみるだけ)

ふと taskselxubuntu desktopを選ぼうとすると、そこにそれらしき項目があったので、使ってみた。その時のメモ。

基本、シェルはbashでroot権限で行きます。 sudo はつけないので、 su したくない方は sudo をつけて実行してください。

PCが仮想化に対応しているか確認する

KVMは完全仮想化をCPUの仮想化技術で実現しているようなので、次のコマンドで確認する。

# egrep -c '(vmx|svm)' /proc/cpuinfo

出力が1以上なら仮想化できます。

パッケージをインストールする

とりあえずKVM環境を動かすためのライブラリ群を入れます。次のコマンドを打ちます。

# tasksel

GUIっぽい画面が出てくるので、一覧から「Virtual Machine Host」に スペースキー でチェックを入れ、 エンターキー で続行します。

あとは待つだけ。これが終わると、仮想化に必要なものがほとんど入ります。

ただし、GUIで設定できる「virt-manager」はあいにく入れてくれないので、これだけは手動で入れます。

# apt install virt-manager

これがあれば、Virtual BoxみたいにGUIで簡単に操作ができるようになります。

ネットワークインターフェイス(NIC)をブリッジする

なんか、皆さんこぞってブリッジしているので、ブリッジしたほうが幸せなのかな?と半信半疑でブリッジしました。

一応パッケージをチェック。

# apt list | grep bridge-utils

[インストール済み] と末尾に表示されていなければ、インストールします。

# apt install bridge-utils

次に、ブリッジするよう /etc/network/interfaces を編集

Ubuntuは最近、規定のNICインターフェイス名が環境によって変わるようになったので、適宜読み替えてください。

auto enp0sXXXX
iface enp0sXXXX inet manual

auto br0
iface br0 inet static
address 192.168.XXX.XXX
network 192.168.XXX.0
netmask 255.255.255.0
gateway 192.168.XXX.XXX
dns-nameservers XXX.XXX.XXX.XXX
bridge-ports enp0sXXXX
bridge-stp off

元あった規定NICインターフェイスの設定はすべてコメントアウトか削除かしておきます。その設定を、br0側にすべて書きます。

この後、再起動して ifconfig でbr0側にIPアドレスなどが定義されていることを確認。

参考サイト

qiita.com

www.agilegroup.co.jp

Ubuntu 16.04 KVMのインストールと、bridge接続の構成 - Symfoware

aptでインストールした残骸を確認する

完璧に備忘録ですが。

パッケージの一覧を取得する

# apt list

パッケージの一覧が表示されます。インストール済みかそうでないかにかかわらず、データベースに登録してあるパッケージはすべて出ます。

たぶんこの一覧の中からパッケージの情報を取り出すんだと思います。

設定が残る

上記のコマンドで表示した一覧で、末尾に次のような表示がされることがあります。

  • [インストール済み]
  • [インストール済み、自動]
    • ほかのパッケージが依存しているか何かで、ユーザが直接指定していないけどインストールされたもの
  • [設定が残存]
    • アンインストールして、設定が残っている(再インストール時に既存の設定が使用される)

この「設定が残存」があると、再インストール時に --update フラグとかが自動でつけられたりして、クリーンインストールはしないようです。

設定を消す

# apt purge <パッケージ名>
# apt remove --purge <パッケージ名>

どちらでもいいみたい、

Ubuntu16.10でIPを固定しDNSを指定するのに、2時間かけました。

Ubuntuのことをあまり知らずに挑むからダメなんですよね。

とはいえ、IPを固定するくらいはいくらシステムの根幹に近いカスタマイズとはいえ、もうちょっと簡単でもいいと思うんですよ…

変更した箇所

  • /etc/network/interfaces
  • /etc/hosts
  • /etc/NetworkManager/NetworkManager.conf
  • /etc/resolvconf/resolv.conf.d/tail

いちおうこれらは確実に変更してあります。

ほかにも触ったところあったかもしれないけど、毎回Ubuntuマシン触るときに悩まされる種でもありイライラしていたので覚えていない…。これだからブログ向いてないんですよね。知ってます。

※なお、これから同じような設定をされる方は、ほかの方の記事やしっかりとした情報を一通り見てから実際に設定されることを強くお勧めします。

/etc/network/interfaces

auto lo
ifacw lo inet static

が初期設定。これを次のように変更します。

auto enpX
iface enpX inet static
    address <固定するアドレス XXX.XXX.XXX.XXX>
    netmask <サブネットマスク XXX.XXX.XXX.XXX>
    gateway <デフォルトゲートウェイ XXX.XXX.XXX.XXX>
    dns-nameservers <DNSサーバ XXX.XXX.XXX.XXX>

enpXNICのアドレスのようなもので、 ifconfig コマンドで確認ができます。

lo から enpX に変更するので、 ifconfig を走らせたときに lo と同じような位置に出てくるやつがそれです。

なおDNSサーバプライマリとセカンダリをスペースで区切って指定できるようです。

/etc/hosts

変更する意味があるかと言われれば、もしかしたらないかもしれないですが、一応変更します。

127.0.0.1 localhost
127.0.1.1 <コンピュータ名>

# The following lines are disirable for IPv6 capable hosts
::1     ip6-localhost ip6-loopback
・
・
・

最後のほうは面倒なので端折りました。

ここに出てくる 127.0.1.1 を変更します。

127.0.0.1 localhost
<固定するアドレス> <コンピュータ名>

# The following lines are disirable for IPv6 capable hosts
::1     ip6-localhost ip6-loopback
・
・
・

/etc/NetworkManager/NetworkManager.conf

悪党を倒しに行きます(?)

[main]
plugins=ifupdown,keyfile,ofono
dns=dnsmasq

[ifupdown]
managed=false

これを、以下のように直します。

[main]
plugins=ifupdown,keyfile,ofono
#dns=dnsmasq

[ifupdown]
managed=false

これで悪党は目覚めないことでしょう。きっと。

/etc/resolvconf/resolv.conf.d/tail

最後に強制的にDNSを変えます。もう最終手段です。

このファイルは、私の環境では入っていなかったため、自分で作りました。

そして以下のように記述します。

nameserver <DNSサーバ XXX.XXX.XXX.XXX (プライマリ)>
nameserver <DNSサーバ XXX.XXX.XXX.XXX (セカンダリ)>

果たして /etc/network/interfaces に記述した dns-nameservers に意味はあったのだろうか。

そして最後、 resolv.conf を生成します。

$ sudo resolvconf -u

再起動

最後に再起動すれば、IPアドレスは固定になり、DNSサーバも変わっているはず。

時間がかかった。

結局、表題の通りすべての帳尻合わせするため 2時間 以上はかけました。はい。

IPアドレスを固定するために /etc/interfaces を書き換えている方がたくさんいたのですが、それだけだとなぜかうまくいかなくて。

試行錯誤を重ねてやっと固定された感じです。つらい。

やっぱりここらへんはCentOSのほうが楽なんでしょうかね。。。

StringBuilderをstringと同じだけ作ると、どれほど遅いのか。

この間、以下のようなコードを見かけました。

StringBuilder strSql = new StringBuilder();
strSql.Append(@"SELECT * FROM ...
                WHERE ..."); // SQLは長いので省略。
this.ExecuteNonQuery(strSql.ToString());

フォームのメソッドに書かれていたのですが、フォーム自体がユーザコントロールになっていて、データベース接続の機能をすでに実装しているんですね。

なので、それにSQLを投げれば完了というすごくシンプルなものなのですが。

StringBuilderにする必要あったのかなって。

StringBuilderを必要とする理由

StringBuilder は、 string 型が演算で毎回新しいインスタンスを生成したり破棄したりするのに対して、最初のインスタンスを使いまわすために使う、というものだと思います。

具体的には次のコード。

string s = "";
s = s + "a";
s = s + "b";
s = s + "c";

上記のコードでは、最終的に変数 s に入るのは "abc" という文字列ですが、その文字列を得るまでにインスタンスを生成する回数は7回です。

それに対し、

StringBuilder s = new StringBuilder();
s.Append("a");
s.Append("b");
s.Append("c");

上記のコードでも同様に、最終的に変数 s に入るのは "abc" という文字列ですが、インスタンスの生成は4回で済みます。

インスタンスの生成、という観点からみると、 string 型より StringBuilder 型のほうが同じインスタンスを使いまわすためオーバーヘッドが少ないと考えられます。

しかし。

最初のコードって、はじめに StringBuilder 型の変数を確保しているので、毎回インスタンスを生成していることになるんですよね。

これではstring型を直で触るのと変わらないのでは…。

ということで調べてみました。

検証コード

次の検証コードを用意しました。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        /// <summary> 出力用ダミー変数 </summary>
        static string d_out;

        /// <summary>
        /// メインエントリポイント
        /// </summary>
        static void Main(string[] args)
        {
            try
            {
                int count1;
                int count2;
                List<double> measuredList = new List<double>();

                //------------------------------//
                // 入力
                //------------------------------//

                do
                    Console.WriteLine("1回の計測で確保を行う回数を指定してください...");
                while (!int.TryParse(Console.ReadLine(), out count1));

                do
                    Console.WriteLine("計測を行う回数を指定してください...");
                while (!int.TryParse(Console.ReadLine(), out count2));

                //------------------------------//
                // 計測 string
                //------------------------------//

                measuredList.Clear();
                Console.WriteLine("-------------------------------------------------------");
                Console.WriteLine("stringクラスで計測します。");

                GC.Collect(GC.MaxGeneration);
                for (int i = 0; i < count2; i++)
                {
                    measuredList.Add(MeasureTime_String(count1));
                    Console.WriteLine((i + 1).ToString().PadLeft((int)Math.Log10(count2) + 1) + "回目 : " + measuredList[i]);
                }
                Console.WriteLine("平均 : " + measuredList.Average());

                //------------------------------//
                // 計測 StringBuilder
                //------------------------------//

                measuredList.Clear();
                Console.WriteLine("-------------------------------------------------------");
                Console.WriteLine("StringBuilderクラスで計測します。");

                GC.Collect(GC.MaxGeneration);
                for (int i = 0; i < count2; i++)
                {
                    measuredList.Add(MeasureTime_StringBuilder(count1));
                    Console.WriteLine((i + 1).ToString().PadLeft((int)Math.Log10(count2) + 1) + "回目 : " + measuredList[i]);
                }

                Console.WriteLine("平均 : " + measuredList.Average());

                //------------------------------//
                // 計測 StringBuilder + Append
                //------------------------------//

                measuredList.Clear();
                Console.WriteLine("-------------------------------------------------------");
                Console.WriteLine("StringBuilderクラス + Appendメソッドで計測します。");

                GC.Collect(GC.MaxGeneration);
                for (int i = 0; i < count2; i++)
                {
                    measuredList.Add(MeasureTime_StringBuilderAppend(count1));
                    Console.WriteLine((i + 1).ToString().PadLeft((int)Math.Log10(count2) + 1) + "回目 : " + measuredList[i]);
                }
                Console.WriteLine("平均 : " + measuredList.Average());

            }
            finally
            {
                Console.WriteLine("何かキーを押してください...");
                Console.ReadKey();
            }
        }

        /// <summary>
        /// stringクラスを指定された回数確保し、その時間を計測します。
        /// </summary>
        /// <param name="count">回数</param>
        /// <returns>時間(秒)</returns>
        static double MeasureTime_String(int count)
        {
            DateTime strT = DateTime.Now;

            for (int i = 0; i < count; i++)
            {
                string s = "";
                s = s + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
                Program.d_out = s.ToString();
            }

            DateTime endT = DateTime.Now;

            return (endT - strT).TotalSeconds;
        }

        /// <summary>
        /// StringBuilderクラスを指定された回数確保し、その時間を計測します。
        /// </summary>
        /// <param name="count">回数</param>
        /// <returns>時間(秒)</returns>
        static double MeasureTime_StringBuilder(int count)
        {
            DateTime strT = DateTime.Now;

            for (int i = 0; i < count; i++)
            {
                StringBuilder s = new StringBuilder("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890");
                Program.d_out = s.ToString();
            }

            DateTime endT = DateTime.Now;

            return (endT - strT).TotalSeconds;
        }

        /// <summary>
        /// StringBuilderクラスを指定された回数確保し、Appendメソッドで文字列を指定します。その時間を計測します。
        /// </summary>
        /// <param name="count">回数</param>
        /// <returns>時間(秒)</returns>
        static double MeasureTime_StringBuilderAppend(int count)
        {
            DateTime strT = DateTime.Now;

            for (int i = 0; i < count; i++)
            {
                StringBuilder s = new StringBuilder();
                s.Append("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890");
                Program.d_out = s.ToString();
            }

            DateTime endT = DateTime.Now;

            return (endT - strT).TotalSeconds;
        }
    }
}

汚いとか言わない。

実際にやってみた。

実際に動かしました。スペックは以下の通り。

  • CPU: Atom Z3775 (1.46GHz 4core)
  • メモリ: 2GB

f:id:yuzutan_hnk:20170226021055p:plain:w300

やっぱり、string型のほうがオーバーヘッドは少ないみたいですね。


追記 2017/02/26 実験に使ったスペックを記述していなかったので追記。

現在 0000/00/00 00:00 を生きています。