snuffkinの遊び場

IT関係、スポーツ、数学等に関することを、気が向いたときに書いてます。

MessagePack vs POF(Coherence)でシリアライズサイズの勝負!

前回はJBoss MarshallingとMessagePackのシリアライズサイズを比較してみましたが、今回はCoherenceで使用しているPortable Object Format(POF)も比較してみましょう。
Oracleのページから評価版のCoherenceをダウンロードすれば良かったのかもしれませんが、手っ取り早くWebから情報を探してきました。「CoherenceのPOF形式でシリアライズされたオブジェクトのサイズ見積もりツール」(山本大@クロノスの日記)にPOFのシリアライズサイズについて載っています(こういう情報、ありがとうございます)。このページでは「Person.java」に「ExampleMain.java」でサンプルデータを入れてサイズ測定をしています。同じクラス、データでMessagePackでシリアライズして比較してみました。

フレームワークの紹介

Coherenceで使われているシリアライズの仕組み。Javaシリアライズでは効率が悪いため、独自に開発されました。言語に依存しないシリアライズの仕組みです。

その他についての紹介は、前回を参照してください。

シリアライズ結果

早速ですが、シリアライズ結果です。今回はJava標準のシリアライズも計測してみました。

Java標準

171byteのサイズにシリアライズされました。山本大さんのページに書いてある結果とも一致しています。

JBoss Marshallingと同じくクラス名の情報がシリアライズされたデータに入っているため、大きなデータになっています。

JBoss Marshalling

141byteのサイズにシリアライズされました。

Java標準より82%のサイズになっていますが、大きく変わったようには見えませんね。java.langパッケージの場合は、クラス名を短く表記できるようになってそうですね。

さて、ここからが本題です。

Portable Object Format

山本大さんのページの調査結果によると29byteのサイズにシリアライズされるそうです。手元にCoherenceがないので、すみませんがキャプチャはありません。Java標準の17%、JBoss Marshallingの21%のサイズになっています。

MessagePack

24byteのサイズにシリアライズされました。

おおっ、さすが。接戦ですが、POFに5byte勝ちました。Java標準の14%、JBoss Marshallingの17%、POFの83%のサイズになっています。MessagePackとPOFは、Java標準やJBoss Marshallingと比べると随分小さくシリアライズできるんですね。

という訳で、今回もMessagePackの勝ち!サイズ勝負だと、POFもなかなかでしたが、MessagePackが強いですね。
この結果は、シリアライザの総合力を比較しているのではなく、特定のデータをいかに小さなサイズにシリアライズできるかのみの観点で見ています。あくまでも、ひとつの側面としてご参考ください。

JBoss Marshalling vs MessagePackでシリアライズサイズの勝負!

ちょっと興味があり、シリアライズフレームワークについて調べてみました。Java標準のシリアライズはサイズが大きくなってしまうため、いろんな人が工夫して小さくシリアライズする仕組みを考えています。そんな中で、今回は(今回しかやらないかもしれませんが)、JBoss MarshallingとMessagePackでどちらが小さなサイズにシリアライズできるのか測ってみました。

フレームワークの紹介

JBossの周辺で使われているシリアライズの仕組み。Javaシリアライズでは効率が悪いため、独自に開発されました。

言語に依存しないシリアライズの仕組み。Format specificationを読むと、シリアライズの小さくするための工夫が徹底している感じがします。

シリアライズ対象のデータ

次のような、どこにでもありそうなクラスを作ってみました。

package jp.gr.java_conf.snuffkin.sandbox.infinispan;

import java.io.Serializable;

import org.msgpack.annotation.Message;

@Message
public class Tweet implements Serializable{
    private static final long serialVersionUID = -7144860256641395394L;
    private String userid;
    private long timestamp;
    private String message;
    
    public String getUserid() {
        return userid;
    }
    public void setUserid(String userid) {
        this.userid = userid;
    }
    public long getTimestamp() {
        return timestamp;
    }
    public void setTimestamp(long timestamp) {
        this.timestamp = timestamp;
    }
    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }
    @Override
    public String toString() {
        return "Tweet [userid=" + userid + ", timestamp=" + timestamp
                + ", message=" + message + "]";
    }
    
}

