カレントディレクトリに入ってるExcelブックを1つずつ処理する

カレントディレクトリに入ってるExcelブックを1つずつ開いて、特定のシートを選択して、特定の列をパーズして、○×の件数を求めるスクリプト書いた。

var excel = WScript.CreateObject("Excel.Application");
excel.Visible = true;
excel.DisplayAlerts = false;
var shell = WScript.CreateObject("WScript.Shell");
var fs = WScript.CreateObject("Scripting.FileSystemObject");
var folder = fs.GetFolder(shell.CurrentDirectory);
var files = new Enumerator(folder.Files);
WScript.Echo("シート名\tOK件数\tNG件数");
for(files.moveFirst(); !files.atEnd(); files.moveNext()){
  if(files.item().Name.search(/.+\.xls$/) >= 0){
    var file = files.item().Name;
    var fullPath = folder + "\\" + file;
    var book = excel.Workbooks.Open(fullPath);
    var sheet = book.Worksheets("Sheet1");
    var last = sheet.UsedRange.Rows.Count;
    sheet.Range("A1:AZ65536").UnMerge();
    var OK = 0; var NG = 0;
    for(i = 1; i <= last; i += 1){
      if(sheet.Cells(i, 27).Value != null){
        if(sheet.Cells(i, 27).Value.indexOf("○") == 0){
          OK += 1;
        }
        if(sheet.Cells(i, 27).Value.indexOf("×") == 0){
          NG += 1;
        }
      }
    }
    WScript.Echo(file + "\t" + OK + "\t" + NG);
    book.Close();
  }
}
excel.Quit();

こんな感じで使う。

>cscript //nologo excel.js
シート名        OK件数  NG件数
Book1.xls       13      20
Book2.xls       10      7

BCL easyPDF SDKていうののベンチマーク取ってみた(続き)

昨日やってみたベンチマークは、ふつうのアプリだったけど、Webアプリでも同じ性能がでるかやってみた。

ベンチマークのルール

jpeg画像をサーバー側にあらかじめ連番で1万件作っておく。名前は0000.jpg〜9999.jpgとする。クライアントから順次変換リクエストを投げて、0000.jpg〜9999.jpgをそれぞれPDFに変換する。名前は0000.pdf〜9999.pdfとする。このPDF変換にかかった時間を計測する。サーブレットコンテナはApache Tomcat 6.0.35 Serverとする。

サーバー側のプログラム

サーバー側のプログラムはこんな感じになった。Tomcatだと自動的にスレッドで処理してくれるので、スレッド処理を自前で実装してた部分を省いた。

import com.bcl.easypdf.*;
import com.bcl.easypdf.EasyPDFPrinter.*;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class PDFprint extends HttpServlet {
    public void doGet (HttpServletRequest request,
            HttpServletResponse response)
            throws ServletException, IOException {
        String fileNo = request.getParameter("fileNo");
        String inputFileName = "";
        inputFileName = String.format("C:\\Users\\hide\\Documents\\%s.jpg", fileNo);
        System.out.println(inputFileName);
        String outputFileName = "";
        outputFileName = String.format("C:\\Users\\hide\\Documents\\%s.pdf", fileNo);
        System.out.println(outputFileName);
        EasyPDF.initialize();
        IPrinter printer = new IPrinter();
        IPrintJob pj = printer.getPrintJob();
        pj.PrintOut(inputFileName, outputFileName);
        EasyPDF.uninitialize();
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.println("<html>\n");
        out.println("<head><title>Servlet Sample</title></head>");
        out.println("<body>");
        out.println(inputFileName);
        out.println(outputFileName);
        out.println("</body>");
        out.println("</html>");
        out.close();
    }
}

クライアント側のプログラム

クライアント側のプログラムは、なるべく早くリクエストを投げるために、スレッドで実行してた部分を流用して、リクエストを投げるようにした。

import java.io.*;
import java.net.*;

class PrintRequest {

  public static void main(String[] args) {
    long timeMillisStart = System.currentTimeMillis();

    // スレッドを32個作成する
    MyThread[] threads = new MyThread[32];
    for (int i = 0; i < 32; i++) {
      threads[i] = new MyThread(i);
      threads[i].start();
    }

    // スレッドがすべて終了するのを待つ
    for (int i = 0; i < 32; i++) {
      try {
        threads[i].join();
      } catch (InterruptedException e) {
        System.out.println(e);
      }
    }

    long timeMillisEnd = System.currentTimeMillis();
    long time = timeMillisEnd - timeMillisStart;
    System.out.println(time + "ms. " + time/1000 + "s" );
  }
}

