搜尋此網誌

2013-01-31

在 Firefox 閱讀 epub 格式 - EPUBReader

EPUBReader

Firefox 的插件,可以直接開啟 epub 格式並自動整理好,比多數的閱讀器美觀,字型大小可以從 View → Zoom 或右下調整(不同的調整機制),非常好用。

Excel 的強力外掛 - xll

xll 是一種變形的 dll 檔,提供 Excel 額外的 function,由於以 C 或 C++ 開發,因此享有編譯等級的速度,遠遠超過 VBA 的效能達百倍以上,在進入 Excel 2007 之後,更可以平行運算,威力十分的強大。

首先下載 Visual C++ 2008 Express,雖然也可以用 Visual C++ 2005 Express,不過要另外安裝 platform SDK,所以不考慮;至於 2010 跟 2012 則要註冊,所以也不考慮。

接著下載我修改過的 Excel2013XLLSDK,內含另一個我改寫的簡單範例。

切換到 Excel2013XLLSDK/SAMPLES,編譯 framewrk 及兩個官方範例;先編譯 RELEASE 版。
make RELEASE BUILD X86
再編譯 DEBUG 版。
make DEBUG BUILD X86
切入 Excel2013XLLSDK/SAMPLES/SimpleXll2013,編譯 DEBUG 版。
nmake /f makefile
編譯 RELEASE 版。
nmake /f makefile TYPE=RELEASE
一般來說,RELEASE 版不含除錯訊息,檔案會小一點。

主要程式說明

UDFs.h 是定義 Excel 中的說明文件與 function 的欄位。
// UDFs.h
#define g_rgNumUDFs 5
#define g_rgUDFdata 11
 
static LPWSTR g_rgUDFs [g_rgNumUDFs][g_rgUDFdata] =
    {
        {
            L"SumTwo",
            L"JJJ",
            L"SumTwo",
            L"Arg1, Arg2",
            L"1",
            L"SimpleXll2013",
            L"",
            L"",
            L"SumTwo function help",
            L"",
            L"",
        },
        {
            L"GetString",
            L"UU",
            L"GetString",
            L"Arg",
            L"1",
            L"SimpleXll2013",
            L"",
            L"",
            L"GetString function help",
            L"",
            L"",
        },
        {
            L"SumAll",
            L"UUU",
            L"SumAll",
            L"Arg1, Arg2",
            L"1",
            L"SimpleXll2013",
            L"",
            L"",
            L"SumAll function help",
            L"",
            L"",
        },
        {
            L"Func",
            L"UUU",
            L"Func",
            L"Arg1, Arg2",
            L"1",
            L"SimpleXll2013",
            L"",
            L"",
            L"Func function help",
            L"",
            L"",
        },
    };
UDFs.c 則是實際的程式內容。
// UDFs.c
#include &ltwindows.h&gt
#include &ltxlcall.h&gt
#include &ltframewrk.h&gt
#include &ltstdio.h&gt

__declspec(dllexport) long SumTwo(long arg1, long arg2) {
    return arg1 + arg2;
}

__declspec(dllexport) LPXLOPER12 WINAPI GetString (LPXLOPER12 arg) {
    static XLOPER12 xResult;
    xResult.xltype = xltypeStr;
    xResult.val.str = L"\024Hello from GetString";

    return (LPXLOPER12)&xResult;
}