コメントは書いていませんが、簡単なクラスなのでご勘弁ください。要は、以下のようなプロパティを持つクラスです。

String userid
long timestamp
String message

各プロパティには、以下のような値を設定します。

userid="snuffkin"
timestamp=現在時刻をミリ秒単位で表現した値
message=140文字のASCII文字列

シリアライズの結果

JBoss Marshalling、MessagePackをシリアライズし、ネットワークに流した結果をWiresharkで解析してみました。その結果は、次の通りです。

JBoss Marshalling

269byteのサイズにシリアライズされました。

クラス名やプロパティ名がシリアライズされたデータに入っているので、パッケージ名が長かったりするとデータが大きくなりますね。固い仕組みなのかもしれませんが、サイズだけ見るとロスがあります。手元に仕様書がないのですが、「3e」でプロパティを区切って、次にデータ長・データと並んでいるように見えます。

MessagePack

162byteのサイズにシリアライズされました。

うおっ。見るからにこちらの方が小さいです。MessagePackはサイズが小さなデータについては、型とデータ長を合わせて1byteに収めるようになっているせいか、見るからに隙間が少ないですね。また、プロパティは定義した順に配列として表現されており、プロパティ名の情報は失われています。

という訳で、結果はMessagePackの勝ち! 約40%もの差をつけての圧勝ですね。シリアライズに特性があったり(この仕組みだと、小さなデータほどサイズ比が大きくなりますね)、実際に使用するときにはサイズだけでなくシリアライズ速度も重要だったり、観点はいろいろあるべきだと思うのですが、ここでは、そのあたりは考慮に入れていません。サイズを比較したのみですので、あくまでも、ひとつの側面としてご参考ください。

イベントドリブンなネットワークプログラミングができるフレームワークNetty

このところ、JBoss関係のプロダクトに触ることが多いのですが、その中で特に気に入ったプロダクトがNettyです(NettyはJBossから独立していますが)。優れたプロダクトだと思うのですが、それほどドキュメントもないため、Nettyについて使ったり調べたことをまとめて行こうと思います。

Nettyとは?

Nettyは、イベントドリブンな非同期通信を行うアプリケーションを開発するためのフレームワークです。ソケット周りを直接触る処理はNettyが行ってくれて、イベントドリブンで接続・切断時の処理を記述したり、電文受信時の処理を記述することができ、簡潔で見通しの良いネットワークプログラミングができます。また、Nettyには電文処理を行うアプリケーションを開発するときに必要なライブラリが用意されているため、オリジナルプロトコルを使う場合にもNettyを使えば開発しやすいです。
Netty: Home」がプロジェクトのサイトです。解説ドキュメントはあまり豊富ではありませんが、Javadocは充実しています。今のところ、Nettyを知る一番のドキュメントはJavadocではないかと思います。また、stackoverflow.comのNettyページでは、活発にQ&Aがやりとりされているため、こちらも参考になると思います。
ライセンスはApache License 2.0です。また、NettyはJavaで書かれています。

私がNettyを使いたくなった動機

RPCやらHTTPやら、異なる言語のプロセスと簡単に通信できる仕組みがいろいろと考えられてきたため、自力でネットワークプログラミングする機会は随分減りました。ですが、既存システムに合わせることが必須のプロジェクトもあり、TCP上のオリジナルプロトコルも健在です。「MessagePack-RPCとか使おうよ!」と言いたいところですが、どうにもなりません。

ネットワークプログラミングは、結構繊細なお決まりの処理をしなきゃいけないため、正直面倒です。JDBC周りでお決まりのclose処理を書くより遥かに面倒です。私はアプリケーションロジックを開発したいのに!
これが一体どれだけ面倒なのか、ちょっと見てみましょう。

