changeset 9:7e1112025b3a

modify chapter4
author sugi
date Thu, 08 Jan 2015 20:36:01 +0900
parents f948d683c29a
children 198cebfd31a3
files paper/chapter4.tex paper/source/CommandMessage.java paper/source/CommandMessageBefore.java paper/source/CreateByteBuffer.java paper/source/ReceiveData.java paper/source/asClass.java
diffstat 6 files changed, 147 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/paper/chapter4.tex	Wed Jan 07 19:01:15 2015 +0900
+++ b/paper/chapter4.tex	Thu Jan 08 20:36:01 2015 +0900
@@ -42,12 +42,83 @@
 しかし、Local Data Segmentに対する通信においては逆効果である。データをLocal Data Segmentに対してputするたびにValue型に変換するコストがかかる。データをpeekする際にもValue型から元の型に変換するというコストがかかる。
 
 この問題を解決するために、一般的なJavaのクラスオブジェクトでもデータ表現を可能にした。Local Data Segmentに対してputする場合は、Valueオブジェクトに変換せず一般的なJavaのクラスオブジェクトのままで、Remote Data Segmentに対してputする場合にのみValueに変換する。これにより、無駄な変換コストを抑えられるようになった。
+
 \section{分散環境における改善}
 AliceVNCを実装するにあたり、Aliceの送受信部分に問題が発見された。ここでは発見された問題の解決方法を示す。
-\subsection{Data Segmentのデータ表現の変更}
+
+\subsection{Data Segmentのデータ表現の変更} \label {subsection:changeDSFormat}
 AliceVNCは、\ref {section:AliceVNC}で説明したように、当研究室で開発しているTreeVNCを分散フレームワークAliceを用いて実装した画面共有システムである。
 
 Topology Nodeは受け取った画面データを描画すると同時に、Remote Data Segmentに送信する。Remote Data Segmentに送信する際にはMessage PackによりValue型に変換し、その後シリアライズ化(byteArrayで表現されたバイナリに変換)される。Topology Nodeは受信するとデシリアライズしValue型に変換した後putされる。
 