__declspec(dllexport) LPXLOPER12 WINAPI SumAll(LPXLOPER12 arg1, LPXLOPER12 arg2) {
    static XLOPER12 xResult;
    double d = 0;
    int j = 0;
    __int64 i;
    LPXLOPER12 *ppxArg;
    XLOPER12 xMulti;
    LPXLOPER12 px;
    int error = -1;

    for (j = 0; j < 2; j++) {
        ppxArg = &arg1 + j;
        switch ((*ppxArg)->xltype) {
            case xltypeMissing:
                break;
            case xltypeNum:
                d += (*ppxArg)->val.num;
                break;
            case xltypeMulti:
                if (xlretUncalced == Excel12f(xlCoerce, &xMulti, 2, (LPXLOPER12) *ppxArg, TempInt12(xltypeMulti))) {
                    return 0;
                }
                for (i = 0; i < xMulti.val.array.rows * xMulti.val.array.columns; i++) {
                    px = xMulti.val.array.lparray + i;
                    switch (px->xltype) {
                        case xltypeNum:
                            d += px->val.num;
                            break;
                        case xltypeErr:
                            error = px->val.err;
                            break;
                        case xltypeNil:
                            break;
                        default:
                            error = xlerrValue;
                            break;
                    }
                }
                Excel12f(xlFree, 0, 1, (LPXLOPER12) &xMulti);
                break;
            case xltypeErr:
                error = (*ppxArg)->val.err;
                break;
            default:
                error = xlerrValue;
                break;
        }
    }
    if (error != -1) {
        xResult.xltype = xltypeErr;
        xResult.val.err = error;
    } else {
        xResult.xltype = xltypeNum;
        xResult.val.num = d;
    }

    return (LPXLOPER12)&xResult;
}

__declspec(dllexport) LPXLOPER12 WINAPI Func(LPXLOPER12 arg1, LPXLOPER12 arg2) {
    static XLOPER12 xResult;
    Excel12(xlfSum, &xResult, 2, arg1, arg2);

    return (LPXLOPER12)&xResult;
}
設定 SimpleXll2013.def 將 functions 輸出就完成了。
LIBRARY SimpleXll2013.xll

EXPORTS

;Standard XLL functions
xlAutoOpen
xlAutoClose
xlAutoRegister12
xlAutoAdd
xlAutoRemove
xlAddInManagerInfo12

;UDFs
SumTwo
GetString
SumAll
Func
由於 Excel 2007 開始才有平行運算,因此範例不向下相容,畢竟用 xll 就是要追求速度,不夠快就不應該用。

Excel + xll 超級強大,簡單的前端界面,陡峭的前中段學習曲線,C 語言的 SDK(相對來說,Excel 97 SDK 就是個垃圾,程式寫的相當差!),open source 目前完全沒有辦法相比。不過 open source 的主要使用者仍以高等級的programmers 為主,實際上很少人會想用這種效能打折的 C 語言就是。

要找現成 library 轉的 xll,可以看這邊

Reference:
Data Types Used by Excel
Calling into Excel from the DLL or XLL
Backward Compatibility
Building an Excel XLL in C/C++ with VS 2008

2013-01-20

設定 LaTex 的紙張輸出格式

由於 Debian 不允許 Tex Live 擁有自己的 manager,所以無法從 texconfig 設定;至於 tlmgr,在 repositories 中根本不會有這支程式。由於不想動預設的印表機輸出格式,所以只好在指令轉換的過程中加上指定格式的參數。

dvi 轉 ps
dvips -t a4 file.dvi
ps 轉 pdf
ps2pdf -sPAPERSIZE=a4 file.ps
dvi 轉 pdf 之一
dvipdf -p a4 file.dvi
dvi 轉 pdf 之二
dvipdfmx -p a4 file.dvi
使用 pdflatex 就沒辦法設定了,因為它會使用系統預設的輸出格式,要不然就以 root 權限得改整個系統的輸出格式。
dpkg-reconfigure libpaper1
收工。

2013-01-15

測試 Transmission 的 port 是否打開

由於某些原因,一些 P2P 的 server 會出問題,這時我們需要確認倒底是 ISP 封了某些 port 還是 P2P 的 server 問題,以 Transmission 來說,假如我們要測試 12345 這個 port 是否打開,只要在瀏覽器輸入
https://portcheck.transmissionbt.com/12345
0 表示 port 不通,1 表示 port 打開。

Reference: http://portcheck.transmissionbt.com

解決 Adobe Flash 的中文方塊問題

目前這問題主要是在 TED 上,由於它的中文採用 BIG5 跟 GB 編碼,因此 UTF8 字型無法正確顯示,解決方式就是安裝 BIG5 跟 GB 編碼的字型。

安裝明體跟楷體。
apt-get install fonts-arphic-uming fonts-arphic-ukai
收工。

2013-01-11

101 Great Computer Programming Quotes

101 Great Computer Programming Quotes

