# HG changeset patch # User sugi # Date 1420716961 -32400 # Node ID 7e1112025b3abaccda076a242bd4d03e8f4921da # Parent f948d683c29a9432d7243a09881c8ca2cd6b3c86 modify chapter4 diff -r f948d683c29a -r 7e1112025b3a paper/chapter4.tex --- 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 asClass(Class 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 diff -r f948d683c29a -r 7e1112025b3a paper/source/CommandMessage.java --- /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; + +} diff -r f948d683c29a -r 7e1112025b3a paper/source/CommandMessageBefore.java --- /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 diff -r f948d683c29a -r 7e1112025b3a paper/source/CreateByteBuffer.java --- /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 diff -r f948d683c29a -r 7e1112025b3a paper/source/ReceiveData.java --- /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 diff -r f948d683c29a -r 7e1112025b3a paper/source/asClass.java --- /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 asClass(Class clazz) { + if (!byteArray) { + return (T) val; + } + byte[] b = (byte[]) val; + + if (serialized) { + return SingletonMessage.getInstance().read(b, clazz); + } else { + return (T) b; + } + +}