blog.Ring.idv.tw

Java

我把程式寫美了,但卻變慢了...

昨天晚上我和同事在討論一個小程式,該程式只是需要一點小邏輯而已,並不會很複雜~ 而且可以有多種寫法,整個故事如下:

該程式需要進行一些轉換的工作,條件是將「1到12的整數,能轉換成1->12, 2->1, 3->2.... 12->11」這樣的處理,很顯然的~ 這只需要一個「if-else」判斷式即可搞定,程式如下:

if (i == 1)
	return 12;
else
	return i-1;

看起來相當直覺~ 而且可讀性也還不錯~ 只是龜毛的我硬是想要用公式來解決它,解法如下:

return (i+10)%12+1;

好了,問題來了~ 以程式碼的可讀性來說前者俱備這樣的優勢,唯一可以挑剔的就是和後者相比程式碼多了點...(其實也還好 XD)

那這兩種解法哪一種執行的效率高呢?「if-else」判斷式?還是公式?

筆者寫了個Java程式來測試~ 不過得不到答案,可能是JVM的關係導致一下子解法一快~ 一下子又解法二比較快... Orz

public class Test
{
    public static int test1(int i)
    {
        return (i + 10) % 12 + 1;
    }

    public static int test2(int i)
    {
        return (i == 1) ? 12 : i - 1;
    }

    public static void main(String[] args)
    {
        long s1 = System.nanoTime();
        for (int i = 0; i < 1000000000; i++)
            test1(i);
        long e1 = System.nanoTime();
        System.out.println(e1 - s1);

        long s2 = System.nanoTime();
        for (int i = 0; i < 1000000000; i++)
            test2(i);
        long e2 = System.nanoTime();
        System.out.println(e2 - s2);
    }
}

不過可以確定的是,如果只從被編譯後的class檔所包含的opcode來看~ 用公式解的opcode數量少了一個,但是執行測試的結果不代表用公式解的效率就比較好:

public static int test1(int);
  Code:
   Stack=2, Locals=1, Args_size=1
   0:    iload_0
   1:    bipush    10
   3:    iadd
   4:    bipush    12
   6:    irem
   7:    iconst_1
   8:    iadd
   9:    ireturn

public static int test2(int);
  Code:
   Stack=2, Locals=1, Args_size=1
    0:    iload_0
    1:    iconst_1
    2:    if_icmpne    10
    5:    bipush    12
    7:    goto    13
   10:    iload_0
   11:    iconst_1
   12:    isub
   13:    ireturn

既然如此,筆者就改用C來測試看看:

#include <stdio.h>
#include <time.h>
int test1(int i)
{
        return (i+10)%12+1;
}
int test2(int i)
{
        return (i==1)?12:i-1;
}
int main(void)
{
        clock_t start_tick, end_tick;
        double elapsed;
        start_tick = clock();
        for(int i = 0 ; i < 1000000000 ; i++)
                test1(i);

        end_tick = clock();
        elapsed = (double) (end_tick - start_tick) / CLOCKS_PER_SEC;
        printf("test1 taken:=%f\n",elapsed);

        start_tick = clock();
        for(int i = 0 ; i < 1000000000 ; i++)
                test2(i);

        end_tick = clock();
        elapsed = (double) (end_tick - start_tick) / CLOCKS_PER_SEC;
        printf("test2 taken:=%f\n",elapsed);
        return 0;
}
gcc -std=c99 -o test test.c

測試結果出爐:

test1 taken:=6.090000
test2 taken:=4.050000

「if-else」判斷式獲勝!! Orz... 那我幹嘛還花時間去想公式~ 唯一的好處就只剩下程式比較「美」罷了~ (自我感覺良好...)

最後謝謝我那位同事(匿名)陪我一起討論 ^^

2010-08-24 14:29:30 | Add Comment

Java class File Format

昨天試著在整理電腦的東西~ 發現一個塵封已久的Word檔~ 裡頭包含的是以前研究Java class File Format記錄~