// スレッド
class MyThread extends Thread {
  int threadNo;
  MyThread(int i){
    threadNo = i;
  }
  public void run(){
    for(int i = 0; i < 10000; i++){
      if(i % 32 == threadNo){
        String requestUrl = "";
        requestUrl = String.format("http://localhost:8080/PDFprint?fileNo=%04d", i);
        System.out.println(requestUrl);
        try {
          URL accessURL = new URL(requestUrl);
          HttpURLConnection con = (HttpURLConnection)accessURL.openConnection();
          con.setRequestMethod("GET");
          con.connect();
          BufferedInputStream bis = new BufferedInputStream(con.getInputStream());
          int data;
          while ( (data = bis.read()) != -1 ) System.out.write(data);
        } catch (java.net.MalformedURLException e){
          e.printStackTrace();
        } catch (java.io.IOException e){
          e.printStackTrace();
        }
      }
    }
  }
}

結果

C:\Users\hide\Documents>java PrintRequest

〜途中省略〜

http://localhost:8080/PDFprint?fileNo=9990
<html>

<head><title>Servlet Sample</title></head>
<body>
C:\Users\hide\Documents\9972.jpg
C:\Users\hide\Documents\9972.pdf
</body>
</html>
<html>

<head><title>Servlet Sample</title></head>
<body>
C:\Users\hide\Documents\9990.jpg
C:\Users\hide\Documents\9990.pdf
</body>
</html>
211650ms. 211s

211秒かかった。だいたい4分足らず。念のため全件できてるか確認してみる。

C:\Users\hide\Documents>dir *.pdf

〜途中省略〜

2012/05/06  15:34           394,675 9990.pdf
2012/05/06  15:34           394,675 9991.pdf
2012/05/06  15:34           394,675 9992.pdf
2012/05/06  15:34           394,675 9993.pdf
2012/05/06  15:34           394,675 9994.pdf
2012/05/06  15:34           394,675 9995.pdf
2012/05/06  15:34           394,675 9996.pdf
2012/05/06  15:34           394,675 9997.pdf
2012/05/06  15:34           394,675 9998.pdf
2012/05/06  15:34           394,675 9999.pdf
           10000 個のファイル       3,946,750,000 バイト
               0 個のディレクトリ  60,136,988,672 バイトの空き領域

できてるっぽい。この処理してる間、タスクマネージャーでCPU使用率見てみた。4コア全てで100%に張り付く感じで推移してた。そのほとんどをTomcatのプロセスが占めてて、easyPDFのプロセスは1%程度だった。

スレッド数について

Tomcat6.0はデフォルトで200までスレッド数を増やしちゃうみたいなので、限界までリクエストを投げちゃうとあぼーんすると思う。クライアント側でリクエストをしぼってやれば、かなりの性能が出る感じ。

BCL easyPDF SDKていうののベンチマーク取ってみた

画像をPDFに変換するのっていろいろなやり方があると思うけど、easyPDFていうソフトもそういう機能がある。で、easyPDFはWindowsのCOMとして動作するかなり変態的*1なソフト。COMとして呼び出せるってことはどっからでも呼び出せる*2ってことで、夢が広がるじゃないですかー。どのくらい性能でるのかベンチマーク取ってみた。

ベンチマークのルール

jpeg画像をあらかじめ連番で1万件作っておく。名前は0000.jpg〜9999.jpgとする。0000.jpg〜9999.jpgをそれぞれPDFに変換する。名前は0000.pdf〜9999.pdfとする。このPDF変換にかかった時間を計測する。マシンのスペックはこんな感じ。

OS: Windows 7 , 64 bit Build 7601 Service Pack 1
CPU:total 4 (2 cores per cpu, 2 threads per core) family 6 model 37 stepping 2, cmov, cx8, fxsr, mmx, sse, sse2, sse3, ssse3, sse4.1, sse4.2, popcnt, ht
 ※Intel(R) Core(TM) i3 CPU 540 @ 3.07GHz
Memory: 4k page, physical 3923448k(1523204k free), swap 7845044k(4954616k free)
vm_info: Java HotSpot(TM) 64-Bit Server VM (20.7-b02) for windows-amd64 JRE (1.6.0_32-b05), built on Apr  5 2012 13:39:03 by "java_re" with MS VC++ 8.0 (VS2005)