精選

"Never trust a computer you can’t throw out a window."
(Steve Wozniak)

"Most software today is very much like an Egyptian pyramid with millions of bricks piled on top of each other, with no structural integrity, but just done by brute force and thousands of slaves."
(Alan Kay)

"True innovation often comes from the small startup who is lean enough to launch a market but lacks the heft to own it."
(Timm Martin)

"It has been said that the great scientific disciplines are examples of giants standing on the shoulders of other giants. It has also been said that the software industry is an example of midgets standing on the toes of other midgets."
(Alan Cooper)

"There are two ways of constructing a software design. One way is to make it so simple that there are obviously no deficiencies. And the other way is to make it so complicated that there are no obvious deficiencies."
(C.A.R. Hoare)

"There’s an old story about the person who wished his computer were as easy to use as his telephone. That wish has come true, since I no longer know how to use my telephone."
(Bjarne Stroustrup)

"First learn computer science and all the theory. Next develop a programming style. Then forget all that and just hack."、"To iterate is human, to recurse divine."
(George Carrette)

"There are only two kinds of programming languages: those people always bitch about and those nobody uses."
(Bjarne Stroustrup)

"PHP is a minor evil perpetrated and created by incompetent amateurs, whereas Perl is a great and insidious evil perpetrated by skilled but perverted professionals."
(Jon Ribbens)

"Java is, in many ways, C++–."
(Michael Feldman)

"Saying that Java is nice because it works on all OSes is like saying that anal sex is nice because it works on all genders."
(Alanna)

"Software is like sex: It’s better when it’s free."
(Linus Torvalds)

"The only people who have anything to fear from free software are those whose products are worth even less."
(David Emery)

"Good code is its own best documentation."
(Steve McConnell)

"The first 90% of the code accounts for the first 90% of the development time. The remaining 10% of the code accounts for the other 90% of the development time."
(Tom Cargill)

"I don’t care if it works on your machine! We are not shipping your machine!"
(Vidiu Platon)

"I think there’s a world market for about 5 computers."
(Thomas J. Watson, Chairman of the Board, IBM, circa 1948)

"It would appear that we have reached the limits of what it is possible to achieve with computer technology, although one should be careful with such statements, as they tend to sound pretty silly in 5 years."
(John Von Neumann, circa 1949)

真是相見恨晚!

2013-01-08

Binary Tree Deletion

程式碼
#include <stdio.h>    /* printf */
#include <stdlib.h>    /* calloc, free */
#include <string.h>    /* memset */

struct Tnode {
    unsigned int* value;
    struct Tnode* left;
    struct Tnode* right;
};

unsigned int* gen_rand(unsigned int n) {
    unsigned int i, temp1, temp2;
    unsigned int* array = (unsigned int *)calloc(n, sizeof(unsigned int));
    for ( i = 0; i < n; i++ ) {
        *(array + i) = i + 1;
    }
    for ( i = 0; i < n; i++ ) {
        temp1 = (unsigned int)rand() % n;
        temp2 = *(array + i);
        *(array + i) = *(array + temp1);
        *(array + temp1) = temp2;
    }

    return array;
}

/* insert item recursively */
void insert(struct Tnode** tree, struct Tnode* item) {
    if ( (*tree) == NULL ) {
        *tree = item;
        return;
    }
    if ( *(item->value) < *((*tree)->value) ) {
        insert(&((*tree)->left), item);
    } else if ( *(item->value) > (*tree)->value ) {
        insert(&((*tree)->right), item);
    }
}

struct Tnode* create_tree(unsigned int n, unsigned int* array) {
    unsigned int i;
    struct Tnode* tree = (struct Tnode *)calloc(n, sizeof(struct Tnode));
    for ( i = 0; i < n; i++ ) {
        (tree + i)->value = array + i;
        insert(&tree, tree + i);
    }

    return tree;
}

void inorder(struct Tnode* root) {
    if ( root != NULL ) {
        inorder(root->left);
        printf("%u ", *(root->value));
        inorder(root->right);
    }
}