まずは、NIO以前からある、昔ながらのSocketクラスを使ったプログラミング。エラー処理とか考えると、「Javaネットワークプログラミング講座」にあるサンプルのような処理が必要です(コードへのリンクがあるので、SocketConnector.java、MessageReciever.java、MessageSender.javaあたりを参照してください)。考えないといけないことが多く、結構大変です。

NIOの導入で便利になったものの、サーバ側だけでも「Java In The Box」にあるサンプルのような処理を書く必要があります。アプリケーションロジックを開発したいのに、依然として、このあたりの処理は面倒です。

とまあ、自分で書くのが面倒なので、他のサイトを参照させて頂きました。さて、ここまで読んだ人で、これらのサイトのコードをしっかり追った方はどのくらいいるでしょうか。正直、呪文のようなコードに挫折した人も多いのでは。サイトのコードが悪いのではなく、面倒な処理が必要なAPIになっているのです。
こういう実装は嫌だったので、いろいろ探してたどり着いたのがNettyでした。Nettyを使うと、ネットワークプログラミングをもっと見通し良く記述することができます。

Nettyを使った具体例

それでは、サンプルとしてクライアントから"Hello, World!"という文字列を受信してそのまま返すエコーサーバを紹介します。
「データの先頭に電文長があり、その後に実際の電文内容が続く」という電文プロトコルに、文字列を詰めて送信することにします。最初の4byteに電文長を格納し、残りは電文内容(文字列をbyteにしたもの)だとしましょう。

まずはサーバ側です。EchoServerがメインクラスです。具体的に受信した電文を処理しているアプリケーションロジックがEchoServerHandlerです。細かな説明は今回は省略し、別の機会に書きます。

  • EchoServer
package jp.gr.java_conf.snuffkin.sandbox.netty.echo;

import java.net.InetSocketAddress;
import java.util.concurrent.Executors;

import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder;
import org.jboss.netty.handler.codec.frame.LengthFieldPrepender;
import org.jboss.netty.handler.codec.string.StringDecoder;
import org.jboss.netty.handler.codec.string.StringEncoder;

/**
 * サーバ側メインクラス
 */
public class EchoServer {

    public static void main(String[] args) {
        ChannelFactory factory = 
            new NioServerSocketChannelFactory( // server
                    Executors.newCachedThreadPool(),
                    Executors.newCachedThreadPool()
                    );
        
        ServerBootstrap bootstrap = new ServerBootstrap(factory);
        bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
            public ChannelPipeline getPipeline() {
                ChannelPipeline pipeline = Channels.pipeline();
                // Downstream(送信)
                pipeline.addLast("frameEncoder", new LengthFieldPrepender(4));
                pipeline.addLast("stringEncoder", new StringEncoder());
                // Upstream(受信)
                pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(8192, 0, 4, 0, 4));
                pipeline.addLast("stringDecoder", new StringDecoder());
                // Application Logic Handler
                pipeline.addLast("handler", new EchoServerHandler()); // server
                
                return pipeline;
            }
        });
        
        bootstrap.bind(new InetSocketAddress(9999)); // 9999番ポートでlisten
    }
}
  • EchoServerHandler
package jp.gr.java_conf.snuffkin.sandbox.netty.echo;

import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;

/**
 * サーバ側アプリケーションロジック
 */
public class EchoServerHandler extends SimpleChannelHandler {
    /**
     * クライアントから電文を受信した際に呼び出されるメソッド
     */
    @Override
    public void messageReceived(ChannelHandlerContext ctx, MessageEvent event) {
        String msg = (String) event.getMessage(); // 受信電文を取りだす
        ctx.getChannel().write(msg); // クライアントに送信
    }
}

次にクライアント側です。EchoClientがメインクラスです。アプリケーションロジックを実装しているのが、EchoClientHandlerです。こちらも、細かな説明は今回は省略し、別の機会に書きます。

  • EchoClient
package jp.gr.java_conf.snuffkin.sandbox.netty.echo;

import java.net.InetSocketAddress;
import java.util.concurrent.Executors;