還記得當初剛看到「0xCAFEBABE」真是會心一笑啊~ 咖啡寶寶?XDDD 是的,沒錯! Java就是拿這四個Bytes當做File Signature,真是有創意極了!!

依稀記得這個class format曾經大幅更動過一次~ 那時候是從「Java 1.4.x」直接跳到「Java 5.0」,而當時最流行的就是「二隻老虎」~ 一隻是Java 5.0的代號「Tiger」,另一隻則是「Mac OS X 10.4」代號也稱為「Tiger」~ 直到去年才被「自然界」所取代... 因為開始出現很多的「Cloud」和「Air」.. 不是「雲」就是「大氣」~ 不勝枚舉 XDD(Cloud Computing、Tag Cloud、Adobe AIR、MacBook AIR...)

上述格式的原始檔如下:

Hello.java

public class Hello
{
	public static void main(String arg[])
	{
		String s = "Hello";
	}
}

有興趣的人可以對照著「VM Spec The class File Format」來剖析~

而Java 5.0 更動的class format請至「JSR 202: JavaTM Class File Specification Update」下載。

筆者的記錄檔:Hello.class 格式剖析 (整張圖放上來會漏漏長...)

2009-01-06 12:54:34 | Add Comment

PDFBox - 擷取PDF檔案中的純文字

PDFBox.是一個Open Source的Java PDF Library,可以利用它來協助處理PDF檔案的一些應用(用iText也是可行的,不過它好像不支援擷取純文字「iText in Action, pp. 576」),例如:擷取PDF檔案中的純文字、轉換PDF檔案到Image檔等等.. 諸如此類的應用。

而且「Lucene」就是用它來轉換PDF到純文字再進行索引的~

下述是擷取PDF檔案中的純文字:(FontBox-0.1.0-dev.jar、PDFBox-0.7.3.jar required!)

import java.io.IOException;
import org.pdfbox.pdmodel.PDDocument;
import org.pdfbox.util.PDFTextStripper;

public class PDFTextExtractor
{
    private PDDocument document;
    
    public String extractText(String file) throws IOException
    {
        document = PDDocument.load(file);       
        PDFTextStripper stripper = new PDFTextStripper();
        stripper.setStartPage(1);
        stripper.setEndPage(document.getNumberOfPages());
        return stripper.getText(document);
    }

    public static void main(String[] args)
    {
        PDFTextExtractor extractor = new PDFTextExtractor();
        try
        {
            String text = extractor.extractText("C:\\test.pdf");
            System.out.println(text);
        }catch (IOException e)
        {
            e.printStackTrace();
        }
    }
}

相關資源

PDF Reference and Adobe Extensions to the PDF Specification - PDF規格書

Glyph & Cog: Text Extraction - 解釋為何擷取PDF中的純文字不是那麼容易

2009-01-03 18:17:22 | Add Comment

SQLite JDBC

由於SQLite官網沒有提供相對應的JDBC Library~ 所以只好從Google下手~ 到目前為止找到了兩套~

一套是「SQLite ODBC Driver」,從它的名稱來看~ 是屬於JDBC Type 1的產物...暫不考慮~

而另一套則是「SQLiteJDBC」~ 嗯~ 就用這一套來連結SQLite吧~

下述是官網所提供的範例:

import java.sql.*;

public class Test {
  public static void main(String[] args) throws Exception {
      Class.forName("org.sqlite.JDBC");
      Connection conn = DriverManager.getConnection("jdbc:sqlite:test.db");
      Statement stat = conn.createStatement();
      stat.executeUpdate("drop table if exists people;");
      stat.executeUpdate("create table people (name, occupation);");
      PreparedStatement prep = conn.prepareStatement(
          "insert into people values (?, ?);");

      prep.setString(1, "Gandhi");
      prep.setString(2, "politics");
      prep.addBatch();
      prep.setString(1, "Turing");
      prep.setString(2, "computers");
      prep.addBatch();
      prep.setString(1, "Wittgenstein");
      prep.setString(2, "smartypants");
      prep.addBatch();

      conn.setAutoCommit(false);
      prep.executeBatch();
      conn.setAutoCommit(true);

      ResultSet rs = stat.executeQuery("select * from people;");
      while (rs.next()) {
          System.out.println("name = " + rs.getString("name"));
          System.out.println("job = " + rs.getString("occupation"));
      }
      rs.close();
      conn.close();
  }
}