プログラム

こんな感じのプログラムを作って実行してみた。Javaのスレッドを利用してる。32スレッドを非同期で*3実行して、1万件のファイルを32スレッドで分担して処理する。

import com.bcl.easypdf.*;
import com.bcl.easypdf.EasyPDFPrinter.*;
import java.io.File;

class PrintPDF {

  public static void main(String[] args) {
    long timeMillisStart = System.currentTimeMillis();

    // スレッドを32個作成する
    MyThread[] threads = new MyThread[32];
    for (int i = 0; i < 32; i++) {
      threads[i] = new MyThread(i);
      threads[i].start();
    }

    // スレッドがすべて終了するのを待つ
    for (int i = 0; i < 32; i++) {
      try {
        threads[i].join();
      } catch (InterruptedException e) {
        System.out.println(e);
      }
    }

    long timeMillisEnd = System.currentTimeMillis();
    long time = timeMillisEnd - timeMillisStart;
    System.out.println(time + "ms. " + time/1000 + "s" );
  }
}

// スレッド
class MyThread extends Thread {
  int threadNo;
  MyThread(int i){
    threadNo = i;
  }
  public void run(){
    for(int i = 0; i < 10000; i++){
      if(i % 32 == threadNo){
        String input = String.format("%04d.jpg", i);
        File inputFile = new File(input);
        String inputFileName = "";
        try {
          inputFileName = inputFile.getCanonicalPath();
        } catch (java.io.IOException e){
          e.printStackTrace();
        }
        System.out.println(inputFileName);
        String output = String.format("%04d.pdf", i);
        File outputFile = new File(output);
        String outputFileName = "";
        try {
          outputFileName = outputFile.getCanonicalPath();
        } catch (java.io.IOException e){
          e.printStackTrace();
        }
        System.out.println(outputFileName);
        EasyPDF.initialize();
        IPrinter printer = new IPrinter();
        IPrintJob pj = printer.getPrintJob();
        pj.PrintOut(inputFileName, outputFileName);
        EasyPDF.uninitialize();
      }
    }
  }
}

結果

C:\Users\hide\Documents>java PrintPDF

〜途中省略〜

C:\Users\hide\Documents\9868.jpg
C:\Users\hide\Documents\9868.pdf
C:\Users\hide\Documents\9900.jpg
C:\Users\hide\Documents\9900.pdf
C:\Users\hide\Documents\9932.jpg
C:\Users\hide\Documents\9932.pdf
C:\Users\hide\Documents\9964.jpg
C:\Users\hide\Documents\9964.pdf
C:\Users\hide\Documents\9996.jpg
C:\Users\hide\Documents\9996.pdf
253235ms. 253s

253秒かかった。だいたい4分ちょい。非同期で実行してるので、最後が9999になるとは限らないってことっすね。念のため全件できてるか確認してみる。

C:\Users\hide\Documents>dir *.pdf

〜途中省略〜

2012/05/05  00:54           394,675 9990.pdf
2012/05/05  00:54           394,675 9991.pdf
2012/05/05  00:54           394,675 9992.pdf
2012/05/05  00:54           394,675 9993.pdf
2012/05/05  00:54           394,675 9994.pdf
2012/05/05  00:54           394,675 9995.pdf
2012/05/05  00:54           394,675 9996.pdf
2012/05/05  00:54           394,675 9997.pdf
2012/05/05  00:54           394,675 9998.pdf
2012/05/05  00:54           394,675 9999.pdf
           10000 個のファイル       3,946,750,000 バイト
               0 個のディレクトリ  60,342,808,576 バイトの空き領域

できてるっぽい。この処理してる間、タスクマネージャーでCPU使用率見てみた。4コア全てで100%に張り付く感じで推移してた。そのほとんどをJavaのプロセスが占めてて、easyPDFのプロセスは1%程度だった。

スレッド数について

スレッド数をなんで32個にしたかというと、あんまり増やすとException吐いて変換できないことがあるから。だいたい47個を超えるとException吐く感じ。たぶん47個くらいまでならいけるんじゃないかなー、と思う。4コアなので4の倍数ということで32個にしてみた。ちなみに1000スレッドとかにするとクラッシュダンプ吐いてJVMが死ぬ。あ、1000スレッドとかにしても、synchronized付ければ安定する。するけど、直列で実行されることになるので意味ない。

*1:褒めてます

