前田です。

At Wed, 28 May 2003 09:48:31 +0900,
"NAKAMURA, Hiroshi" <nakahiro / sarion.co.jp> wrote:
> 構造体を1000個やりとり!
> 
> オブジェクトがでかいと、
>   object <-A-> SOAP Data Model <-B-> XML instance
>   Integer      SOAP::SOAPInt         <foo xsi:type="xsd:int"/>
> の、Aの部分も重そうですね。

はい、そうですね。

      <complexType name="PostalData">
        <all>
          <element name="jisCode" type="string" />
          <element name="postCodeShort" type="string" />
          <element name="postCode" type="string" />
          <element name="prefKanji" type="string" />
          <element name="cityKanji" type="string" />
          <element name="townKanji" type="string" />
          <element name="prefKana" type="string" />
          <element name="cityKana" type="string" />
          <element name="townKana" type="string" />
          <element name="flag1" type="int" />
          <element name="flag2" type="int" />
          <element name="flag3" type="int" />
          <element name="flag4" type="int" />
          <element name="flag5" type="int" />
          <element name="flag6" type="int" />
        </all>
      </complexType>

こんな感じの構造体300個の送信を計測してみると、

Ruby 1.6:
RPCRouter#createResponse	2.434415
Processor.marshal		21.544701

Ruby 1.8:
RPCRouter#createResponse	2.345288
Processor.marshal		2.905458

といった感じでした。

Ruby 1.6の場合、データ数が多くなると、String#+でやるよりString#<<
の方が遅くなってしまうようです。
たぶん、大きなメモリを小刻みに何度もallocateするために遅くなってし
まうのではないかと推測しています。

createResponseを

    def createResponse( namespace, methodName, result )
      name = fqName( namespace, methodName )
      if ( @method.has_key?( name ))
	method = @method[ name ]
      else
	raise RPCRoutingError.new( "Method: #{ name } not defined." )
      end

      soapResponse = method.createMethodResponse
      soapReturnValue = SOAPReturnValue.new(result) # 自前のwrapper
      soapResponse.setRetVal(soapReturnValue)
      return soapResponse
    end

のように再定義して、resultを一回ラップしてやるだけにしてから、

class String
  def soap_attr
    return ' xsi:type="xsd:string"'
  end

  def soap_content
    return to_s
  end
end

class Integer
  def soap_attr
    return ' xsi:type="xsd:int"'
  end

  def soap_content
    return to_s
  end
end

class PostalData
  def soap_attr
    return ' xsi:type="txsd:PostalData"'
  end

  def soap_content
    return <<EOF
<jisCode xsi:type="xsd:string">#{@jisCode.soap_content}</jisCode>
<postCodeShort xsi:type="xsd:string">#{@postCodeShort.soap_content}</postCodeShort>
<postCode xsi:type="xsd:string">#{@postCode.soap_content}</postCode>
<prefKanji xsi:type="xsd:string">#{@prefKanji.soap_content}</prefKanji>
<cityKanji xsi:type="xsd:string">#{@cityKanji.soap_content}</cityKanji>
<townKanji xsi:type="xsd:string">#{@townKanji.soap_content}</townKanji>
<prefKana xsi:type="xsd:string">#{@prefKana.soap_content}</prefKana>
<cityKana xsi:type="xsd:string">#{@cityKana.soap_content}</cityKana>
<townKana xsi:type="xsd:string">#{@townKana.soap_content}</townKana>
<flag1 xsi:type="xsd:int">#{@flag1.soap_content}</flag1>
<flag2 xsi:type="xsd:int">#{@flag2.soap_content}</flag2>
<flag3 xsi:type="xsd:int">#{@flag3.soap_content}</flag3>
<flag4 xsi:type="xsd:int">#{@flag4.soap_content}</flag4>
<flag5 xsi:type="xsd:int">#{@flag5.soap_content}</flag5>
<flag6 xsi:type="xsd:int">#{@flag6.soap_content}</flag6>
EOF
  end
end

のような要領でXMLを生成するようにすると、

Ruby 1.6:
RPCRouter#createResponse	0.000607
Processor.marshal		0.129808

Ruby 1.8:
RPCRouter#createResponse	0.000581
Processor.marshal		0.092752

と、劇的に早くなりました:)

YAMLで書いたデータ定義からWSDLとテストケースを生成するようにして
いるので、上記のようなコードも自動生成するようにしようと思ってい
ます。
# 最初はWSDLを手で書いてたのですが、破綻しました:(

> Stringの連結によるXML instance生成が遅いのであれば、
> IOに直接書くというのはどうですかね? 設定だけで、というわけには
> いかないような気がしますが、なんかちょっと書いてみます。
> 別に使っていただかなくても、単になひの趣味で作るので
> 気にしないでください。

おお、いいですね:)

> ちなみにIOに書くときの問題は、途中で送るべきデータにエラーを発見
> しても、既に送ってしまっていて手遅れなこと。dRubyも同じ問題を
> 抱えていた気がする(速度のためには直接IOに書きたいけど、
> 上記の問題で一度Stringにせざるを得ない)。

mod_rubyも同じ問題をかかえているのですが、結局設定で変えられるよ
うにしてユーザに選択してもらうことにしました。

> > # 単純なデータのやり取りだけなので、SOAP4Rみたいにまじめにやらず
> > # に、ほとんど決め打ちですませるつもりです(^_^;
> 
> それで済むのがSOAPやXML-RPCのお気楽なところですね。
> Java搭載携帯端末でやってます。

はい、いじるのがラクでいいですね。
今回はクライアントがWindowsなのでSOAPを選択した(クライアント側は
お客さんが作るので、なるべく労力が少なくてすむようにする必要があ
るのです)のですが、WSDL4Rのおかげでコードを自動生成できるので重宝
しています:-)

ほんとはRubyのソースからWSDLを自動生成したいところですが、コメン
トに型情報を埋め込むとかしない無理ですよねえ。

-- 
前田 修吾