import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder;
import org.jboss.netty.handler.codec.frame.LengthFieldPrepender;
import org.jboss.netty.handler.codec.string.StringDecoder;
import org.jboss.netty.handler.codec.string.StringEncoder;

/**
 * クライアント側メインクラス
 */
public class EchoClient {

    public static void main(String[] args) {
        ChannelFactory factory =
            new NioClientSocketChannelFactory( // client
                    Executors.newCachedThreadPool(),
                    Executors.newCachedThreadPool()
                    );
        
        ClientBootstrap bootstrap = new ClientBootstrap(factory);
        bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
            public ChannelPipeline getPipeline() {
                ChannelPipeline pipeline = Channels.pipeline();
                // Downstream(送信)
                pipeline.addLast("frameEncoder", new LengthFieldPrepender(4));
                pipeline.addLast("stringEncoder", new StringEncoder());
                // Upstream(受信)
                pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(8192, 0, 4, 0, 4));
                pipeline.addLast("stringDecoder", new StringDecoder());
                // Application Logic Handler
                pipeline.addLast("handler", new EchoClientHandler()); // client

                return pipeline;
            }
        });
        
        ChannelFuture future = bootstrap.connect(new InetSocketAddress("localhost", 9999)); // 9999番ポートにconnect
        future.getChannel().getCloseFuture().awaitUninterruptibly();
        bootstrap.releaseExternalResources();
    }
}
  • EchoClientHandler
package jp.gr.java_conf.snuffkin.sandbox.netty.echo;

import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;

/**
 * クライアント側アプリケーションロジック
 */
public class EchoClientHandler extends SimpleChannelHandler {
    /**
     * サーバに接続した際に呼び出されるメソッド
     */
    @Override
    public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent event) {
        ctx.getChannel().write("Hello, World!");
    }
    
    /**
     * サーバから電文を受信した際に呼び出されるメソッド
     */
    @Override
    public void messageReceived(ChannelHandlerContext ctx, MessageEvent event) {
        String msg = (String) event.getMessage();
        System.out.println(msg);
    }
}

動作の流れを解説すると、以下の通りです。

  1. EchoServerを起動してください。9999番ポートでクライアントからの接続を待ちます。
  2. サーバが起動したら、EchoClientを起動してください。EchoClientは9999番ポートに接続します。
  3. クライアントがサーバに接続すると、EchoClientHandler#channelConnectedがNettyから呼び出されます。この中で、サーバに対して文字列"Hello, World!"を送信します。
  4. サーバがクライアントからの電文を受信すると、EchoServerHandler#messageReceivedがNettyから呼び出されます。クライアントから受信した文字列を、クライアントに送信します。
  5. クライアントがサーバからの電文を受信すると、EchoClientHandler#messageReceivedがNettyから呼び出されます。サーバから受信した文字列を標準出力します。
Hello, World!

import文を除いたとしても、よくあるRPCのサンプルと比べるとコード量は多いです。ですが、この例はオリジナルプロトコルを実装したものであり、RPCライブラリに乗っかって通信したものより、多くの処理を自分で作ったことになります。なので、単純には比較できないですね。

アプリケーションロジックを書いているEchoServerHandlerやEchoClientHandlerを見てください。このくらい簡単にオリジナルプロトコルで通信することができます。このサンプルではエラー処理を省略したところがありますが、ネットワークプログラミングをイベントドリブンで記述できるようになると、こんなにも見通しが良くなります。Nettyを使わないサンプルと違って、コードを読む気になりますよね?

この例を振り返ってみると、byteの変数がまったく登場しません。このくらい簡単な例なら、byteの変数すら使わずに書けるのが、ネットワークアプリケーションを開発するためのフレームワークであるNettyの良さだと思います。

さて、Nettyの印象はどうだったでしょうか。Nettyの気持ち良さの一端が伝われば幸いです。今回書いたコードの具体的な解説は、また今度書きたいと思います。

AKBのぐぐたすリスト

