S2Dao.NETのチューニング?

自分の仕事で使えないかとS2Dao.NETを調査中。

using System;
using System.Collections;
using Seasar.Dao.Attrs;

namespace My.Dao
{
    [Bean(typeof(MYTABLE))]
    public interface IMYTABLEDao
    {
        IList GetAllList();
    }
}

S2Dao.NETはこんなインタフェースを用意するだけでAOPで実装を動的に用意してくれる優れもの。前々から使いたいと思っていたのですが、パフォーマンステストをしてみたところ、ちょっと私の仕事には厳しそうでした。
テスト環境はOracle10g。対象となるテーブルはカラム数が100、レコード数が600程度です。このテーブルを全件検索してみたところ、9秒掛かりました。TableAdapterを使った場合は1秒未満なので、この差はツライ。

ちょっと気になってソースコードを追ってみたところ、DataReaderの内容をオブジェクトに設定する処理が重そうでした。ループ内でやっている処理を一部、ループ外に出せそうだったので、ちょっと修正してみることに。

public class ColumnAccessPath
{
    string columnName;
    public string ColumnName
    {
        get
        {
            return columnName;
        }
        set
        {
            columnName = value;
        }
    }

    PropertyInfo propertyInfo;
    public PropertyInfo PropertyInfo
    {
        get
        {
            return propertyInfo;
        }
        set
        {
            propertyInfo = value;
        }
    }

    IValueType valueType;
    public IValueType ValueType
    {
        get { return valueType;  }
        set { valueType = value; }
    }
}

こんなクラスを作ってDataReaderやオブジェクトのプロパティへのアクセス方法をキャッシュしてみることにします。

protected ColumnAccessPath[] CreateColumnAccessPath(IList columnNames)
{
    System.Collections.Generic.Dictionary<string, object> names = null;
    System.Collections.Generic.List<ColumnAccessPath> pathList = new System.Collections.Generic.List<ColumnAccessPath>();

    for (int i = 0; i < beanMetaData.PropertyTypeSize; ++i)
    {
        ColumnAccessPath path = new ColumnAccessPath();
        IPropertyType pt = beanMetaData.GetPropertyType(i);
        if (columnNames.Contains(pt.ColumnName))
        {
            path.ColumnName = pt.ColumnName;
            path.PropertyInfo = pt.PropertyInfo;
            path.ValueType = pt.ValueType;
            pathList.Add(path);
        }
        else if (columnNames.Contains(pt.PropertyName))
        {
            path.ColumnName = pt.PropertyName;
            path.PropertyInfo = pt.PropertyInfo;
            path.ValueType = pt.ValueType;
            pathList.Add(path);
        }
        else if (!pt.IsPersistent)
        {
            if (names == null)
            {
                names = new System.Collections.Generic.Dictionary<string, object>();
                foreach (string name in columnNames)
                    names[name.Replace("_", String.Empty).ToUpper()] = null;
            }

            if (names.ContainsKey(pt.ColumnName.ToUpper()))
            {
                path.ColumnName = pt.ColumnName;
                path.PropertyInfo = pt.PropertyInfo;
                path.ValueType = pt.ValueType;
                pathList.Add(path);
            }
        }
    }
    return pathList.ToArray();
}

こんな感じでループの外でPropertyInfo等を保存。

        protected void Handle(IDataReader dataReader, IList list)
        {
            IList columnNames = CreateColumnNames(dataReader.GetSchemaTable());
            // プロパティへのアクセス方法をキャッシュ
            ColumnAccessPath[] pathList = CreateColumnAccessPath(columnNames);
            
            int relSize = BeanMetaData.RelationPropertyTypeSize;
            RelationRowCache relRowCache = new RelationRowCache(relSize);
            while (dataReader.Read())
            {
                // object row = CreateRow(dataReader, columnNames);
                object row = CreateRow(dataReader, pathList);

呼び出し元を修正。

結果は、

修正前
0:09.4687500

修正後
00:00:00.2812500

カラム数分余計にメモリを食いますが、その分の効果はありそうです。