-このValue型への変換が問題である。受け取ったデータを自分の子ノードに対して送信する際には、デシリアライズしValue型に変換する必要はない。シリアライズ状態のまま子ノードに送信すれば、Value型に変換するオーバーヘッドとValue型をシリアライズするオーバーヘッドを無くすことができる。そこで、Remoteからputされたデータ表現をValue型からbyteArrayで表現されたbinaryに変更した。
-\subsection{Protocolの再設計}
\ No newline at end of file
+このValue型への変換が問題である。受け取ったデータを自分の子ノードに対して送信する際には、デシリアライズしValue型に変換する必要はない。シリアライズ状態のまま子ノードに送信すれば、Value型に変換するオーバーヘッドとValue型をシリアライズするオーバーヘッドを無くすことができる。そこで、Remoteからputされたデータ表現をValue型からbyteArrayで表現されたbinaryに変更した。また、Remoteにputする際にもValue型に変換せずに直接byteArrayに変換するように変換した。
+
+しかし、この変更で新しい問題が発生した。Remoteからputされたデータは必ずbyteArrayで表現される。しかし、putされたbyteArrayが全てシリアライズ化された状態であるとはいえない。一般的なJavaのクラスオブジェクトとしてbyteArrayが使用されている場合が存在する。例えば、AliceVNCで使われる画像データはbyteArrayで表現されているが、これはLocalからputされている。 Input Data Segmentが格納されるReceiverクラスには{\tt asClass()}というメソッドがある。
+
+\begin{itemize}
+\item \verb+public <T> T asClass(Class<T> clazz)+
+\end{itemize}
+
+このメソッドは取得したデータがRemoteからputされた場合、Value型でなっているためMessage Packを使い適切な型に変換するものである。しかし、byteArray型に変更したため、変換可否を判断することができなくなってしまった。
+
+ここからわかることは、データを表現するにはデータ単体をやりとりするだけでは不十分ということである。変更以前はValue型であるということが状態を表していた。しかし、一般的なJavaのクラスオブジェクトとbyteArrayで表現されたbinaryが混在する現在では、データと一緒にデータの状態を表すメタデータもやりとりする必要がある。そこで、データとデータの状態を1つのオブジェクトにまとめ扱うように変更した。(ソースコード\ref {src:ReceiveData})
+
+\begin{table}[html]
+\lstinputlisting[label=src:ReceiveData, caption=データを表現するクラス]{source/ReceiveData.java}
+\end{table}
+
+{\tt val}がデータ本体が保存格納される。{\tt serialized}と{\tt byteArray}がデータの状態を表すメタデータである。{\tt serialized}
+は、データ本体がシリアライズ化されているかを示す。{\tt byteArray}がデータ本体がbyteArrayであるかを示す。
+この2つの状態があることで{\tt asClass()}を使い、適切に変換することができる。(ソースコード\ref {src:asClass})
+
+\begin{table}[html]
+\lstinputlisting[label=src:asClass, caption=asClassの処理]{source/asClass.java}
+\end{table}
+
+asClassが行う処理は、Localからputされたデータ({\tt serialized}と{\tt byteArray}がfalseの場合または{\tt byteArray}のみtrueの場合)は、目的のClassにcastする。Remoteからputされたデータ({\tt serialized}がtrueの場合)はMessage Packを使い変換する。
+
+\subsection{パケットの再設計}
+Aliceの通信の際には、CommandMessage.classのインスタンスをMessage Packによりシリアライズ化したものが送信される。
+つまり、CommandMessage.classがパケットの構造を表すものといえる。
+
+\begin{table}[html]
+\lstinputlisting[label=src:CommandMessageBefore, caption=変更前のCommandMessage]{source/CommandMessagebefore.java}
+\end{table}
+ソースコード\ref {src:CommandMessageBefore}が変更前のCommandMessageの内容である。表\ref{tb:variable}はCommandMessageの各変数が何を表しているかを示したものである。
+\begin{table}[htbp]
+\caption{CommandMessageの変数名の説明}
+\label{tb:variable}
+\begin{center}
+\begin{tabular} {|l|l|}
+  \hline
+  変数名&説明\\
+  \hline
+  type&CommandType {\tt PEEK, PUT}などを表す\\
+  \hline
+  seq&Data Segmentの待ち合わせを行っているCode Segmentを表すunique number\\
+  \hline
+  key&どのKeyに対して操作を行うか指定する\\
+  \hline
+  val&Data Segment本体\\
+  \hline
+  quickFlag&SEDAを挟まずCommandを処理を行うかを示す\\
+  \hline
+  serialized&Data Segment本体のシリアライズ状態を示す\\
+  \hline
+\end{tabular}
+\end{center}
+\end{table}
+
+このパケット構造に問題が存在する。データ本体はCommandMessageがシリアライズ化されるときにはすでに、シリアライズされている。つまり、このままCommandMessageをシリアライズ化を行うと、データ本体をもう1度シリアライズ化を行ってしまう。配列をシリアライズ化する場合、配列のサイズによってはオーバーヘッドが大きいため、2度シリアライズするのを防がなければならない。
+
+\begin{table}[html]
+\lstinputlisting[label=src:CommandMessage, caption=変更後のCommandMessage]{source/CommandMessage.java}
+\end{table}
+
+そこで、CommandMessageをソースコード\ref{src:CommandMessage}のように変更した。Data Segment本体をCommandMessageのフィールドから外し、後からByteBufferにまとめることにより2度のシリアライズを防ぐ。(ソースコード\ref{src:convert})
+
+\begin{table}[html]
+\lstinputlisting[label=src:convert, caption=ByteBuffer作成部分]{source/CreateByteBuffer.java}
+\end{table}
+
+この実装ではCommandMessage部をヘッダーとして扱っている。データ部はCommandTypeが{\tt UPDATE、PUT、REPLY}の時のみ後から付加される。以前の実装ではbyte[]の値としてnullを示すNilValueがあるものとしてシリアライズ化されており、これもオーバーヘッドである。現在の実装にでは、CommandTypeが{\tt UPDATE、PUT、REPLY}以外はの時は、データ部をシリアライズ化しないため、nullをシリアライズ化するオーバーヘッドはなくなっている。
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/paper/source/CommandMessage.java	Thu Jan 08 20:36:01 2015 +0900
@@ -0,0 +1,9 @@
+@Message
+public class CommandMessage {
+    public int type;
+    public int seq;
+    public String key;
+    public boolean quickFlag = false;
+    public boolean serialized = false;
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/paper/source/CommandMessageBefore.java	Thu Jan 08 20:36:01 2015 +0900
@@ -0,0 +1,9 @@
+@Message
+public class CommandMessage {
+    public int type;
+    public int seq;
+    public String key;
+    public byte[] val;
+    public boolean quickFlag;
+    public boolean serialized;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/paper/source/CreateByteBuffer.java	Thu Jan 08 20:36:01 2015 +0900
@@ -0,0 +1,36 @@
+public ByteBuffer convert() {
+    ByteBuffer buf = null;
+    MessagePack msg = SingletonMessage.getInstance();
+    try {
+	byte[] header = null;
+	byte[] data = null;
+	byte[] dataSize = null;
+	boolean serialized = false;
+
+	switch (type) {
+	case UPDATE:
+	case PUT:
+	case REPLY:
+	    data = msg.write(rData.getObj());
+	    CommandMessage cm = new CommandMessage(type.id, seq, key, false, serialized);
+
+	    header = msg.write(cm);
+	    dataSize = msg.write(data.length);
+	    buf = ByteBuffer.allocate(header.length+dataSize.length+data.length);
+	    buf.put(header);
+	    buf.put(dataSize);
+	    buf.put(data);
+	    break;
+	default:
+	    header = msg.write(new CommandMessage(type.id, seq, key, quickFlag, false);
+	    buf = ByteBuffer.allocate(header.length);
+	    buf.put(header);
+	    break;
+	}
+
+	buf.flip();
+    } catch (IOException e) {
+	e.printStackTrace();
+    }
+    return buf;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/paper/source/ReceiveData.java	Thu Jan 08 20:36:01 2015 +0900
@@ -0,0 +1,6 @@
+public class ReceiveData {
+    private Object val;
+    
+    private boolean serialized = false;
+    private boolean byteArray = false;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/paper/source/asClass.java	Thu Jan 08 20:36:01 2015 +0900
@@ -0,0 +1,13 @@
+public <T> T asClass(Class<T> clazz) {
+    if (!byteArray) {
+        return (T) val;
+    }
+    byte[] b = (byte[]) val;
+
+    if (serialized) {
+        return SingletonMessage.getInstance().read(b, clazz);
+    } else {
+        return (T) b;
+    }
+
+}