unsigned int search(struct Tnode* root, unsigned int d) {
    struct Tnode* ptr = root;

    while ( ptr != NULL ) {
        if ( *(ptr->value) == d ) {
            return 1;
        } else {
            if ( *(ptr->value) > d ) {
                ptr = ptr->left;
            } else {
                ptr = ptr->right;
            }
        }
    }

    return 0;
}

void delete(struct Tnode* root, unsigned int d) {
    struct Tnode *parent, *child, *ptr;
    unsigned int isfound = 0;
    parent = ptr = root;
    while ( ptr != NULL && isfound == 0 ) {
        if ( *(ptr->value ) == d ) {
            isfound = 1;
        } else {
            parent = ptr;
            if ( *(ptr->value) > d ) {
                ptr = ptr->left;
            } else {
                ptr = ptr->right;
            }
        }
    }
    if ( ptr == NULL ) {
        return;
    }
    /* leaf node */
    if ( ptr->left == NULL && ptr->right == NULL ) {
        if ( parent->left == ptr ) {
            parent->left = NULL;
        } else {
            parent->right = NULL; 
        }
        memset(&ptr, 0, sizeof(struct Tnode *));
        return;
    }
    /* no left node */
    if ( ptr->left == NULL ) {
        if ( parent != ptr ) {
            if ( parent->left == ptr ) {
                parent->left = ptr->right;
            } else {
                parent->right = ptr->right;
            }
        } else {
            root = root->right;
        }
        memset(&ptr, 0, sizeof(struct Tnode *));
        return;
    }
    /* no right node */
    if ( ptr->right == NULL ) {
        if ( parent != ptr ) {
            if ( parent->right == ptr ) {
                parent->right = ptr->left;
            } else {
                parent->left = ptr->left;
            }
        } else {
            root = root->left;
        }
        memset(&ptr, 0, sizeof(struct Tnode *));
        return;
    }
    /* have left and right node */
    parent = ptr;
    child = ptr->left;
    while ( child->right != NULL ) {
        parent = child;
        child = child->right;
    }
    ptr->value = child->value;
    if ( parent->left == child ) {
        parent->left = child->left;
    } else {
        parent->right = child->left;
    }
    memset(&ptr, 0, sizeof(struct Tnode *));
    return;
}

int main(int argc, char* argv[]) {
    unsigned int n = (unsigned int)strtoul(argv[1], NULL, 10);
    unsigned int d = (unsigned int)strtoul(argv[2], NULL, 10);
    unsigned int* array = gen_rand(n);
    struct Tnode* root;

    if ( argc != 3 ) {
        exit(0);
    }
    root = create_tree(n, array);
    inorder(root);
    printf("\nFound? %u\n", search(root, d));
    delete(root, d);
    inorder(root);
    printf("\n");
    free(root);
    free(array);

    return 0;
}
編譯
gcc deletion.c -ansi -Wall -Wextra -pedantic -Wconversion -Wshadow -o deletion
隨機排列 1 ~ 8,列印,尋找 3 並刪除 3,再列印。
./deletion 8 3
輸出結果。
1 2 3 4 5 6 7 8 
Found? 1
1 2 4 5 6 7 8

2013-01-07

指定特定 CPU 核心執行特定程式 - taskset

由於系統會在多核心 CPU 之間分配與切換工作,而切換 CPU 時需要將 cache 拷貝到新的 CPU,因此會造成效能低落,因此需要指定某一特定核心專責執行某些程式。

指定核心四執行 virtualbox。
taskset -c 3 virtualbox
收工。

Reference: How to Assign a Process to a Certain CPU Core in Ubuntu Linux

CPU 壓力測試 - cpuburn

主要是用來測試 CPU 的散熱效果的,穩定度或效能倒不是主要設計目的。

以 root 權限安裝。
apt-get install cpuburn
以一般權限測試單核心。
burnP6
Ctrl + c 中止,或在另一個 terminal 執行中止指令。
killall burnP6
由於一次只能測試一個核心,所以如果要測試四核心 CPU。
burnP6 & burnP6 & burnP6 & burnP6
收工。

2013-01-04

compiler 的最佳化

Inorder Tree Traversal without Recursion

這是 iteration 的版本,到底跟易於了解的 recursion 版本的速度差別有多大呢?