2008-12-08 02:18:00 | Add Comment

Jetty - Java HTTP Servlet Server

.2008-12-15 新增Apache Commons DBCP範例

大約六、七年前剛開始初學Web Application的時候~ 那時常聽到的是TomcatResin~ 至於Jetty... 嗯~ 我聞不見其名...XD

Jetty.是一個100%以Java撰寫而成並開放源碼的HTTP Server & Servlet Container~ 從三年前的「Jetty vs. Tomcat vs. Resin: A Performance Comparison」這篇文章來看~ 它的效率似乎表現不錯~ 好了~ 回歸重點~ 為什麼我對它燃起了興趣?... 二個原因,其一,到目前Hadoop 0.19.0版所內建的Servlet Container就是採用Jetty 5.1.4~ 不過根據「(#HADOOP-1650) Upgrade Jetty to 6.x - ASF JIRA」這個issue來看~ 0.20版就會變成Jetty 6.x版了~

另一個原因,由於今年年初有協助老師將一個「English Collocations」的雛型系統改寫成Web版~ 而這樣的系統其實只需要「唯讀」資料庫的內容即可~ 想將整個這樣的系統都做成「DVD-ROM」版本~ 這樣就變成可以帶著跑的Web Application了~ 方便性大大的提高~ 所以Jetty相當適合這樣的應用!!

下面簡單地記錄一下一些常用的設定及步驟:

啟動Jetty Sever

java -jar start.jar etc/jetty.xml

手動增加一個新的Web Application (含VirualHost設定)

請修改「/etc/jetty.xml」。

<New class="org.mortbay.jetty.webapp.WebAppContext">
	<Arg><Ref id="Contexts"/></Arg>
	<Arg><SystemProperty name="jetty.home"/>/webapps/webapp</Arg>
	<Arg>/webapp</Arg>
	<Set name="defaultsDescriptor"><SystemProperty name="jetty.home" default="."/>/etc/webdefault.xml</Set>
	<Set name="VirtualHosts">
	<Array type="java.lang.String">
		<Item>localhost</Item>
	</Array>
	</Set>
</New>

測試Servlet - Hello World

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class Test extends HttpServlet
{
	public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
	{
		res.setContentType("text/html");
		PrintWriter out = res.getWriter();
		out.println("<html>");
		out.println("<head><title>Hello</title></head>");
		out.println("<body>");
		out.println("HIHI");
		out.println("</body>");
		out.println("</html>");
	}
}

修改「web.xml」

<servlet>
	<servlet-name>Hello</servlet-name>
	<servlet-class>Test</servlet-class>
</servlet>
<servlet-mapping>
	<servlet-name>Hello</servlet-name>
	<url-pattern>/Hello.do</url-pattern>
</servlet-mapping>

最後開啟「http://localhost:8080/webapp/Hello.do」即可。

PostgreSQL - ConnectionPoolDataSource

請修改「/etc/jetty.xml」。

<New id="DSTest" class="org.mortbay.jetty.plus.naming.Resource">
	<Arg>jdbc/DSTest</Arg>
	<Arg>
	<New class="org.postgresql.ds.PGConnectionPoolDataSource">
		<Set name="User">postgres</Set>
		<Set name="Password">xxx</Set>
		<Set name="DatabaseName">test</Set>
		<Set name="ServerName">localhost</Set>
		<Set name="PortNumber">5432</Set>
	</New>
	</Arg>
</New>

修改「web.xml」。

<resource-ref>
	<description>My DataSource Reference</description>
	<res-ref-name>jdbc/DSTest</res-ref-name>
	<res-type>javax.sql.DataSource</res-type>
	<res-auth>Container</res-auth>
</resource-ref>

一個簡單的測試程式如下:

import java.io.*;
import java.sql.*;
import javax.sql.*;
import javax.naming.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class PostgresDBTest extends HttpServlet
{
	public void doGet (HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
	{
		res.setContentType("text/html");
		PrintWriter out = res.getWriter();
		
		Connection con = null;
		Statement stmt = null;
		ResultSet rs = null;
		
		try
		{
			ConnectionPoolDataSource source = (ConnectionPoolDataSource)new InitialContext().lookup("jdbc/DSTest");
    			con = source.getPooledConnection().getConnection();  
    			stmt = con.createStatement();
    			rs = stmt.executeQuery("select * from test");
    			while(rs.next())
    			{
    				out.println("Title:"+rs.getString(1)+"<br/>");
    			}
		} catch(Exception e){
    			e.printStackTrace();
		} finally {
    			if(con != null)
    			{
        			try {
       	 				con.close();
        			}catch(SQLException e)
        			{
        				e.printStackTrace();
        			}

    			}
		}		
	}	
}

修改「web.xml」。

<servlet>
	<servlet-name>PostgresDBTest</servlet-name>
	<servlet-class>PostgresDBTest</servlet-class>
</servlet>
<servlet-mapping>
	<servlet-name>PostgresDBTest</servlet-name>
	<url-pattern>/PostgresDBTest.do</url-pattern>
</servlet-mapping>

最後開啟「http://localhost:8080/webapp/PostgresDBTest.do」即可。

Apache Commons DBCP - PostgreSQL

下載下述這三個Library,並複製到「Jetty_Home/lib」底下。

commons-dbcp.jar

commons-pool.jar

commons-collections.jar

請修改「/etc/jetty.xml」。

<New id="pgsqldbcp" class="org.mortbay.jetty.plus.naming.Resource">
	<Arg>jdbc/Blog</Arg>
	<Arg>
	<New class="org.apache.commons.dbcp.BasicDataSource">
	<Set name="driverClassName">org.postgresql.Driver</Set>
	<Set name="url">jdbc:postgresql://localhost/Blog</Set>
	<Set name="username">postgres</Set>
	<Set name="password">1234</Set>
	<Set name="maxActive">10</Set>
	</New>
	</Arg>
</New>

修改「web.xml」。

<resource-ref>
	<description>My DataSource Reference</description>
	<res-ref-name>jdbc/DSTest</res-ref-name>
	<res-type>javax.sql.DataSource</res-type>
	<res-auth>Container</res-auth>
</resource-ref>

測試程式如下:

import java.io.*;
import java.sql.*;
import javax.sql.*;
import javax.naming.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class PostgresDBTest extends HttpServlet
{
	public void doGet (HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
	{
		res.setContentType("text/html");
		PrintWriter out = res.getWriter();
		
		Connection con = null;
		Statement stmt = null;
		ResultSet rs = null;
		
		try
		{
			InitialContext ic = new InitialContext();
			DataSource source = (DataSource)ic.lookup("jdbc/Blog");
			con = source.getConnection();  
			stmt = con.createStatement();
			rs = stmt.executeQuery("select * from test");
			while(rs.next())
			{
    				out.println("Title:"+rs.getString(1)+"<br/>");
			}
		} catch(Exception e){
			e.printStackTrace();
		} finally {
			if(con != null)
			{
				try {
					con.close();
				}catch(SQLException e)
				{
					e.printStackTrace();
				}
			}
		}
	}		
}

2008-12-07 21:36:29 | Comments (5)

Previous Posts
Copyright (C) Ching-Shen Chen. All rights reserved.

::: 切換內文字級 :::

::: 分類 :::

::: 搜尋 :::

Ching-Shen Chen

::: 文件歸檔 :::

::: 最新文章 :::

::: 最新回應 :::

::: 訂閱 :::

Atom feed
Atom Comment

::: 人氣指數 :::

今日人氣:37

累積人氣:897024


::: 線上人數 :::

counter