Ubuntuのtaskselを使って、ラクしてKVM環境を作る
Linuxで楽しよう。Linuxを楽しもう。(名言っぽく言ってみるだけ)
ふと tasksel
でxubuntu 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アドレスなどが定義されていることを確認。
参考サイト
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>
enpX
はNICのアドレスのようなもので、 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
やっぱり、string型のほうがオーバーヘッドは少ないみたいですね。
追記 2017/02/26 実験に使ったスペックを記述していなかったので追記。
内臓HDDのUbuntuをUSBメモリのGrub2から叩く。
非常に初歩的なことですが、かなり調べて納得がいったようないってないようなところまで行きついたので、メモっておきます。
今回は 起動できればいい というスタンスで行くので、詳しいことは全くわかりません。これから勉強します。
環境
今回試したのは、以下の環境。
- UEFI (lenovo BIOS)
- SATA : 480GB SSD (MBR)
- SATA : 1TB HDDドライブ (GPT、2番目のパテーションにUbuntuインストール済み)
- USB : Ubuntu日本語Remix インストールDVDイメージ(8GB)
目標は簡単なことです。 USBのGrub2 から SATAのHDDのUbuntuを起動する だけです。普通はしないけど。
なんでこんなまどろっこしいことがしたいかというと、Ubuntuを内臓HDDにインストールしたまではよかったんですけど、UEFIが入っているはずのGrub2を認識してくれないのです。もしかしてUEFIモード対応のBIOSだったのかこれ?
順序
あくまで理論的な話。
Grub2が起動すると、規定ではOSを選ぶメニューが表示されます。
このとき、メニューで「c」キーを押すとシェル画面に遷移します。この画面ではbashライクな初歩的なコマンドにより、ドライブの中身を確認したりOSをブートさせたりできます。
この機能を使って、実際にUSBのGrub2のメニューには出てこない、内臓HDDのUbuntuを起動させます。
手順
まずは確認
まずは、ディスクの名前を確認します。これは ls
コマンドで一覧を取得できます。
grub> ls (memdisk) (hd0) (hd0,msdos1) (hd1) (hd1,gpt4) (hd1,gpt3) (hd1,gpt2) (hd1,gpt1) (hd2) (hd2,msdos1)
現在起動に使っているUSBは (hd0)
として認識されています。試しに、 (hd0)
の情報を表示してみます。
なんか、大量にエラーが出ていますが、今回は 起動ができればいい ので無視します。一応、最後の一文がそれっぽい結果になっています。
grub> ls (hd0) Device hd0: No known filesystem detected - Sector size 512B - Total size 7834624KiB
確かに容量が8GBに近いですので、起動USBと考えて間違いないでしょう。
次に、起動するUbuntuの入ったHDDを確認します。
たぶん見るからに (hd1)
っぽいです。なぜかというと、grub上(というかLinuxでは全般に)では、 MBRパテーションテーブルは msdos
として表示 されますし、 GPTパテーションテーブルは gpt
として表示 されるからです。起動したいのはGPTのHDDです。
grub> ls (hd1) Device hd1: No known filesystem detected - Sector size 512B - Total size 976762584KiB
案の定ですね。これが1TBのHDDです。
では、今度はUbuntuを起動させるため、Linuxカーネルの配置を確認します。パテーションの1つめはESPなので、2つめのファイル一覧を取得します。
grub> ls (hd1,gpt2)/ lost+found/ boot/ hdata/ etc/ media/ var/ bin/ dev/ home/ lib/ lib64/ mnt/ opt/ proc/ root/ rules.d/ run/ sbin/ snap/ srv/ sys/ tmp/ usr/ vmlinuz initrd.img cdrom/ initrd.img.old vmlinuz.old
Ubuntuのルートですね。さて、これでUbuntuを起動させられるだけの情報は整いました。
え?カーネルのフルネームを確認できていないって?そうそう、なんだか知りませんが、 システムのルートにシンボリックリンクが張ってあるのでそれを使えば問題ない そうです。
起動する
では、実際に起動します。次のコマンドを打って、終わり。
grub> set root=(hd1,gpt2) grub> linuxefi /vmlinuz root=/dev/sda2 grub> initrdefi /initrd.img grub> boot
linuxefi
コマンドで、起動するLinuxカーネルを指定します。今回はシステムルートにある vmlinuz
というシンボリックリンクを使って指定しています。これがない場合は、 boot/
以下にある本体をフルネームで指定します。
同時にシステムのルートを指定します。 root=/dev/sda2
の部分です。多くのサイトの解説では、今回のような (hd1,gpt2)
をルートとしたい場合、 /dev/sdb2
とするのが一般的だそうです。たしかに (hd0)
があるので、それを sda
とすると、 (hd1)
は sdb
になります。理屈はわかります。
しかし、 それでは起動しませんでした 。憶測ですが、もしかしたら起動時にカーネルからデバイスの再スキャンが行われて、カーネルが認識した順番がGrubの認識した順番と違う、という現象が起こっているのかもしれません。全く分からない。
次に initrd
コマンドですが、どうもモジュール等を含むデータをメモリに読み込むためのコマンドのようです。よくわかりませんが、これも initrd.img
というシステムルートにあるシンボリックリンクを使います。ない場合は、 boot/
以下にある本体をフルネームで指定します。
最後に boot
コマンドでブートさせます。
ちなみに、UEFI環境でない場合は、efi
がついていないコマンドでブートします。UEFIでも使えるので、実質どちらでもいいのでは…?
grub> set root=(hd1,gpt2) grub> linux /vmlinuz root=/dev/sda2 grub> initrd /initrd.img grub> boot
最後に
無事に起動できました。grubのシェルを直で触るなんて初めてで、いろいろ試行錯誤して、起動できたときは本当に感動しました。大歓喜でした。
今回は本当に 起動できればいい というスタンスなので、あまり深入りはしませんが、これで調べるべき対象が一つ増えた気がします。
やっぱりLinuxは楽しい(まだ何もわかっていないのに)。
C#でJavascriptみたいな即時関数を実行する
個人的には、「即時関数」より「即時構文」というほうがしっくり来ます。
名づけ親の本では「即時関数パターン」とされているので、それでもしっくり来ます。
「即時関数」じゃ、少し足らない気がします。
Javascriptでよく使われる即時関数について
まず即時関数というものが何者かを解説します。
ずばり、匿名関数をその場で実行する書き方です。
普通は関数を別に定義して、名前()
で呼び出すのが普通ですが、Javascriptでは関数が立派なオブジェクトですので名前をつける必要がありません。そこで、名前を付けずにその場で実行してしまおうという書き方になります。なんとも野心的。
書き方
例1:
var x = (function() { return 1; })();
上記では、x
に 1 が代入されます。つまり、function { return 1; }
が実行され、その結果が代入されます。
例2:
var x = (function(num) { return function(innNum) { return innNum * num; }; })(5);
上記では、xにはfunction(innNum) { return innNum * 5; }
が代入されます。
一見「なんだこれ?」ですが、中でfunction
を返しているというところに注目してください。
中のfunction
文は即時関数を使っていないので、実行されません。すなわち匿名関数の定義のみ行います。
そして、Javascriptでは匿名関数の定義は評価時に行われるという特徴があります。今回の場合、クロージャの影響でnum
に 5 が入っていることを参照してから定義するため、返される匿名関数内のnum
は 5 に固定されるのです。
C#で書く
C#は最近、匿名関数だのラムダ式だの、Javascriptや関数型言語のパラダイムを意欲的に取り入れているため、同じように即時関数を書くことができるようです。
Javascriptで挙げた2つの例を、C#で書き直してみます。
例1:
int x = new Func<int>(() => 1)();
C#はJavascriptと違って静的な型付け言語のため、匿名関数も型にはめてあげないといけないようです。new Func<int>
を省略すると「メソッド名が必要です」というコンパイルエラーが発生します。
例2:
Func<int, int> x = new Func<int, Func<int, int>>((num) => new Func<int, int>((innNum) => innNum * num))(5);
Func<>
がとんでもなく多い。さらに、引数と戻り値と型をいちいち指定しなければならない。この場合はちょっと無理がありそうですね。
関数を返すような処理をしなければ、一応使えそうな書き方ですね。
初心者が初めてグラボを取り付けたお話
去年の暮れ、はじめてグラフィックボードなるものをパソコンに取り付けました。
理由としては、年賀状の作成に使っているInkscapeのフィルタ機能が極端に重いので、
「もしかしてグラボつけたら速くなるんじゃね?」
という安直な思いの元、購入したのでした。
結果速くはならなかったんですけど。
初めてグラボを買う
今回、購入したグラボはこちら↓
GeForce GTX 1050です。Tiではないのでビデオメモリは2GBです。1050にしては特徴ガン無視の補助電源ありのモデルです。
価格として15000円強。普通の1050なら10000円ちょっとで買えるものなのですが、HDMIが3本出力できるというところで選びました。今のところ2つしか使いませんが、ちょうど2つなんでいうグラボはなかったんですね。
早速開ける
半分くらいが発泡スチロール。そして本体は黒みがかったビニールに包まれています。
取り出しました。まあまあの重さです。
大きなファンとごついプラスチックケース。この中に基盤が収まっているんですね。
グラボって無駄にかっこいいよね
たしかにHDMIが3本、DisplayPort、DVIがあります。すべてキャップが付いてます。
これがパソコンの背面に出てくる形になるんですね。
取り付け準備
まず、PCケース背面にあるカバーを取り外します。
ねじで止まっているので、ドライバーを使って外します。外すと、外側から中が見えるようになります。
まずは練習
初めてパソコンにパーツを増設するため、一応練習も兼ねて小さな基盤も購入しました。
こちらはPCI Express x1に指すとUSBの口が4つ(?)増えるものです。背面にAポートが2つ、そしてヘッダが1つ増えます。もともとのUSBの口が足らなくなってきたのもあったので、購入しました。
端子をあわせて……
挿す。
すると、背面にちょうどUSBの端子が見えるようになります。
最後に、このUSB基盤は補助電源にSATAの口があるため、ハードディスク用のSATA電源をつなぎます。そして、先ほど外したねじでカバーの部分を固定します。
案外、簡単じゃない?
グラボを取り付ける
次にグラボを取り付けていきます。
グラボも同じ要領で、端子を合わせて挿すだけです。ただし本体が分厚いため、斜めにして確認することは難しいです。また先にUSB基盤を取り付けてしまったため、横から確認することも難しいです。
とりあえず勘で挿したらうまくいきました。なんとか。
PCI Express x16 は1つしか使いませんが、背面は2スロット使います。
グラボの補助電源は、PCI Express 補助電源コネクタを使用します。
基本的に6ピン構成、電力が大きいものだと8ピン構成のものもあります。
端子を合わせて…
挿します。
グラボもカバー部分をねじで止めます。
以上で完成です。繋ぐだけです。本当に簡単ですね。
ドライバインストール
ハードが完成したので、今度はソフト側を構成します。
グラボを挿すと、自動的にビデオ出力はグラボ側から出るようになります(オンボードは使えなくなります。使える場合もあるそうですが)
ただし初回はドライバがインストールされていないため、互換モード(640x480?)で出力されます。
このような付属のCDがあればそこから、インターネットにつながっていれば公式サイトから落とすことができます。
が、ここで予想外の出来事が。
自動でドライバを認識してくれた!!
このあと、再起動すると普通にネイティブ解像度で出力され、環境構築が完了していました。うーん、なんだか微妙な気持ち…
ベンチマーク
Plag and Playの影響なのか何なのかはわかりませんが、すぐに使えるようになったので早速ベンチマークします。
今回使ったのは、ドラゴンクエスト10 と PSO2 のベンチマークソフトです。
ゲームが本来の目的ではないので、一応簡単にできればいいかなと…
ドラクエ10
- 設定
- 画質 : 最高画質
- 解像度 : 1920x1080
- フルスクリーン
- その他スペック
- CPU : i7-6700
- メモリ : 16GB
↓(装着前) オンボード Intel HD Graphics
↓(装着後) GeForce GTX 1050
結構上がりましたね。
ドラクエ10はそもそも、オンボードでも結構快適に動いていたんですよね。スコアはスコアですが、結果としても「標準」として出ているように、そこまで高スペックを求めなければ十分遊べそうな環境でした。
しかし、やはりグラボを載せると安定さが違いますね。ムービーの見た目はそこまで変わりませんでしたが、たぶん戦闘中などパーティクルが多くなった時のカクツキが少なくなる(というかなくなる)気がします。
PSO2
- 設定
- 画質 : 6
- ゲーム設定-描画設定 : すべてON
- ゲーム設定-詳細設定 : 50(max)
- 解像度 : 1920x1080
- フルスクリーン
- その他スペック
- CPU : i7-6700
- メモリ : 16GB
↓(装着前) オンボード Intel HD Graphics
↓(装着後) GeForce GTX 1050
破壊的に変わってますね
オンボードだとFPSが常時20前後、影や反射などの描画がおかしかったりと、とてもゲームができる環境ではありませんでした。
しかしグラボをつけると、もうカクツキから完璧に解放されたと言えます。すべてのエフェクトをつけてもコマ落ちすることはなく、常にFPS120あたりを推移していました。どうせ今のディスプレイは最高60Hzあたりでしょうから、申し分ありません。
結果
グラフィック性能はものすごく上がりました。これでゲームははかどりそうです。
しかし、実際にInkscapeで年賀状を作るときの処理は、まったくもって変わりませんでした。あれはCPUに描画処理をすべて任せているのでしょうね。
同じように、いつも遊んでいるK-shoot maniaという音ゲーも、DirectXを使ってはいますが肝心の譜面部分がソフトウェア描画になっているので、全く変わりませんでした。
やっぱり、普段からゲームをしない人にとっては不必要なんでしょうかねぇ…。