24
|
1 \chapter{画面共有システムTreeVNCの評価}
|
|
2 \section{実験環境}
|
|
3 TreeVNCは多人数の同時接続を可能にするソフトウェアである。よって、評価を行うためには多数のコンピュータが必要となる。\\
|
|
4 今回は、学科の並列計算環境とOSの授業の時間を使用させてもらって実験を行った。\\
|
|
5 使用した並列計算環境を表\ref{tab:cluster_spec}に示す。\\
|
|
6 \begin{table}[!htbp]
|
|
7 \caption{検証に利用するVMWareクラスタの仕様}
|
|
8 \label{tab:cluster_spec}
|
|
9 \begin{center}
|
|
10 \begin{tabular}{|c||c|} \hline
|
|
11 名前 & 概要 \\ \hline \hline
|
|
12 CPU & Intel(R) Xeon(R) CPU X5650@2.67GHz \\ \hline
|
|
13 物理コア数 & 2 \\ \hline
|
|
14 論理コア数 & 4 \\ \hline
|
|
15 CPUキャッシュ & 12MB \\ \hline
|
|
16 Memory & 8GB \\ \hline
|
|
17 OS & CentOS 5.8 \\ \hline
|
|
18 HyperVisor & VMWare ESXi \\ \hline
|
|
19 \end{tabular}
|
|
20 \end{center}
|
|
21 \end{table}
|
|
22 \subsection{CUI Versionの作成}
|
|
23 TreeVNCはGUI(Graphical User Interface)を使用したアプリケーションである。並列計算環境はCUI(Character User Interface)なので、そのままだと実行することができない。\\
|
|
24 そこで、TreeVNCからGUIを使用している部分を取り除いてCUI環境で実行できるようにする必要があったので作成することにした。\\
|
|
25 Listing\ref{src:tree}はTreeVNCのMain文の一部である。ここで継承されているJAppletは、GUIのコンポーネントなので、このクラスを使用するとCUI環境で実行することができない。\\
|
|
26 CUI環境で実行するにはJAppletを継承していないクラスを作成する必要があるが、Listing\ref{src:getclass}のようなViewerクラスを受け渡す場所をすべてでCUIとGUIのクラスに対応しなければならない。\\
|
|
27 この問題に対して、CUIとGUIの共通Interface(Listing\ref{src:interface})を作成し、このInterfaceを利用することで解決した。
|
|
28
|
|
29
|
|
30 \begin{lstlisting}[language=java,frame=lrbt,label=src:main,caption=TreeVNCのMainClass,numbers=left]
|
|
31 public class Viewer extends JApplet implements Runnable, WindowListener
|
|
32 {
|
|
33 final ConnectionView connectionView = new ConnectionView(Viewer.this,
|
|
34 connectionPresenter, hasJsch);
|
|
35 }
|
|
36 \end{lstlisting}
|
|
37
|
|
38 \begin{lstlisting}[language=java,frame=lrbt,label=src:getclass,caption=Viewerの受け取り,numbers=left]
|
|
39 public SwingViewerWindowFactory(boolean isSeparateFrame, boolean isApplet, Viewer viewer)
|
|
40 {
|
|
41
|
|
42 }
|
|
43 \end{lstlisting}
|
|
44
|
|
45 \begin{lstlisting}[language=java,frame=lrbt,label=src:interface,caption=ViewerImpl,numbers=left]
|
|
46 public interface ViewerImpl
|
|
47 {
|
|
48 public boolean getCuiVersion();
|
|
49 public MyRfbProto getRfb();
|
|
50 public void closeApp();
|
|
51 public void run();
|
|
52 }
|
|
53 \end{lstlisting}
|
|
54
|
25
|
55 \subsection{Capistrano}
|
26
|
56 今回の実験では、48台のサーバ上でCUI版のTreeVNCを立ち上げる必要がある。実験する度に、各サーバにログインしてアプリケーションを立ち上げるのは手間がかかりすぎてしまう。Capistranoを使用することで、この問題を解決することができる。\\
|
25
|
57 Capistranoは複数のサーバ上で同時に処理を実行するためのオープンソースなソフトウェアであり、Rubyを用いて作成されている。\\
|
24
|
58
|
25
|
59 Capistranoを実行する際に使用するスクリプトをListing\ref{src:capistrano}に示す。\\
|
24
|
60 スクリプトはListing\ref{src:cap_run}として実行することができる。\\
|
|
61
|
|
62
|
|
63 \begin{lstlisting}[language=ruby,frame=lrbt,label=src:capistrano,caption=cap.rb,numbers=left]
|
|
64 set :user, "mass" //実行するユーザ
|
|
65
|
|
66 role :pall, "133.13.62.1" //ここに命令を送りたいマシンのアドレス
|
|
67
|
|
68 task :ls do // task名
|
|
69 run "ls -la" // 実行したい命令
|
|
70 end
|
|
71 \end{lstlisting}
|
|
72
|
|
73 \begin{lstlisting}[language=ruby,frame=lrbt,label=src:cap_run,caption=capistranoの実行,numbers=left]
|
|
74 % cap -f cap.rb ls
|
|
75 \end{lstlisting}
|
|
76
|
|
77 \section{木の深さによる遅延}
|
26
|
78 TreeVNCは、クライアントを木構造に配置し画像を配信している。
|
24
|
79 木の深さが深くなってしまうと、データが下に届くまでに遅延が発生してしまう可能性がある。
|
26
|
80 そこで、木の深さによる遅延がどの程度発生するのかを測定してみた。
|
24
|
81 \subsection{遅延の測定方法}
|
25
|
82 RFBプロトコルでは、送られてくるデータの先頭にどのような処理をするかの命令番号が入っている。\\
|
24
|
83 表\ref{tb:message}は送られてくるメッセージの一覧である。\\
|
|
84 命令番号11(CheckDelay)はプロトコルを拡張して作成した命令である。
|
|
85
|
|
86 \begin{table}[htbp]
|
|
87 \caption{Rfbプロトコルと追加したメッセージ一覧}
|
|
88 \label{tb:message}
|
|
89 \begin{center}
|
|
90 \begin{tabular}{|c||c|c|} \hline
|
|
91 命令番号 & 名前 & 説明 \\ \hline
|
|
92 0 & FrameBufferUpdate & 画像の更新情報\\ \hline
|
|
93 1 & SetColourMapEntries & ピクセルフォーマットでColour Mapを使用\\ \hline
|
|
94 2 & Bell & ビープ音機能\\ \hline
|
|
95 11 & CheckDelay & 画像が届くまでのDelayを測定\\ \hline
|
|
96 \end{tabular}
|
|
97 \end{center}
|
|
98 \end{table}
|
|
99
|
26
|
100 Listing\ref{src:delay_cli}、Listing\ref{src:delay_serv}は遅延を測るためのプログラムである。\\
|
|
101 Root NodeはSystem.currentTimeMillis()を用いて時間を取得し、Nodeへ送信する。
|
|
102 System.currentTimeMillis()は、システムの現在時刻をミリ秒(long型の数値)で取得する関数である。
|
|
103 Nodeはこの値を受け取ると、そのままサーバへ受け取った値を返す。
|
24
|
104 Root Nodeはクライアントからの返信を受け取るとSystem.currentTimeMillis()を取り、差分を出してDelayを求める。
|
|
105 遅延を測る頻度は、画像を50回送信するごとに1回である。
|
|
106 \begin{lstlisting}[language=java,frame=lrbt,label=src:delay_cli,caption=遅延を測るプログラム,numbers=left]
|
|
107 BufferedReader is = new BufferedReader(new InputStreamReader(echoSocket.getInputStream()));
|
|
108 DataOutputStream os = new DataOutputStream(echoSocket.getOutputStream());
|
|
109 os.writeBytes("checkdelay\n");
|
|
110 os.writeBytes(String.valueOf(buf.getLong(16))+"\n");
|
|
111 \end{lstlisting}
|
|
112
|
|
113 \begin{lstlisting}[language=java,frame=lrbt,label=src:delay_serv,caption=遅延を測るプログラム,numbers=left]
|
|
114 Long delay = System.currentTimeMillis()-Long.parseLong(is.readLine());
|
|
115 \end{lstlisting}
|
|
116
|
|
117 \subsection{遅延の測定結果}
|
|
118 2分木で木を構成した場合、Node数が48台だと深さが6となる。\\
|
|
119 Root Nodeを起動し、並列計算環境48台を起動し、Root Nodeから一番下のNodeまでどのくらいの時間がかるのかを測定した。\\
|
25
|
120 \ref{tab:delay} はデータを20回ほど測定し最遅値を取った遅延の表である。\\
|
24
|
121 \begin{table}[!htbp]
|
|
122 \caption{データ送信の遅延}
|
|
123 \label{tab:delay}
|
|
124 \begin{center}
|
|
125 \begin{tabular}{|c||c|} \hline
|
25
|
126 段数 & 最遅値 \\ \hline \hline
|
|
127 2 & 32ミリ秒\\ \hline
|
|
128 4 & 244ミリ秒\\ \hline
|
|
129 6 & 446ミリ秒\\ \hline
|
24
|
130 \end{tabular}
|
|
131 \end{center}
|
|
132 \end{table}
|
|
133
|
26
|
134 \begin{figure}[!htbp]
|
|
135 \begin{flushleft}
|
|
136 \includegraphics[scale = 0.8]{images/graph-lost.pdf}
|
|
137 \end{flushleft}
|
|
138 \caption{
|
|
139 段差(step)によるデータの遅延
|
|
140 }
|
|
141 \label{fig:graph-late}
|
|
142 \end{figure}
|
24
|
143
|
|
144
|
26
|
145 並列計算環境のVMは高速なネットワークでつながっているので、遅延が少ない可能性がある。
|
|
146 そこで、実際にOSの授業で、どの程度の遅延があるのかを測定してみた。\\
|
|
147 OSの授業のときは25Nodeであったので、段数にすると5段である。
|
|
148 結果は、164ミリ秒と並列計算環境より良い結果が出た。
|
24
|
149
|
26
|
150 図\ref{fig:graph-late} は遅延の分布を示したヒストグラムである。X軸は試行の回数で、Y軸は遅延(ミリ秒)を表している。\\
|
|
151
|
|
152
|
|
153 \newpage
|
24
|
154 \section{画面のフリーズ}
|
|
155 データがTimeOutによってどの程度損失しているのかを調べてみた。\\
|
|
156 今回、測定するために画像データのヘッダーの前にシリアルナンバーを付加した(Listing\ref{src:serial})。これによりNode側は、順番通りに画像が来なかった場合、データが損失したことを知ることができる。
|
|
157 Node側の確認用コードをListing\ref{src:timeout}に示す。このコードはRoot Nodeから流れてきたデータを受け取り自分の持っているcheckCounterと比較して、違う値が出ていればデータが損失していることになる。
|
|
158
|
|
159 \begin{lstlisting}[language=java,frame=lrbt,label=src:serial,caption=データの確認プログラム(Root Node側),numbers=left]
|
|
160 ByteBuffer serialNum = ByteBuffer.allocate(8);
|
|
161 serialNum.putLong(counter++);
|
|
162 serialNum.flip();
|
|
163 bufs.addFirst(serialNum);
|
|
164 multicastqueue.put(bufs);
|
|
165 \end{lstlisting}
|
|
166
|
|
167 \begin{lstlisting}[language=java,frame=lrbt,label=src:timeout,caption=データの確認プログラム(Node側),numbers=left]
|
|
168 private void getLost(Reader reader)
|
|
169 {
|
|
170 try
|
|
171 {
|
|
172 long num = reader.readInt64();
|
|
173 if(num != ++checkCounter) {
|
|
174 System.out.println("LostData:"+(num - checkCounter));
|
|
175 checkCounter = num;
|
|
176 }
|
|
177 } catch (TransportException e) {
|
|
178 e.printStackTrace();
|
|
179 }
|
|
180 }
|
|
181 \end{lstlisting}
|
|
182
|
|
183 現在の実装では、0.625秒データの読み込みがなければ、データをTimeOut Threadが読み込み、Node側には順番通りのデータが行かなくなるので画面がフリーズしたように見える。\\
|
|
184 実験の結果、6段目のNodeでデータを受け取って表示してみた結果、データが損失するのを見ることができなかった。\\
|
|
185 データがロストしなのは良いことであるが、その分Root Nodeのメモリ上にデータがあるので、Root NodeがMemoryOverFlowを起こす可能性がある。私のディスプレイ環境(1920*1080)では、MemoryOverFlowが起こることはなかったが、Retinaディスプレイなどの高解像度ディスプレイを使用している場合は、MemoryOverFlowを起こす可能性がある。TimeOutの時間は今後調整が必要である。
|
|
186
|
|
187
|
|
188
|
|
189 \section{分木の最適化}
|
|
190 木の分木数が少ないほうが一つ一つのコンピュータにかかるCPU負荷が少なくなる。しかし、分木数を少なくしてしまうと、Nodeが増えたとき、木の深さが深くなり、データの伝搬に遅延が起こる可能性がある。
|
26
|
191 上記の実験の結果、木の深さが6のときのデータ伝搬に最遅446ミリ秒(0.446秒)の遅延しか起こっていなく、データの欠損も見られなかった。
|
24
|
192 分木数を変更してもコネクションの数はかわらないし、スイッチに対する負荷も変わらない。よって、100人程度で使用する場合は2分木が最適であるということがわかった。
|
|
193
|
|
194 \section{ZRLEとZRLEEのデータ圧縮率の比較}
|
|
195 作成したTreeVNCでは、従来のVNCで使用されているエンコードを使用しておらず、独自で作成しているZRLEEエンコードを使用している。\\一見ZRLEは辞書が一つでZRLEEは辞書が一つ一つの画像データに付加されていて、データ量はZRLEEのほうが多くなってしまっている可能性があるので、ZRLEEとZRLEのデータ量にどの程度の差が出るのかを調べてみた。
|
|
196 全く圧縮されていないRAWデータ,Zlib圧縮を使用しているZRLEE、ZRLEのデータ量の比較を行った。
|
|
197 図6は1920 * 1080の画面の全描画にかかるデータ量を測った結果を示した図である。
|
|
198 ZRLEEの方がデータ量が少なくですんでいる(図\ref{fig:compare_encoding})。
|
|
199 これは、ZRLE(Zlib)が初めに送られた辞書を用いての解凍が余り有効的に働いていない場合があるからだと思われる。
|
|
200 つまりVNCの場合はZRLEEの様に毎回辞書のデータを付加させて送ってもデータ量に差がでない可能性があることが分かった。
|
|
201
|
|
202
|
|
203 \begin{figure}[!htbp]
|
|
204 \begin{center}
|
|
205 \includegraphics[scale = 0.8]{images/compare_encoding.pdf}
|
|
206 \end{center}
|
|
207 \caption{
|
|
208 RAW,ZRLE,ZRLEEによる1画面(1920*1080)描画にかかるデータ量。
|
|
209 x軸はピクセル数、y軸はバイト数を表している。
|
|
210 }
|
|
211 \label{fig:compare_encoding}
|
|
212 \end{figure}
|
|
213
|
|
214
|
|
215 \newpage
|
|
216
|
|
217 \section{VNC Reflectorとの比較}
|
|
218 TreeVNCを用いて、Vnc Reflectorとの比較を行った。並列計算環境のVM48台を使用し、Bladeサーバの外にTreeVNCとVnc Reflectorを起動させたPCを置き、VM48台にアクセスさせ実験を行った。一極集中型の Vnc Reflectorは、48台繋がった時にスループットが2MBpsから5KBpsへ下がっていた、一方、TreeVNCのスループットは48台繋がっている状態でも2MBpsを保つことができていた。一極集中型で繋がっているVNC Reflectorと違い、TreeVNCは48台のアクセスを複数の通信網へ分散しCPU負荷をNode側の分散したいるので、人数が増えてもスループットを落とすことなくアプリケーションを実行することができる。
|