数日前、Google+ ランキング G+Naviで調べたところ、Google+のフォロワー数トップ100にAKB関係者が79人も入っていました。物凄いことになってますね。日本のぐぐたすだけ、他の国とは別の発展の仕方をするかもしれませんね。
あまりに凄いので、Google+で公開されているメンバの一覧を作ってみました(データはGoogle+にあるこのページを元にしています)。https://plus.google.com/の後ろにGoogle+のidをつければ、そのidの人のpostが見れます。
こういうのがあれば、Google+ APIを使っていろいろ遊べそうですね。(一応断っておくと、私の興味の主眼はソーシャルデータです。芸能ネタも好きではありますが)

名前 ふりがな 所属グループ 所属チーム Google+のid
秋元才加 あきもと さやか AKB K 105020230123170728064
阿比留李帆 あびる りほ SKE K2 102764447672444416615
石田晴香 いしだ はるか AKB B 101026469701528255144
板野友美 いたの ともみ AKB K 108406705498777962659
今出舞 いまで まい SKE SKE48研修生 106656898590733509216
内田眞由美 うちだ まゆみ AKB K 105842454176256413407
梅田彩佳 うめだ あやか AKB K 112077362806147944184
梅本まどか うめもと まどか SKE E 118425778047788093453
大島優子 おおしま ゆうこ AKB K 105229500895781124316
大場美奈 おおば みな AKB 4 110216234612751595989
大家志津香 おおや しづか AKB A 108367535733172853340
大矢真那 おおや まさな SKE S 116324240483798147615
小木曽汐莉 おぎそ しおり SKE K2 107135851528812577523
小野晴香 おの はるか SKE S 111145641865855965824
河西智美 かさい ともみ AKB B 110230842586402039931
柏木由紀 かしわぎ ゆき AKB B 109547251260290757268
片山陽加 かたやま はるか AKB A 108485060451296256117
加藤智子 かとう ともこ SKE K2 108340808318911923001
菊地あやか きくち あやか AKB K 102861211420357915358
北原里英 きたはら りえ AKB B 114392080665434978357
鬼頭桃菜 きとう ももな SKE SKE48研修生 102982862918484502371
木下有希子 きのした ゆきこ SKE S 117533056464814306425
倉持明日香 くらもち あすか AKB A 103787924613525409143
桑原みずき くわばら みずき SKE S 100200160956657706975
小嶋陽菜 こじま はるな AKB A 114739367195523292316
小林亜実 こばやし あみ SKE E 103904242672756284899
小林香菜 こばやし かな AKB B 111171583195412578810
指原莉乃 さしはら りの AKB A 104375100134443203420
佐藤亜美菜 さとう あみな AKB B 108623251197634437138
佐藤すみれ さとう すみれ AKB B 112309691086068535265
佐藤聖 さとう せいら SKE K2 112980138521977900424
佐藤夏希 さとう なつき AKB B 102181587900817571440
佐藤実絵子 さとう みえこ SKE K2 114397630658650917209
篠田麻里子 しのだ まりこ AKB A 105312608513060875034
柴田阿弥 しばた あや SKE E 113499890803474188597
島田晴香 しまだ はるか AKB 4 102844391735210836268
島田玲奈 しまだ れな NMB NMB48研修生 106176150979443047095
鈴木まりや すずき まりや AKB B 109273326082502819549
須田亜香里 すだ あかり SKE S 115975634910643785199
高木由麻奈 たかぎ ゆまな SKE E 100806834975375116663
高城亜樹 たかじょう あき AKB A 107954758685270685108
高田志織 たかだ しおり SKE S 112166094088653039891
高野祐衣 たかの ゆい NMB NMB48研修生 114488817218493827050
高橋みなみ たかはし みなみ AKB A 116816132092699657436
高柳明音 たかやなぎ あかね SKE K2 106926723626971174827
竹内舞 たけうち まい SKE E 111253632744203791499
田名部生来 たなべ みく AKB K 115449512138863581856
近野莉菜 ちかの りな AKB B 103259286719163415846
出口陽 でぐち あき SKE S 101713591556895969107
仲川遥香 なかがわ はるか AKB A 102249965218267255722
中田ちさと なかた ちさと AKB A 106819068054122298292
中塚智実 なかつか ともみ AKB K 106902079329146402543
中西優香 なかにし ゆうか SKE S 107061289389523750061
仲俣汐里 なかまた しおり AKB 4 115842256718368316968
中村麻里子 なかむら まりこ AKB 4 100757137373116382078
仲谷明香 なかや さやか AKB A 117151902078571783647
仁藤萌乃 にとう もえの AKB K 106541618903520668641
野中美郷 のなか みさと AKB K 101031357714890657558
秦佐和子 はた さわこ SKE K2 108037953963364967101
平嶋夏海 ひらじま なつみ AKB B 117480374878682152542
平田璃香子 ひらた りかこ SKE S 100593468647281401358
平松可奈子 ひらまつ かなこ SKE S 111254737005235237589
福本愛菜 ふくもと あいな NMB N 101854092637878212405
古川愛李 ふるかわ あいり SKE K2 111050677534976069347
前田敦子 まえだ あつこ AKB A 116582420246242769167
増田有華 ますだ ゆか AKB B 116563387779051485156
松井咲子 まつい さきこ AKB K 111700319553120354024
松井玲奈 まつい れな SKE S 108536765591006886419
松原夏海 まつばら なつみ AKB A 110106903212386059477
松村香織 まつむら かおり SKE SKE48研修生 108705263081706477178
松本梨奈 まつもと りな SKE K2 111792731544936626652
峯岸みなみ みねぎし みなみ AKB K 113175170244847276511
宮崎美穂 みやざき みほ AKB B 105599945949824406376
宮澤佐江 みやざわ さえ AKB K 111186383073996701022
村上文香 むらかみ あやか NMB NMB48研修生 117655629637021320261
矢方美紀 やかた みき SKE K2 102565671074822095382
山口夕輝 やまぐち ゆうき NMB N 103663030781390958288
山田菜々 やまだ なな NMB N 102277090985412703374
山本彩 やまもと さやか NMB N 101423472932208115437
横山由依 よこやま ゆい AKB K 109380179669644031316
米沢瑠美 よねざわ るみ AKB K 110841113220926588945
渡辺美優紀 わたなべ みゆき NMB N 109544372058574620997