*2:ExcelのマクロからとかJScriptで書かれたWSHスクリプトからとか…

*3:synchronizedとか付けずに

DOMを操作して特定のデータを抽出するスクリプト書いた

Apacheってmod_proxy_balancerていうモジュールを使うとリバースプロキシとして使えて、ブラウザからbalancer-managerていう管理画面にアクセスすると、振り分けっぷりが分かる。だけどいちいちブラウザでぽちぽちリロードすんのめんどくさいので、自動的にログ取るスクリプト書いた。
このページの赤枠のとこだけ引っこ抜きたい。

こんな感じになった。

var uri = WScript.Arguments(0);
var ie = WScript.CreateObject("InternetExplorer.Application");
ie.Visible = true;
while(ie != null){
  startTime = +new Date();
  ie.Navigate(uri);
  while(ie.Busy || ie.readystate != 4){
    WScript.Sleep(100);
  }
  var h3s = ie.document.getElementsByTagName("h3");
  var tables = ie.document.getElementsByTagName("table");
  for(var i = 0; i < h3s.length; i++){
    WScript.Echo(h3s[i].innerText);
    for(var j = 1; j < tables[i * 2 + 1].rows.length; j++){
      WScript.Echo(tables[i * 2 + 1].rows[j].cells[0].getElementsByTagName("a")[0].innerHTML
      + "\t" + tables[i * 2 + 1].rows[j].cells[5].innerHTML
      + "\t" + tables[i * 2 + 1].rows[j].cells[6].innerHTML);
    }
  }
  endTime = +new Date();
  WScript.Echo(new Date() + "\t" + (endTime - startTime) + "\n");
  WScript.Sleep(60000);
}

こんな感じで使う。

>cscript //nologo bmstat.js http://192.168.2.31/balancer-manager
LoadBalancer Status for balancer://backend
http://192.168.2.32/    Ok      11
http://192.168.2.33/    Ok      10
Mon Jan 9 17:59:47 UTC+0900 2012        118

LoadBalancer Status for balancer://backend
http://192.168.2.32/    Ok      11
http://192.168.2.33/    Ok      10
Mon Jan 9 17:59:52 UTC+0900 2012        121

去年の9/27に作ったスクリプト↓を改造したもの。

takanooの日記 - 特定のページを延々リロードしてページ取得にかかった時間を記録するスクリプト書いた

HT-03AでイオンのSIMを使う

コムギさんとこのとおりやったらイオンのSIMが使えたので忘れないうちにやったことをメモ。

1. スマホ側でUSBの設定を変更。デバッグモードにする。

[設定]→[アプリケーション]→[開発]→[USBデバッグ]にチェック付ける

2. スマホとパソコンをUSBケーブルで接続する。

Windowsだとデスクトップ右下に準備状況が表示されるので「デバイスを使用する準備ができました」が出るまで待つ。

3. パソコンがスマホを認識できたか確認する。

>adb devices
List of devices attached
HT96KLF01634    device

“device”ていうのが表示されたら大丈夫っぽい。(引っこ抜いてから同じコマンド打つと表示されなくなる)adbコマンドの準備については2011-10-25の日記参照。

4. ライブラリをスマホへ転送する。

コムギさんところで紹介されているライブラリを取ってきておいて、adb pushでとりあえずSDカードにコピーする。
イイ:モバイル

>adb push libhtc_ril.so /sdcard/libhtc_ril.so
1028 KB/s (445368 bytes in 0.423s)

5. シェルを起動してrootに昇格する。

>adb shell
$ su
su

ここまで打つとスマホ側で“Superuserリクエスト”ていうのが出るので「許可」をタップする。

# 

6. ライブラリを新しいやつで上書きする。

mvしてみる。

# mv /sdcard/libhtc_ril.so /system/lib/libhtc_ril.so
mv /sdcard/libhtc_ril.so /system/lib/libhtc_ril.so
failed on '/sdcard/libhtc_ril.so' - Cross-device link

Cross-device linkとかいうのでfaildしちゃった。
ddでやってみる。

# dd if=/sdcard/libhtc_ril.so of=/system/lib/libhtc_ril.so
dd if=/sdcard/libhtc_ril.so of=/system/lib/libhtc_ril.so
/system/lib/libhtc_ril.so: cannot open for write: Read-only file system

fsがRead-onlyだった…
mount状況を確認してみる。

