谷本 心 in せろ部屋

はてなダイアリーから引っ越してきました

Excel読み込み処理のパフォーマンス比較。

チョーざっくりと、POI、JExcelAPI、XPathStAX
Excelを読み込んだらどうなるかを計測してみた。


全くチューニングしてないし、
いい加減な測定なので、結果は参考程度に。

  • 環境
    • CoreDuo T2300 (1.66GHz)
    • メモリ2GB
    • JRE 1.6.0_04
    • Eclipse 3.3上で計測
    • OfficeXP(2002) 古くてゴメン。
  • 方法
    • 5 x 3ぐらいの小さなExcelファイルを作成し、これを読み込む。
    • 同じファイルをxml形式(XMLSpreadSheet)で保存したものも作る。
    • POIとJExcepAPIはxlsファイルを読み、XPathStAXではxmlファイルを読む。
    • 1回、3回、10回、100回読み込んだ時で計測。
    • ソースは恥ずかしいので非公開。
    • 読み込み完了のたびにファイルクローズぐらいはやってる。


結果

ツール 1回(ms) 3回(ms) 10回(ms) 100回(ms)
POI 250 281 297 484
JExcelAPI 172 297 688 6125
JExcelAPI (GCなし) 0 or 16 78 312 3438
XPath 109 172 391 2203
StAX 0 or 16 15 31 141

(2008/2/29 JExcelAPIにsetGCDisabled(true)の場合の結果を追加)

POIは内部キャッシュでも効いているのか、
繰り返し読み込みした時に、性能が落ちないのが良い。


JExcelAPIは逆にキャッシュが効いてなさそうで、
1回読み込みならPOIより早いけど、あとはシーケンシャルに増えていく。
特にGCなしの場合のパフォーマンスは秀逸だけど、
シーケンシャルに増えていく所はあまり変わらない。


XPathはごめん、僕の書き方が悪い可能性が高い。
でも、いくら頑張ってもStAXより早くなることはなさそう。


ということで、StAXはXMLStreamReaderで全部読み取ってるんだけど、早い。
XMLSpreadSheetの仕様さえ把握できるなら、これが一番有利。


ソースコードを読みたい人は、続きを読んでください。

import java.io.InputStream;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamReader;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathFactory;

import jxl.Cell;
import jxl.Sheet;
import jxl.Workbook;
import jxl.WorkbookSettings;

import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.w3c.dom.Document;

public class ExcelRead {
	private static final int trial = 100;

	private static WorkbookSettings settings = new WorkbookSettings();
	private static XPathFactory xpathFactory = XPathFactory.newInstance();
	private static XPath xpath = xpathFactory.newXPath();
	private static DocumentBuilderFactory df = DocumentBuilderFactory
			.newInstance();
	private static XMLInputFactory factory = XMLInputFactory.newInstance();

	public static void main(String[] args) throws Exception {
		settings.setGCDisabled(true);

		long start = System.currentTimeMillis();
		for (int i = 0; i < trial; i++) {
			readByPoi();
		}
		System.out.println(System.currentTimeMillis() - start);

		start = System.currentTimeMillis();
		for (int i = 0; i < trial; i++) {
			readByJExcelAPI();
		}
		System.out.println(System.currentTimeMillis() - start);

		start = System.currentTimeMillis();
		for (int i = 0; i < trial; i++) {
			readByJExcelAPINoGC();
		}
		System.out.println(System.currentTimeMillis() - start);

		start = System.currentTimeMillis();
		for (int i = 0; i < trial; i++) {
			readByXPath();
		}
		System.out.println(System.currentTimeMillis() - start);

		start = System.currentTimeMillis();
		for (int i = 0; i < trial; i++) {
			readByStAX();
		}
		System.out.println(System.currentTimeMillis() - start);
	}

	private static void readByPoi() throws Exception {
		StringBuilder builder = new StringBuilder(45);
		InputStream in = ExcelRead.class.getResourceAsStream("/test.xls");

		try {
			HSSFWorkbook book = new HSSFWorkbook(in);
			HSSFSheet sheet = book.getSheetAt(0);
			int rowNum = sheet.getLastRowNum();

			for (int i = 0; i <= rowNum; i++) {
				HSSFRow row = sheet.getRow(i);
				int colNum = row.getLastCellNum();
				for (short j = 0; j < colNum; j++) {
					builder.append(row.getCell(j).getRichStringCellValue()
							.getString());
				}
			}
		} finally {
			if (in != null) {
				in.close();
			}
		}
	}

	private static void readByJExcelAPI() throws Exception {
		StringBuilder builder = new StringBuilder(45);
		InputStream in = ExcelRead.class.getResourceAsStream("/test.xls");

		try {
			Workbook book = Workbook.getWorkbook(in);
			Sheet sheet = book.getSheet(0);
			int rowNum = sheet.getRows();
			int colNum = sheet.getColumns();

			for (int i = 0; i < rowNum; i++) {
				Cell[] row = sheet.getRow(i);
				for (short j = 0; j < colNum; j++) {
					builder.append(row[j].getContents());
				}
			}
		} finally {
			if (in != null) {
				in.close();
			}
		}
	}

	private static void readByJExcelAPINoGC() throws Exception {
		StringBuilder builder = new StringBuilder(45);
		InputStream in = ExcelRead.class.getResourceAsStream("/test.xls");

		try {
			Workbook book = Workbook.getWorkbook(in, settings);
			Sheet sheet = book.getSheet(0);
			int rowNum = sheet.getRows();
			int colNum = sheet.getColumns();

			for (int i = 0; i < rowNum; i++) {
				Cell[] row = sheet.getRow(i);
				for (short j = 0; j < colNum; j++) {
					builder.append(row[j].getContents());
				}
			}
		} finally {
			if (in != null) {
				in.close();
			}
		}
	}

	private static void readByXPath() throws Exception {
		StringBuilder builder = new StringBuilder(45);
		InputStream in = ExcelRead.class.getResourceAsStream("/test.xml");

		try {
			Document document = df.newDocumentBuilder().parse(in);

			for (int i = 1; i <= 5; i++) {
				for (int j = 1; j <= 3; j++) {
					String str = xpath.evaluate(
							"//Workbook/Worksheet[1]/Table/Row[" + i
									+ "]/Cell[" + j + "]/Data", document);
					builder.append(str);
				}
			}
		} finally {
			if (in != null) {
				in.close();
			}
		}
	}

	private static void readByStAX() throws Exception {
		StringBuilder builder = new StringBuilder(45);
		InputStream in = ExcelRead.class.getResourceAsStream("/test.xml");

		XMLStreamReader reader = null;
		try {
			reader = factory.createXMLStreamReader(in);

			for (; reader.hasNext(); reader.next()) {
				int eventType = reader.getEventType();
				if (eventType == XMLStreamConstants.START_ELEMENT) {
					if ("Cell".equals(reader.getLocalName())) {
						reader.next();
						builder.append(reader.getElementText());
					}
				}
			}
		} finally {
			if (in != null) {
				in.close();
			}

			if (reader != null) {
				reader.close();
			}
		}
	}
}