ACLとJ2を同時に戦う夢のようなことは起きず

男子は史上初のJ2対決の決勝。今年の天皇杯は本当に番狂わせでしたね。決勝のJ2対決だけでなく、JFL松本山雅がJ2横浜FC・J1アルビレックス新潟を撃破したのもビックリ。なぜか大きく報道されていないと思いますが、一番の驚きはJ1の大宮アルディージャが大学生に負けたこと。一体どうなっているんでしょう。J1昇格した年に柏レイソルが優勝したし、何かおかしなことになっていませんか。
で、一番期待したのは京都サンガが優勝して、ACLとJ2を同時に戦う夢のようなことが起きること。なので、京都サンガを応援しましたが、さすがにFC東京はJ2チャンピオンでしたね。京都が先制するも、今野のゴールですぐに追いつく(FC東京のファンには感慨深いゴールでしょうね)と、森重のフリーキックで勝ち越し点。このフリーキック、斜め横方向から見ていたのですが、キーパーが若干変な動きして、ゴールのそれほど隅でもないところに決まりました。「なんとなく伸びた?」とは思いましたが、あまり気にしていませんでした。その後、VTRで分かったのですが、実はコレ、無回転シュートだったんですね。初めて生で見ました。横から見ていると良く分かりませんね。。。
あとは、ルーカスや石川が上手すぎて、歯が立たない感じでした。このサッカーができれば、J1に入っても上位に行けるはず。スコアは4-2でしたが、京都もひどかった訳ではなく、ちゃんとしていました。今日のようなレベルを年間通してキープできれば、J1で戦えるチームだと思いました。偶然決勝に来たわけではなく、両チームとも調子が良かったから決勝に来たのだろうと思います。