程式碼
#include <stdio.h>  /* printf */
#include <stdlib.h> /* malloc, free */
#include <papi.h>   /* PAPI_get_real_cyc */

struct Tnode {
    unsigned int* value;
    struct Tnode* left;
    struct Tnode* right;
};

struct Snode {
    struct Tnode* tnode;
};

unsigned int* gen_rand(unsigned int n) {
    unsigned int i, temp1, temp2;
    unsigned int* array = (unsigned int *)calloc(n, sizeof(unsigned int));
    for ( i = 0; i < n; i++ ) {
        *(array + i) = i + 1;
    }
    for ( i = 0; i < n; i++ ) {
        temp1 = (unsigned int)rand() % n;
        temp2 = *(array + i);
        *(array + i) = *(array + temp1);
        *(array + temp1) = temp2;
    }

    return array;
}

/* insert item recursively */
void insert(struct Tnode** tree, struct Tnode* item) {
    if ( (*tree) == NULL ) {
        *tree = item;
        return;
    }
    if ( *(item->value) < *((*tree)->value) ) {
        insert(&((*tree)->left), item);
    } else if ( *(item->value) > *((*tree)->value) ) {
        insert(&((*tree)->right), item);
    }
}

struct Tnode* create_tree(unsigned int n, unsigned int* array) {
    unsigned int i;
    struct Tnode* tree = (struct Tnode *)calloc(n, sizeof(struct Tnode));
    for ( i = 0; i < n; i++ ) {
        (tree + i)->value = array + i;
        insert(&tree, tree + i);
    }

    return tree;
}

void inorder(struct Tnode* root) {
    if ( root != NULL ) {
        inorder(root->left);
        if (*(root->value) > 0) {}
        inorder(root->right);
    }
}

void inorder_iter(unsigned int n, struct Tnode* root) {
    unsigned int done = 0;
    struct Snode* stack = (struct Snode*)calloc(n, sizeof(struct Snode));
    struct Tnode* tcurrent = root;
    struct Snode* scurrent = stack;

    while ( done == 0 ) {
        if ( tcurrent != NULL ) {
            if ( scurrent == NULL ) {
                scurrent->tnode = tcurrent;
            }
            scurrent++;
            scurrent->tnode = tcurrent;
            tcurrent = tcurrent->left;
        } else {
            if ( scurrent != stack ) {
                tcurrent = scurrent->tnode;
                scurrent--;
                if (*(tcurrent->value) > 0) {}
                tcurrent = tcurrent->right;
            } else {
                done = 1;
            }
        }
    }
    free(stack);
}

int main(int argc, char* argv[]) {
    unsigned int n = (unsigned int)strtoul(argv[1], NULL, 10);
    unsigned int* array = gen_rand(n);
    unsigned int t1, t2, t3, t4;
    struct Tnode* root;

    if ( argc != 2 ) {
        exit(0);
    }
    t1 = (unsigned int)PAPI_get_real_cyc();
    root = create_tree(n, array);
    t2 = (unsigned int)PAPI_get_real_cyc();
    inorder(root);
    t3 = (unsigned int)PAPI_get_real_cyc();
    inorder_iter(n, root);
    t4 = (unsigned int)PAPI_get_real_cyc();
    free(root);
    free(array);
    printf("elapsed cycles 1 = %u\n", t2 - t1);
    printf("elapsed cycles 2 = %u\n", t3 - t2);
    printf("elapsed cycles 3 = %u\n", t4 - t3);

    return 0;
}
編譯
gcc compare_iter_recu.c -Wall -Wextra -pedantic -Wconversion -Wshadow -static /usr/local/lib/libpapi.a -o compare_iter_recu
執行
./compare_iter_recu 200000
結果
elapsed cycles 1 = 237677068
elapsed cycles 2 = 18256342
elapsed cycles 3 = 17505206
大約在 200000 筆資料時打成平手,不過 iteration 版在記憶體配置多了三分之一,所以並沒有比較吃香。

PS︰用 clang 編譯的話,速度大約比 gcc 慢了 40%,最佳化這玩意還是需要一些程式碼的累積。