# mount
mount
rootfs / rootfs ro 0 0
tmpfs /dev tmpfs rw,mode=755 0 0
devpts /dev/pts devpts rw,mode=600 0 0
proc /proc proc rw 0 0
sysfs /sys sysfs rw 0 0
tmpfs /sqlite_stmt_journals tmpfs rw,size=4096k 0 0
none /dev/cpuctl cgroup rw,cpu 0 0
/dev/block/mtdblock3 /system yaffs2 ro 0 0
/dev/block/mtdblock5 /data yaffs2 rw,nosuid,nodev 0 0
/dev/block/mtdblock4 /cache yaffs2 rw,nosuid,nodev 0 0
/dev/block//vold/179:1 /sdcard vfat rw,dirsync,nosuid,nodev,noexec,uid=1000,gid=1015,fmask=0702,dmask=0702,allow_utime=0020,code
page=cp437,iocharset=iso8859-1,shortname=mixed,utf8 0 0

/systemはRead-onlyすね。remountしてリトライしてみる。

# mount -o rw,remount /dev/block/mtdblock3 /system
mount -o rw,remount /dev/block/mtdblock3 /system
# dd if=/sdcard/libhtc_ril.so of=/system/lib/libhtc_ril.so
dd if=/sdcard/libhtc_ril.so of=/system/lib/libhtc_ril.so
869+1 records in
869+1 records out
445368 bytes transferred in 0.379 secs (1175113 bytes/sec)
# exit
exit
$ exit
exit

よっしゃ。無理矢理感はあるけど上書きできた。

7. APNの設定をする。

スマホを再起動してから、APNの設定をしてみた。
編集したのは以下の4項目だけ。これでつながった。

名前 bmobile
APN dm.jplat.net
ユーザー名 bmobile@aeon
パスワード bmobile

セグメンテーションフォールトを発生させてみる

セグメンテーションフォールトを発生させるプログラム書いてみた。

int main ()
{
  int* x = 10;
  printf ("%d\n", *x);
}

コンパイルして実行してみる。

>gcc segfault.c -o segfault.exe
segfault.c: 関数 'main' 内:
segfault.c:3:12: 警告: 初期化で整数からキャスト無しにポインタを作成しています [デフォルトで有効]
segfault.c:4:3: 警告: 組み込み関数 'printf' の互換性がない暗黙的な宣言です [デフォルトで有効]

>segfault

こんなダイアログ出た。

終了するしかなくなった。

xはint*というふうに宣言してるので、ふつうの変数じゃなくてポインタになる。で、*xで表されるアドレス(10番地)をプリントしようとして、セグメンテーションフォールトが発生する。アドレス10番地はユーザープログラムだと読み取ることすらできないみたい。(Windows 7で検証)

UNIXライクのオペレーティングシステム上では、不正なメモリにアクセスをするプロセスはSIGSEGVシグナルを受け取る。Microsoft Windows上では、不正なメモリにアクセスするプロセスはSTATUS_ACCESS_VIOLATION例外を受け取る。
wikipedia:セグメンテーション違反

STATUS_ACCESS_VIOLATION例外を受け取ったプログラムは終了するしかなくなるってことかな。

LANケーブルが差さってないときにIPアドレスを確認する

ipconfigコマンドってアクティブなインターフェースしか出てこない感じだけど、オフラインのときにも確認できるコマンドないかなー、と思って探してみた。
こんな感じだった。

Windowsキー+R

ファイル名を指定して実行が開くので、

cmd.exe /k netsh interface ipv4 show address

て打つとこんな感じで出る。

>cmd.exe /k netsh interface ipv4 show address

インターフェイスの構成 "ネットワーク ブリッジ"
    DHCP 有効:                         はい
    IP アドレス:                           192.168.2.25
    サブネット プレフィックス:                        192.168.2.0/24 (マスク 255.255.255.0)
    デフォルト ゲートウェイ:              192.168.2.1
    ゲートウェイ メトリック:              0
    インターフェイス メトリック:                      10

インターフェイスの構成 "Loopback Pseudo-Interface 1"
    DHCP 有効:                         いいえ
    IP アドレス:                           127.0.0.1
    サブネット プレフィックス:                        127.0.0.0/8 (マスク 255.0.0.0)
    インターフェイス メトリック:                      50

ブリッジ組んでるとそれぞれのインターフェース名(ローカル エリア接続とか)は出てこないんすね。
省略形はこんな感じ。

cmd /k netsh i i sh a