January 2010 - 文章

解決Virtual PC網路分享存取暴慢的問題

在Windows 7使用Virtual PC VM時發現一個嚴重問題,當透過網路分享方式存取VM分享資料夾時,傳輸速度慢到嚇人!

例如: 我的Windows 7上有台Virtual PC VM(IP = 192.168.1.15),當從Windows 7的C:\Softeware Copy檔案到\ \192.168.1.15\Downloads 時,速度暴慢,始終維持在16KB/s以下...

Windows 7與Virtual PC分明共用一張100M Ehternet網卡,卻只能用到128k,會不會太扯了一點? 簡直比扯鈴還扯呀~~~

查詢到MS KB-Slow performance when you try to access resources on your Virtual Server 2005 host computer from a guest virtual machine,裡面提到VM所模擬的DEC Intel 21140A網卡晶片不支援TCP Segmentation Offloading(這個火星術語翻譯成地球語就是: 把拆封包的工作交給網卡,減少CPU的負擔),當主機啟用此功能時會造成網路速度變慢並衍生斷線等問題。

KB建議的解決方式有三種:

  1. 另外新增一張虛擬網卡(Microsoft Loopback Adapter),VM改用虛擬網卡Routing上網。(我覺得這有點為了喝牛奶養牛)
  2. 修改Registry,停用TCP Task Offloading。(此舉可能會增加CPU負擔,但我的CPU使用率很少超過5%,應該算是"九千牛一毛"吧! )
  3. 不想全機停用TCP Task Offloading的話,可以只停用特定網卡的Offloading。(我只有一張網卡,所以用2也沒差)

最後我決定採用方法2,增加HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\DisableTaskOffload後重新開機,再試一次搬檔,40MB/s!!! 對嘛,這才像話!

Sharepoint Web Service NullReferenceException Error

最近在整合Sharepoint的Search.asmx做客製化,一路上波折不斷。

很幸運地,找到一個好用的Open Source查詢工具--Sharepoint Search Sevice Tool,可以提供Scope、欄位資訊,用勾選就可以自動組出Query XML,按鈕後馬上看結果。複雜的Sharepoint Search瞬間被簡化,讓我這個新手在最短的時間可以進入狀況,真是勝造七級浮屠的聖品! (面對全然陌生的技術議題,當你手上有一個可以RUN的Sample,那種安全感無可言喻啊!)

不過,我並沒有因為獲得神奇道具就從此過著幸福快樂的日子。

我要處理的MOSS是由多台Server組成的Web Farm,測試發現同樣要連Search.asmx,某幾台Server OK,有幾台卻怎麼敲帳號密碼都不會過。仔細比對發現,有記憶連線帳號密碼的主機不必問帳號密碼就可以連上;遇上未記憶密碼的Server,怎麼輸入都會得到HTTP 401 UnauthorizedAccess Exception。

追了一下,發現問題出在原程式中未將Domain Name傳成NetworkCredential的第三個參數,修改後就可解決。(參考: NOTES-NetworkCredential Constructor for Domain Account)

殺掉小魔王後,遇到另一個更機車的問題。例如: 我的Web Farm FQDN是sps2007.darkthread.net,其中有五台機器,192.168.100.1 - 192.168.100.5。結果連到192.168.100.1/192.168.100.2 Search.asmx可以正常查詢,連至3, 4, 5三台則在呼叫Search.asmx GetPortalSearchInfo()時出現NullReferenceException。

爬文半天也毫無頭緒,後來心一橫決定鋸箭! 由於呼叫GetPortalSearchInfo是用來取得Scope清單,而另一個Method GetSearchMetadata也可以取得Scope清單,在文件上更是建議用它取代GetPortalSerachInfo。我把GetPortalSearchInfo的程式碼刪除,將Scope擷取邏輯交給GetSearchMetadata,暫時就閃開了這個問題。

只可惜高興不到兩分鐘,這番鋸箭雖然可以讓我連上3,4,5號機列出Scope,但只要一Query,就會彈出System.ArgumentNullException!! 能正確執行的還是只有1,2號機。

到此,我已耗去兩天時間(其間還試著用Reflector查看GetPortalSearchInfo想找出端倪、也設法試跑過Managed Object Model、當然也爬了無數的文),最後,看到有人提及用FQDN失敗、用Machine Name成功的案例,給了我一線生機。

偵辦方向由程式API規格轉到與SPS設定上,於是便問了一下熟悉架構的同事,原來Sharepoint 2007是有玄機的,有個備用存取對應決定了內部URL與公用URL的對應,看到以下設定畫面的那一刻...

一切盡在不言中~~~

CODE-Save ADO.NET DataTable As CSV

之前寫過將CSV檔案內容轉換成ADO.NET DataTable物件,今天的需求剛好反過來,要將DataTable的內容匯出成CSV,邏輯上簡單許多,但還是PO文一篇,下回需要時比較好找。

static string Quoted(string v) {
    return "\"" + v.Replace(@"""", @"""""") + "\"";
}
 
static void SaveDataTableAsCSV(DataTable t, string csvPath)
{
    StringBuilder sb = new StringBuilder();
    List<string> l = new List<string>();
    foreach (DataColumn c in t.Columns)
        l.Add(Quoted(c.ColumnName));
    sb.AppendLine(string.Join(",", l.ToArray()));
    foreach (DataRow r in t.Rows)
    {
        l.Clear();
        for (int i = 0; i < t.Columns.Count; i++)
            l.Add(Quoted(r[i].ToString()));
        sb.AppendLine(string.Join(",", l.ToArray()));
    }
    File.WriteAllText(csvPath, sb.ToString(), Encoding.UTF8);
}

註:

  1. File.WriteAllText時要加註Encoding.UTF8以確定輸出檔案會包含BOM檔頭,Excel才能正確開啟。
  2. 測試發現,欄位內容值即使包含換行符號,也能被Excel正確解析,讓我驚喜了一下。
NOTES-產生具有BOM的UTF8編碼檔案

上回有討論過Excel開啟CSV時的中文編碼問題,今天發現關於.NET處理BOM的幾個特性,再補充三則筆記:

  • 雖然預設UTF8Encoding的encoderShouldEmitUTF8Identifier參數預設為true,但GetBytes()的結果不會包含BOM
  • File.WriteAllText與StreamWriter在沒有指定Encoding.UTF8時,會產出UTF-8編碼但沒有BOM的檔案
  • 以下的範例中,只有F2.csv、F4.csv可以正確被Excel開啟,原因請見上回文章
string s = "牛,牪,犇";
File.WriteAllText("B:\\F1.csv", s);
File.WriteAllText("B:\\F2.csv", s, Encoding.UTF8);
using (StreamWriter sw = 
    new StreamWriter("B:\\F3.csv", false))
{
    sw.WriteLine(s);
    sw.Close();
}
using (StreamWriter sw =
    new StreamWriter("B:\\F4.csv", false, 
        Encoding.UTF8))
{
    sw.WriteLine(s);
    sw.Close();
}
NOTES-NetworkCredential Constructor for Domain Account

這問題之前曾遇過幾次,但處理得有些含糊,這回特別做了測試釐清。

【疑問】要用NetworkCredential設定存取身份時,網域帳號可否寫成"domainName\userName",例如: new NetworkCredential("domainName\\userName", "password") ?

不知為什麼,我一直記得這樣寫是可行的(也許因為Windows的登入視窗,可以選擇domainName\userName或將domainName寫到第三個欄位吧?),今天實地做了測試,答案是: (分別測試了Windows 2000/Windows 2003、在Web本機存取自己的Web、本網域/信任網域...)

不行!!! 請乖乖寫成new NetworkCredential("userName", "password", "domainName")

不過,為求方便,我們應該要允許使用者輸入"domainName\userName",然後在程式裡用以下方法快速拆解:
if (userName.Contains("\\")) {
    string[] p = userName.Split('\\');
    cred = new NetworkCredential(p[1], password, p[0]);
}

【網友經驗】

Posted 28 January 2010 04:37 PMJeffrey | no comments
Filed under:
【茶包射手日記】失落的change事件

有個網頁在某欄位的change事件掛了一段邏輯,依輸入內容連動其他欄位值。使用者抱怨上個月第一次使用完全正常,這個月再用時,在該欄位中輸入資料,其他欄位卻未跟著連動...

我用自己的Client連到同一網頁,跟User輸入同樣的值,一切正常。移駕到使用者座位,使用"肇事"機器實地操作,連動功能也完全正常! 莫非,這程式會認主人,只要遇到拎杯親自操作就不敢造次?

世界上有很多無法解釋的玄妙事件,但本案例並不包含在內。依茶包射手實戰手冊第748頁的記載,此種靈異現象通常是User與Developer在操作上有細微差異才造成不同結果,最好的處理方式是請User依"平日習慣"操作一次,實地進行觀察。終於發現端倪了! 原來,使用者這個月Key單時輸入該欄位的資料跟上月雷同,於是好心的IE亮出了"自動完成"提示(如下圖示意),能少打字當然要省,User使Click一下自動帶入;而我在測試時,為了確保change被觸發,潛意識驅使之下是一個字母一個字母手動輸入。重點來了--IE的自動完成有一項特性,它 不 會 觸 發 change 事 件 !

之前在測試開發階段,因偷懶加掛了One Click自動填表功能對手動測不多;而更早的手動測試階段,雖然曾反覆輸入不同值做測試,因表單沒有Submit,並不會納入自動完成提示清單(When a user submits a form, the name, value, and domain of the form component are encrypted for safekeeping),因此還是錯失"以自動完成輸入"的機會。

事件成因清楚了,要解決就不難。我想到幾種做法:

  1. 利用<input autocomplete="off" />關閉自動完成功能。
  2. 改用blur取代change,但缺點是輸入值未變時也會觸發不必要的連動邏輯。
  3. 用onpropertychange取代onchange,但缺點是會有跨瀏覽器問題,再不然就是針對IE加Javascript針對不同瀏覽器採行不同的做法。(鄉親吶~~ 看清楚,這就是跨瀏覽器要付出的代價呀!)
生活瑣記-201001
  • 自從有了DSLR胡亂玩了好些年,也不乏曾為追逐天晴大景背著沈重相機搶登山巔,
    總以為要有好照片少不了景好技術佳加上老天賞臉,
    直到前幾天,用老爺CASIO相機拍下這張半糊的照片,
    我才驀然發現,
    其實,照片是否引起悸動挑動心絃,無關乎構圖快門或光圈...


  • 幫兒子洗澡時,他好奇地摸著自己的"蛋蛋"誠心發問: 這個是做什麼用的呀?
    鄉親吶~~~ 在黑暗浴室臨時舉辦的【百萬小學堂】,關於這道【 大班 自然與生活科技】問題,你會怎麼回答呢?
    (計分標準: 需以五歲小朋友可以理解的語言傳達正確知識,搞笑胡扯搪塞迴避者不予計分。另因臨時舉辦,故小西瓜、大力都沒來,所以也沒有好人卡、團結卡、知識卡可以用囉...)
    至於我的答案? 先賣個關子,寫在文末。
  • 車子的大燈壞了一顆,看在車主手冊有寫拆裝步驟的分上,決定自己賺工錢。
    在網站比了價(幸好有先做功課,查到網購價最低140,老闆原本想唬我燈泡價位不可能這麼低),在汽車材料行花150元買了一顆歐司朗H1,花了點時間模索... 哇哈哈! 黑暗水電工邁出挑戰【黑暗修車工】的第一步囉!

  • 陽台上有一棵種了兩年的香水檸檬,原本是跟花販買的小盆裁,入手時就帶了一棵拳頭大小的果實。
    據說它是極好種的植物,隨便養都能果實纍纍。無奈放在陽台日照不足,加上平時也沒在園藝下功夫,在咱們家隨便種的結果,就是它也敷衍長。兩年來下再多肥料也只長葉子,開花次數用一隻手都可以數出來,更甭提結果實。所幸半年前有兩朵花順利修得正果,結出香水檸檬來。
    這讓人頗有老來得子的振奮感,當然就捨不得摘,日益變大之際還怕樹枝不堪負荷,在下面墊了個玻璃瓶權充嬰兒椅。這陣子果實開始變黃,看來己熟透,摘下前特地拍照一張留念。
    圖中有個一元硬幣可當比例尺,讓大家感受一下它的體積。其實也還好啦,還是像拳頭一般大,只是這回是像"砂鍋大的拳頭"一樣大,很強吧? XD

百萬小學堂的【 大班 自然與生活科技】問題大家有解答了嗎? 在此公佈黑暗版的答案:

"這是用來裝生小孩【種子】的地方,你要保護好哦!"

兒子想起在開心農場種馬鈴薯播種發芽的過程,很滿意地點點頭,還自行演繹出"哦! 所以把種子放到女生的肚子裡,就可生小Baby囉!"

怎樣,這個比喻超讚吧?

CODE-列舉元素已掛載的jQuery事件

開發程式時偵錯的需求,想確認預期的事件函數是否已正確bind到元素上?

直覺想法是去查詢jQuery內部物件,列出已經掛載的事件函數。追了一下原始程式,發現jQuery會把各元素的事件保存在jQuery.data(elem, "events"),而events裡又會為不同事件(例如: click, dblclick, load)各宣告一個handlers,放入events[eventType];由於我們可以對同一事件宣告多個事件函數,因此handlers中會以handlers[handlerId]的方式保存事件函數。

看起來很抽象,跑一次範例便一目瞭然:

$("div")
.click(function() { alert("Click 1"); })
.click(function() { alert("Click 2"); })
.dblclick(function() { alert("DblClick"); });
var handlers = jQuery.data($("div")[0], "events");
var sb = [];
for (var t in handlers)
{
    sb.push("*EventType=" + t);
    for (var h in handlers[t])
        sb.push("    HandlerId[" + h + "]->" + handlers[t][h]);
}
alert(sb.join("\n"));

可得結果如下: (程式裡所謂HandlerId是一個流水號,是jQuery.event物件為每個事件函數所賦與不重複的編號)

*EventType=click
    HandlerId[3]->function() { alert("Click 1"); }
    HandlerId[4]->function() { alert("Click 2"); }
*EventType=dblclick
    HandlerId[5]->function() { alert("DblClick"); }

實地測試,這個做法在jQuery-1.3.2或jQuery-1.4下都是可行的。但在jQuery 1.4下HandlerId會從1起跳,背後原因是jQuery-1.4取消了原本在bindReady()裡jQuery.event.add( window, "load", jQuery.ready );,以及防止IE Memory Leak的邏輯中改用attachEvent取代jQuery( window ).bind( 'unload', ...);,少了兩個系統內建事件函數,自訂事件函數序號變成從1開始。

【註】以上的做法列舉範圍只限透過jQuery.bind方式宣告的事件函數。

用.NET展現多核威力(3) – 佛心TPL之Parallel.For好威

前一篇文章裡,我們驗證了為每個CPU Core開一條獨立Thread並事先分攤好計算工作,可以讓巨量Log10計算程式飆出最高效能! 但是,仔細看看程式碼:

int WORKER_COUNT = 2;
Thread[] workers = new Thread[WORKER_COUNT];
int jobsCountPerWorker = MAX_COUNT / WORKER_COUNT;
for (int i = 0; i < WORKER_COUNT; i++)
{
    int st = jobsCountPerWorker * i;
    int ed = jobsCountPerWorker * (i + 1);
    if (ed > MAX_COUNT) ed = MAX_COUNT;
    workers[i] = new Thread(() =>
    {
        for (int j = st; j < ed; j++)
        {
            double d = Math.Log10(Convert.ToDouble(j));
        }
    });
    workers[i].Start();
}
for (int i = 0; i < WORKER_COUNT; i++)
    workers[i].Join();

我們寫了近20行的程式碼,而且還得花腦筋寫邏輯分割工作給多條Thread,要曉得如何用Thread.Join同步完成時間。說實在話,沒有三兩三,恐怕沒膽玩。

如果我說有一種很簡單的新寫法可以實現類似的效果:

Parallel.For(0, MAX_COUNT, j =>
{
    double d = Math.Log10(Convert.ToDouble(j));
});

看到這裡,大家會不會有想起立鼔掌的衝動?

這是.NET Framework 4.0裡內建的Task Parallel Library(TPL),一組幫助程式新手的佛心API。把原本複雜的多執行緒運算程式簡化成一行打死,寫程式的人就算對Thread.Join、lock()、ManualResetEvent一無所悉,照樣可以寫出漂亮的平行運算程式。(這下子,程式老鳥又有一項優勢被剝奪了,被菜鳥幹掉的日子愈來愈近了,我好怕...)

依我個人的理解,TPL強調的是平行運算的能力,目的在搾乾每一滴CPU運算能力。它在概念上介於ThreadPool與自行管理數條Thread之間,最精彩的地方是會依CPU的負載狀況自動調節Thread數,直到所有CPU使用率都飆上100%,系統運算能力完全被榨乾為止。換句話說,一開始迴圈只啟動一條Thread,接著會在資源允許的前題下增加平行處理的Thread數(the loop starts with a degree of 1, and may work its way up to any maximum that’s specified as resources become available,參考)。

接著,我們就讓原來寫法跟Parallel.For車拼一下(測試平台為E6400雙核CPU): (註: 要體驗.NET 4.0,請先下載VS2010 Beta回家玩)

using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
 
namespace MultiCore
{
    class TestParalleFor
    {
        static void Main(string[] args)
        {
            int MAX_COUNT = 5000 * 10000;
            Stopwatch sw = new Stopwatch();
            for (int round = 0; round < 3; round++)
            {
                sw.Reset();
                sw.Start();
                int WORKER_COUNT = 2;
                Thread[] workers = new Thread[WORKER_COUNT];
                int jobsCountPerWorker = MAX_COUNT / WORKER_COUNT;
                for (int i = 0; i < WORKER_COUNT; i++)
                {
                    int st = jobsCountPerWorker * i;
                    int ed = jobsCountPerWorker * (i + 1);
                    if (ed > MAX_COUNT) ed = MAX_COUNT;
                    workers[i] = new Thread(() =>
                    {
                        for (int j = st; j < ed; j++)
                        {
                            double d = Math.Log10(Convert.ToDouble(j));
                        }
                    });
                    workers[i].Start();
                }
                for (int i = 0; i < WORKER_COUNT; i++)
                    workers[i].Join();
                sw.Stop();
                Console.WriteLine("Multi-Thread[{1}] = {0:N0}ms", 
                    sw.ElapsedMilliseconds, WORKER_COUNT);
 
                sw.Reset();
                sw.Start();
                Parallel.For(0, MAX_COUNT, j =>
                {
                    double d = Math.Log10(Convert.ToDouble(j));
                });
                sw.Stop();
                Console.WriteLine("Parallel.For = {0:N0}ms",
                    sw.ElapsedMilliseconds);
            }
            Console.Read();
        }
    }
}

Multi-Thread[2] = 2,003ms
Parallel.For = 2,119ms
Multi-Thread[2] = 2,098ms
Parallel.For = 2,283ms
Multi-Thread[2] = 1,966ms
Parallel.For = 2,113ms

就數據而言,二者相近,但在這個例子中,大部分的時間Parallel.For還是略輸一籌。理由是Parallel.For強調的是動態調節,由一條Thread開始,再逐步增加,自然會比事先規劃好全程用兩條Thread衝刺慢一些。不過別沮喪,我們動個手腳,馬上就能還Parallel.For一個公道。

我們將:

double d = Math.Log10(Convert.ToDouble(j));

改成每5000次Delay 10ms: (假裝在等待某項非CPU資源)

double d = Math.Log10(Convert.ToDouble(j));
if (j % 5000 == 0) Thread.Sleep(10);

並把執行次數改為100萬次縮短總執行時間。

Multi-Thread[2] = 1,046ms
Parallel.For = 652ms
Multi-Thread[2] = 1,009ms
Parallel.For = 502ms
Multi-Thread[2] = 1,007ms
Parallel.For = 550ms

怎樣? 前一個測試二者結果相近,加入Thread.Sleep後比原來的一核一緒寫法快了近一倍,程式簡潔N倍,在這兩回合比試中,我認定Parallel.For明顯勝出!!

因為Thread.Sleep的加入會降低CPU使用率,Parallel.For動態增加Thead的能力便可派上用場,填補了CPU空檔,也就縮短了總執行時間。在實務上,即便是以運算為主的工作,還是免不了有等待I/O、等待其他Thread就緒的同步需求,必須暫停等待其他非CPU資源後再繼續,等待期間會產生CPU使用率下降的情況。平行運算哲學中,讓CPU閒著是一種罪惡,在CPU使用率未達100%時增加Thread數充分利用閒置的運算能力,自然會比一核一緒更上一層樓。要自己寫出視CPU使用率動態調節Thread數的程式並非易事,而Parallel.For可以幫我們做到這一點,很威吧? 大家如果在.NET 4.0中開發類似的平行運算需求時,千萬不要錯過它囉~~

[2010-02-05補充] 若想在.NET 3.5中使用 Parallel.For(),可以參考這篇文章

NOTES-客製化Sharepoint查詢研究筆記

最近工作上遇到需要客製Sharepoint查詢的需求,這篇主要是研究過程的隨手筆記,避免明天一覺醒來忘到一乾二淨。由於內容較雜亂無章,請讀者自行斟酌是否要直接略過。

  • 文件: MOSS 2007 Custom Search
    1. 先考慮用現成Search,可以節省大把時間。SPS2007已經比SPS2003先進方便很多--Business Data Content(BDC)。
    2. 真需客製時可用Microsoft.Office.Server.Search.Query建立WebPart
    3. 跨系統搜索(例如: SPS, CMS)的做法
      1) 建立BDC Application Definition, 匯入SSP(Shared Services Provider)。其中要實作ProductIDEnumerator method,用來傳回Primary Key,讓Sharepoint可以用它讀資料回來做Index。
      2) 匯入時會建立BDC Entities的網頁,可用來檢視資料是否正確匯入。
      3) 在SSP中加入DBC Source,開始Crawl
      4) 建立自訂屬性對應到爬回來的屬性,加入一些進偕搜尋可以用來篩選的條件(Mapping時可設定順位,若沒A,用B)
      5) 重新再爬一次,這次會對應回Managed Properties。
      6) 自訂搜尋吧! 有三種方法:
          * Keyword Query syntax (直接送給Enterprise Search service)
          * SQL syntax (加料式SQL語法)
          * URL syntax (直接串連搜尋介面網頁)
    4. SQL法範例:
      SELECT        Title, Path, Description, HitHighlightedSummary, Rank, ProductMembershipRank, DomesticRegion, ProductCategory
      FROM            SCOPE()
      WHERE         FREETEXT(Description,'Fun') AND ProductCategory = 'Accommodation'
      AND DomesticRegion = 'Australia''s South West'
      ORDER BY MembershipRank DESC, RANK DESC
    5. SQL法的傳回結果:
      FullTextSqlQuery ftsq = new FullTextSqlQuery(ServerContext.Current); ftsq.ResultTypes = ResultType.RelevantResults;
      ftsq.QueryText = <YOURQUERY>;
      //Return the search results to a ResultTableCollection,可視為DataSet用
      ResultTableCollection results = ftsq.Execute()
    6. ResultTableCollection,可視為DataSet用,同時有ElapsedTime, HitHighlightedSummary等好用屬性。
  • 官方文件: Enterprise Search Architecture
    • 組成元件: Index Engine, Query Engine, Protocol Handlers(將不同管道取得的原始資料中的文件取出來), IFilters(取出文件中的文字內容與屬性), Content Index, Property Store, Search Configuration Data, Wordbreakers
    • Content Crawling: Filter Daemon使用Protocol Handler把資料取回來,用IFilter去取出其中的文字與Metadata,做全文檢索Index時會用到Workbreaker,Property與Index是分開存放的。Property存放處還一併記錄了文件層級的安控資訊。
    • Search Query Engine: 搜尋關鍵字會先經Wordbreaker處理。有加Property條件時,會先用Index查,再依查到結果去取回屬性資料做第二層篩選,沒權限的查詢結果項目也會被刪除。
    • 支援的資料來源: Sharepoint Content, Web Content, File Share, Exchange Folder Content, Business Data Content
    • Shared Scopes: 用一些條件把內容項目定義成一個特定範圍,可用的Rule包含: Address, Property Query, Content Source
    • Document Property Mappings: 有兩種Property-Crawled Prop.及Managed Prop.,二者間可以設定Mapping。
    • Server Mapping: 可以設定查到的結果怎麼Mapping成特定的URL,例如: File Share -> Web Link
    • Revevance Inclusions: 計算Ranking,改變結果項目顯示順序,可能的參數包含: Click distance, Hyperlink anchor text, URL surf depth, URL text matching, Automated metadata extraction, Automatic language detection, File type relevancy biasing, Enhanced text analysis
    • File Type Inclusions: 決定Crawler哪些檔案類別要擷取哪些不要
    • Logging: Query Log, Crawl Log
    • Site Level的搜尋管理: Scope, Keyword and best bet
  • 官方文件: Building Enterprise Search Queries
    • Keywords
      • 關鍵字: Keyword1 +Keyword2 -Keyword3
      • 屬性:
        <property name>:<value>
        <scope name>:<value>
        <collapsed results type>:<value>
      • 屬性範例: site:http://blog.darkthread.net, author:"Jeffrey Lee", scope:"ScopeName", duplicate:http://<displayUrl>(這個看不懂)
      • 查詢時,不會檢查URL, SiteName, File Ext,就是查不到東西
    • Enterprise Search SQL Language
      • 以ANSI-SQL為基礎
      • 不一定要精確,例如: program <-> programming
      • rank欄位會傳回吻合度,0-1000
      • 有些(謎之聲: 這叫很多吧?)SQL語法不可用: CONVERT(但CAST可以)、CREATE VIEW、DATASOURCE、AVG(), COUNT(), MAX(), MIN(), SUM(), GRANT, INSERT, CONTAINS, LIKE, MATCHES, 關聯欄位相比較(不會吧?), Parameter, REVOKE, SCOPE, SELECT ALL, Stored Procedure, UNKNOWN, UPDATE, BATCH...
      • 要指定Scope => SELECT ... FROM Scope() WHERE "scope" = "All Sites"...
      • CINTAINS精確比對,FREETEXT類似比對
      • LIKE, string/date/timestamp/numeric可以用大小於比對, NULL代表未定義
      • WHERE LastModifiedTime <=DATEADD (DAY, -2, DATEADD (HOUR, -4, GETGMTDATE()))
      • ARRAY [1,2] < ARRAY [1,2,3]
    • URL法
      • 參數: k-關鍵字, s-Scope, v=date or relevance, start=頁數
    • Enterprise Search Query Object Model
      • Web Part or ASPX
      • Microsoft.Office.Server.dll, Microsoft.Office.Server.Search.dll, Microsoft.SharePoint.dll
      • 物件
      • 用哪一個物件? 複雜時KeywordQuery會無法勝任,就用FullTextSqlQuery
    • Web Service: httq://Server_Name/[sites/][Site_Name/]_vti_bin/search.asmx
      • Query只傳回Title, Desc, Date, Relevance,QueryEx還可傳回Rank, Author, Size, Path...等。
      • QueryEx傳回DataSet!! (Yahoo~~~)
    • 自訂結果存取權限管控,決定User該不該看到該筆查詢: ISecurityTrimmer
    • 客製查詢結果: 修改XSLT
    • 客製查詢中心
    • 寫程式管理Search Content、Metadata、Scope、Keyword...
    • Featured Search: 在查詢結果中混入其他來源的結果(置入性行銷?)
  • 佛心來著的Sharepoint Search Service Tool: 可以輔助產生Query Syntax、馬上看結果的好東西
用.NET展現多核威力(2A) - 一核一緒補充包

前一篇多核研討文章中,用了一個計算1000萬次Log10運算的範例驗證Thread數與Core相同時可以達到最佳效能,網友Google質疑以Log10計算當範例是否用能代表"以運算為主的大量作業",在此做點補充說明。

我想若以茶包射手實事求是的精神,"以運算為主的大量作業"這個命題是有問題的,應該要修正成"不涉及非CPU資源競爭的大量純運算作業"更貼近原意。用白話來解釋,這裡假設的前題是---有一大堆運算工作要處理,每件運算工作彼此獨立可以同時進行,且每件運算所需的資料自給自足,不需要排隊讀取/寫入記憶體、磁碟、網路等資源。當此前題成立,理論上連續大量運算就會耗光單一CPU的所有運算資源,讓CPU呈現100%佔用狀態。因此不管是"簡單的Log10計算"或是"複雜的超大矩陣運算"應該都能得到相同的結果--要達到最高效能,就是讓每顆CPU都忙到最高點,並避免Thread過多時要耗費CPU去做Context Switch拖累效能。以這個概念演繹,為每一核安排一條執行緒應為最佳解。

補充完前回理論命題的不足,我們也來試一下"矩陣運算的範例",看看是否可以獲得一核一緒的結論?

不想自己造輪子,這裡用現成"內含小型矩陣運算的DES加密"當作範例,但複雜度己比Log10高出許多。我用亂數產生了1000個長度為512KB的byte[],分別用1-16條的Thread分工合作處理對這1000筆資料做DES加密,不同Thread數的測試各做五次,取其總耗用時間的平均值。

using System;
using System.Diagnostics;
using System.Threading;
using System.Security.Cryptography;
using System.Text;
using System.IO;
using System.Collections.Generic;
 
namespace MultiCore
{
    class ComplexCalc
    {
        static void Main(string[] args)
        {
            int MAX_COUNT = 1000;
            Dictionary<int, byte[]> pool = new Dictionary<int, byte[]>();
            Random rnd = new Random();
            for (int i = 0; i < MAX_COUNT; i++)
            {
                byte[] b = new byte[512 * 1024];
                rnd.NextBytes(b);
                pool.Add(i, b);
            }
            int ROUND_COUNT = 5;
            Stopwatch sw = new Stopwatch();
            for (int WORKER_COUNT = 1; WORKER_COUNT < 16; WORKER_COUNT++)
            {
                long sum = 0;
                List<string> detail = new List<string>();
                for (int round = 0; round < ROUND_COUNT; round++)
                {
                    sw.Reset();
                    sw.Start();
                    Thread[] workers = new Thread[WORKER_COUNT];
                    int jobsCountPerWorker = MAX_COUNT / WORKER_COUNT;
                    for (int i = 0; i < WORKER_COUNT; i++)
                    {
                        int st = jobsCountPerWorker * i;
                        int ed = jobsCountPerWorker * (i + 1);
                        if (ed > MAX_COUNT) ed = MAX_COUNT;
                        workers[i] = new Thread(() =>
                        {
                            DESCryptoServiceProvider des =
                                new DESCryptoServiceProvider();
                            des.Key = Encoding.UTF8.GetBytes("ABCDEFGH");
                            des.Key = Encoding.UTF8.GetBytes("12345678");
                            for (int j = st; j < ed; j++)
                            {
                                byte[] randomContent = pool[j];
                                using (MemoryStream ms = new MemoryStream())
                                {
                                    using (CryptoStream cs = new CryptoStream(ms,
                                        des.CreateEncryptor(), CryptoStreamMode.Write))
                                    {
                                        cs.Write(randomContent, 0, randomContent.Length);
                                        cs.FlushFinalBlock();
                                    }
                                }
                            }
                        });                          
                        workers[i].Start();
                    }
                    for (int i = 0; i < WORKER_COUNT; i++)
                        workers[i].Join();
                    sw.Stop();
                    sum += sw.ElapsedMilliseconds;
                    detail.Add(sw.ElapsedMilliseconds.ToString());
                }
                Console.WriteLine("平行處理[{1}] = {0:N0}ms [{2}]",
                    sum / ROUND_COUNT, WORKER_COUNT, string.Join(",", detail.ToArray()));
            }
            Console.Read();
        }
    }
}

有幸借到一台8核CPU的神器,在上面測試得到的結果,應該可讓我們對"一核一緒"的推論更有信心。

平行處理[1] = 14,838ms [14825,14804,14834,14913,14815]
平行處理[2] = 7,942ms [7932,7781,8069,7992,7938]
平行處理[3] = 5,224ms [5434,5234,5138,5160,5158]
平行處理[4] = 3,976ms [3952,3941,3975,3959,4053]
平行處理[5] = 3,312ms [3299,3322,3374,3305,3261]
平行處理[6] = 2,900ms [2874,2905,2913,2915,2897]
平行處理[7] = 2,615ms [2566,2646,2605,2670,2592]
平行處理[8] = 2,540ms [2526,2449,2506,2566,2654]
平行處理[9] = 2,641ms [2809,2588,2713,2522,2576]
平行處理[10] = 2,726ms [2737,2706,2710,2809,2672]
平行處理[11] = 2,684ms [2724,2638,2670,2625,2767]
平行處理[12] = 2,771ms [2796,2795,2744,2717,2805]
平行處理[13] = 2,755ms [2648,2764,2665,2710,2990]
平行處理[14] = 2,726ms [2741,2705,2809,2711,2667]
平行處理[15] = 2,781ms [2862,2686,2792,2837,2732]

陸續用過不同的演算內容(共同等徵是自給自足的純CPU運算不涉及其他資料共用)做相同測試,大致的結果都很相近--Thread數增加時,耗用時間會遞減,最短的時間會出現在Thread數與CPU核數相同時;而Thread超過CPU核數時,時間會略為增加,但並不會隨Thread數繼續上升而明顯增加,而是呈現隨機變化(以上為例: 11變快,12變慢,13又比12快,反覆做了幾次,發現其無一定規則)。針對這個觀察,我的解釋是,Thread數比CPU核數略多時,產生的Context Switch狀況有限,故對效能的影響也不大。經過多次實驗,耗用時間最短的數據出現在Thread數等於CPU核數這點倒是被獲得證實。

平行運算是門深奧的學問,要深入探討學術理論遠超出一般程式開發人員的能力範圍。不過,依茶包射手的精神,在能力許可範圍親手實驗一下與自己切身相關的效能議題,體驗意想不到的樂趣有何不可? 上述的範例算是一個有趣的實驗基礎,稍加修改就可以用來驗證不同運算需求在不同核緒比例下的效能表現,有興趣的人可以玩玩,檢視一下不同運算需求的最佳核緒比例,也歡迎有不同心得的朋友分享切磋。

邁入光纖時代囉~~

家裡的ADSL速率維持2M/256K的速率已經多年,由於平時沒有畜養動物的習慣,這個頻寬用來上網查資料、噗浪、讓家裡的兩位小自耕農栽種水果花卉... 倒是綽綽有餘。

唯二小小不滿足,是發Mail上載大型附檔時常會被256K的上傳頻寬勒住,有種用吸管喝湯的不暢快感;還有在看YouTube影片時,每次只要按下HQ高畫質鈕,主角就會三不五時遭人點穴無法動彈,看了於心不忍。

一直在等待高頻寬的價位平民化,去年底剛好看到現有ISP有個針對老客戶的資訊月特惠專案,升級成10MB/2MB,每個月大約多一百多塊,頻寬就可放大五倍,感覺上頗為划算,便提出申請。

接獲通知要來裝機,特別在ADSL退休前,做個傳輸速度Benchmark,一方面留念,一方面好做為升級後的比較基準。(茶包射手格言: 建立衡量基準是Performance Tuning的第一步)

2M/256K ADSL

10M/2M 光纖

依據升級頻寬前後下載同樣檔案的傳輸速率,算下來差不多真的提升約五倍。另外,看YouTube HQ也確實不再有停格卡住的狀況,感覺良好!

從此正式宣佈,黑暗府堂堂邁入光纖時代囉~~~

Posted 16 January 2010 12:31 PMJeffrey | no comments
Filed under:
jQuery 1.4 小閱兵

為了歡度jQuery四歲生日(jQuery由John Resig 於 2006/01/14 在 BarCamp NYC 首次發表[註]),jQuery開發團隊在2010/01/14釋出了jQuery 1.4版

簡單整理一下我所理解的1.4改版重點:

  1. 大量重構常用的函數,降低程式複雜度(主要是減少內部函數彼此呼叫的次數),達到改善效能的目標。
  2. *.css(), .attr(), .val(), .html(), .text(), .append(), .prepend(), .before(), .after(), .replaceWith(), .wrap(), .wrapInner(), .offset(), .addClass(), .removeClass(), .toggleClass() 可傳入函數,由函數的傳回值當作參數值。(傳入函數當成參數的做法,有個術語叫Function Settter)
  3. *.css(), .attr(), .val(), .html(), .text(), .append(), .prepend(), .offset(), .addClass(), .removeClass(), .toggleClass() 在Function Setter時,該函數的第二個參數即原值。這點很方便,例如,如果要在所有的<a>顯示文字的後方加上"[L]"字串,過去會寫成:
    $("a”).each(function(i) { var $a = $(this); $a.html($a.html() + "[L]"); });
    現在可以結合2, 3點的新功能,寫成:
    $("a”).html(function(i, html) { return html + “[L]”; });
    變簡單很多吧!
  4. $.ajax在傳送陣列參數時,預設會改用ary[]=elem1&ary[]=elem2的Query String表示法(過去會轉成ary=elem1&ary=elem2),這多半跟PHP、Ruby on Rails搭配使用。 如果想要保留原做法,需另外設定jQuery.ajaxSettings.traditional = true;
  5. *$.ajax可不指定dataType,由Response的ContentType決定: applcation/json時自動調為"json",text/javascript或application/x=javascript時變成”script”,text/xml時視為"xml”。
  6. $.ajax設定ifModified:true時,支援ETag檢查決定是否使用Cache的內容。
  7. *JSON解析原本都是用eval,從1.4起會在Browser有支援的情況下使用內建的JSON Parser(如: IE8, FF3.1, Chrome, Safari),比eval來得安全也更有效率。
  8. .serialize支援HTML5的欄位元素,例如: datetime, range。
  9. *$.ajax多了context參數,在Callback函數中可以透過this直接存取context所傳入的元素物件,不必再用Closure,簡單很多! 例如:
    $.ajax({
        url: “gettime.aspx”,
        context: $(“#spnNowTime”),
        success: function(s) { context.html(s); }
    });
  10. $.get, $.post, $getJSON, $.getScript 的success Callback多了第三個傳入參數--XHR。以前若想存取XHR,就必須放棄$.get等簡易寫法,回頭改用$.ajax。
  11. 送出AJAX Request時不管有無資料內容,一律傳送Content-Type Header,以符合某些應用場合的要求。
  12. 可以指定JSONP的callback函數名稱了! 也就是在邊做邊學jQuery第14集中提到的jsonp1238089172708,現在可透過jsonpCallback參數自訂。
  13. $.ajax用onreadystatechange取代setTimeout輪詢,改善效能 。
  14. .text()支援CDATA
  15. *很酷的新HTML元素建構寫法:
    jQuery("<div/>", {
        id: "foo",
        css: {
            height: "50px",
            width: "50px",
            color: "blue",
            backgroundColor: "#ccc"
        },
        click: function() {
           $(this).css("backgroundColor", "red");
        }
    }).appendTo("body");
  16. *.eq(-N), .get(-N)可以取"倒數第N個",.first(), .last()可以取第一個跟最後一個。
  17. 過去jQuery()會傳回document的jQuery物件,1.4版起則改為傳回空的jQuery集合,以便動態加入元素。jQuery().ready()在1.4仍維持jQuery(document).ready()的效果,但建議不要再用了。
  18. jQuery(“tagName”)改用getElementByTagName,速度大幅改善;$("<div>”), $(“<div />”), $(“<div></div>”)均改用document.createElement,$(“<div></div>”)效能己改善。
  19. .css()加快兩倍、.addClass(), removeClass(), .hasClass()加快三倍。
  20. .toggleClass()支援一次切換多個class,例如: toggleClass("class1 class2")。
  21. *.data(myCacheObject)可以用傳入的物件置換掉整個Cache物件,而.data()可以取回整個Cache物件。
  22. .data()使用的Cache物件改成有用到才建立,以改善效能。
  23. 呈現動畫時,可針對不同屬性設定不同的Easing效果。[參考: 天才少年James PadolseyDemo]
  24. jQuery.proxy()可以為現有函數產生一個分身,並指定該分身的Scope(簡單來就是強制指定this)。這有什麼用呢? 常見的案例是在事件函數中,要用另一個元素當作this。例如:
    var f = function() { alert(this.id); };
    $("#A,#B").click(jQuery.proxy(f, $("#A")[0]));

    在A,B上掛了同一個click事件,但透過jQuery.proxy指定Scope為A,則B的click事件裡,this也會指向A。
  25. .bind()時可傳入物件{ click: function() { … }, keydown: function() { … } },一次指定多個事件。
  26. *克服了change(), submit()在IE裡跟其他瀏覽器部分行為有差異的問題。
  27. *新事件focusin, focusout。類似focus及blur,但是支援Bubble Up。應用範例,<p>中包了<input>,當<input>取得焦點時,<p>的focusin事件也會被觸發。
  28. *除了ready, focus(可用focusin取代), blur(可用focusout取代),所有的事件都可以.live()了,萬歲!!! 另外,live的對象也可以指定Context了。(這應該可以克服上回在滲透式live管不到Iframe document的問題)
  29. .ready()會使用setTimeout持續檢查document.body是否存在,原則上只用來解決Safari瀏覽器的少數問題案例。
  30. 非IE瀏覽器不再做unload事件的Memory Leak檢查以加快速度。
  31. .append(), .prepend(), .before(), .after()變快約一倍,.html()變快三倍,.remove(), .empty()變快4倍。
  32. 新方法: .detach(),將元素自DOM移除,但保留事件函數,多半用在暫時移除,稍後加回去的情境。
  33. 新方法: .unwrap(),剝掉外層,留下html()的部分。例如: <div id="X"><span>A</span></div>,$("#X").unwrap() -> <span>A</span>
  34. 內部函數domManip加入Cache機制,jQuery("<div>"), .after("<div>")會比以前快。
  35. 沒有加進DOM的物件也可以用.before(), .after(), .replaceWith()。例如: jQuery("<div/>").before("<p>Hello</p>").appendTo("body") 。
  36. *.clone(true)現在連.data()也一併會複製 。
  37. .offset()支援設定功能,可用來改變元素位置。(設定時,CSS position會被自動改成relative)
  38. *queue()不再只侷限在動畫應用上,新增.delay(n)函數,可暫停n ms、.next()可跳到下一個動作、clearQueue()可以清除動作。
  39. *.index()取得元素在同層元素中的排行,.index(selector)取得元素在同層元素同符合selector的排行。
  40. .has()。$("div”).has(“a”) 相當於 $("div:has(a)”)
  41. .nextUntil(), .prevUntil(), parentsUntil(),類似.nextAll(), .prevAll(), .parents(),但會一直向後/前/上蒐集元素,直到符合selector元素為止。
  42. .add(), .closest()支援傳入第二個參數作為Context 。
  43. jQuery.isEmptyObject()用來判別是否物件沒有任何屬性 。
  44. jQuery.isPlainObject()用來判別是否為物件 。
  45. jQuery.contains(A, B)用來判別元素B是否被包在元素A中 。
  46. *jQuery.noop(),什麼事都不做的空函數,用在一定要傳入函數作為參數的場合。
  47. jQuery.unique(),只保留DOM元素陣列中不重複的部分,並依在DOM出現順序排列。
  48. 其他: jQuery.browser支援Engine導向,例如: jQuery.browser.webkit用來判別所有以WebKit開發的Browser、對Java Applet的支援更好、不再使用arguments.callee(ECMAScript 5要廢掉)、使用Closure Compiler而非YUI Min來壓縮jquery-1.4.min.js 。

【改版後不相容之處】

改用1.4,請注意以下的寫法可能會出問題:

  1. .add()的結果改成依元素在DOM裡的順序排列
  2. .clone(true)會一併複製事件跟.data()儲存的資料
  3. jQuery.data(elem)不再只傳回id字串,而是該元素的.data() Cache物件
  4. jQuery() != jQuery(document)
  5. *.val(“…”)作用在<option>或<input type=”checkbox”>時,只認value,不再看text
  6. *jQuery.browser.version現在傳回Engine版本
  7. 將會阻擋格式不符規定的JSON表示字串
  8. *預設會以PHP/Rails的ary[]=elem1&ary[]=elem2方法序列化參數,如想維持ary=elem1&ary=elem2的做法,請設定jQuery.ajaxSettings.traditional = true;
  9. 內建屬性jQuery.className移除
  10. jQuery.extend(true, …)將不再適用於非物件及陣列
  11. *$.ajax未指定dataType,而Response.ContentType == “text/javascript”時,將會自動被當成Script執行,不再當成string處理
  12. $.ajax的ifModified會一併納入ETags決定是否使用Cache。

好消息是,如果你發現原來的js換了1.4有以上問題,懶得改程式又很想用1.4,只要加掛一個Plug-In就可以解決。
<script src="http://code.jquery.com/jquery.js"></script>
<script src="http://code.jquery.com/jquery.compat-1.3.js"></script>

我只能說,jQuery真是佛心來著呀!!

【註】紅色*是我個人認為值得關注的改變

連三拉三

感謝各位鄉親支持,讓老人家再次獲得微軟肯定,第四度連莊MVP,今天收到Award Kit。

附上很沒誠意的開箱照: (原本想安排正妹一起入鏡才符合當今開箱的主流時尚,無奈寒舍僅有的小正妹只顧著專心看海綿寶寶... 大家偶爾吃清淡一點也好)

從去年起,MVP禮品由傳統的阿宅玩具改成尊榮的水晶獎座。今年微軟又重新設計了一個水晶獎座,而旁邊可掛上砝碼形狀的年度環,未來連任時(如果還有機會的話)可以玩一下疊疊樂。

今年的獎座挺美的,讓我想拿來當作商業攝影的練習題。盡管花了點功夫調光線、弄背景、也嘗試不同的曝光量,但照出來的作品還是未夠班呀~~ 大家湊和著看吧!

  

BUG-VS2008在x64 OS下會忽略Form_OnLoad的未處理例外

昨天河道上看到噗友bauann為了VS2008在W7 x64下無法順利運作,重灌到滿頭大汗外加內心淌血。我原本以為這是一起"抽中籤王"的個案,因為我公司與家裡的環境分別是Win2008 x64 + VS2008與W7 x64 + VS2008,印象中都可以正常使用。(若公司機器的VS2008有問題還混到現在,應該早就被炒魷魚了 XD)

不過,噗裡引用了一篇論壇討論,卻讓我想起一些東西...

One VERY important point is that this issue is only on exceptions raised during the Forms.OnLoad event handler (or methods called from within this event handling).

回憶起確實遇過Form_Load階段有錯誤,卻沒被VS2008抓到的經驗,當時很狐疑是否VS2008某個設定沒調好才會這個樣子,但因為不致"卡關",便未深究。

bauann提供的資訊解答了我的疑惑,我寫了一個WinForm Project來驗證:

using System;
using System.Windows.Forms;
using System.IO;
 
namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public static string InvalidPath =
            "1:\\invlid_path.txt";
 
        public Form1()
        {
            InitializeComponent();
        }
 
        private void Form1_Load(object sender, EventArgs e)
        {
            MessageBox.Show("Form1_Load 1");
            string s = File.ReadAllText(InvalidPath);
            MessageBox.Show("Form1_Load 2");
        }
 
        private void button1_Click(object sender, EventArgs e)
        {
            string s = File.ReadAllText(InvalidPath);
        }
    }
}

果不其然,MessageBox “Form1_Load 1”後,接著就進入WinForm操作界面。理論上在File.ReadAllText那一列就該因檔案路徑亂給而發生Exception,但在x64 OS下的VS2008卻若無其事地忽略之後的程式碼MessageBox.Show(“Form_Load 2”)繼續執行。在button1_Click裡,相同的程式碼,Unhandled Exception倒可以正確地被VS2008捕捉。

由MSDN論壇的討論得知,這確認是一個VS2008的Bug,目前尚無HotFix。但有幾種解決方法:

1) 設定Exception throw時就攔截(如下圖),但這會導致每一個try { … }遇到的Exception也觸發中斷。

2) 在Form_Load()第一列加入System.Diagnostics.Debugger.Break(),然後按Ctrl-F5跑程式(Start without Debugging),如此Debugger會事後掛上去,就可攔到Unhandled Exception。

以上兩個方法都不是頂好的根治之道,只能稍稍止痛,依目前的產品發展時程,我認為這個Bug被修復的機會不太大。但由於問題出現時機只限於Form_OnLoad事件,在使用Windows x64 + VS2008寫Windows Form Project時多加留意,I think I can live with it。

更多文章 下一頁 »

搜尋

Go

<January 2010>
SunMonTueWedThuFriSat
272829303112
3456789
10111213141516
17181920212223
24252627282930
31123456
 
RSS
【工商服務】
最新回應

Tags 分類檢視
關於作者

一個醉心技術又酷愛分享的Coding魔人,十年的IT職場生涯,寫過系統、管過專案, 也帶過團隊,最後還是無怨無悔地選擇了技術鑽研這條路,近年來則以做一個"有為的中年人"自許。

文章典藏
其他功能

這個部落格


BlogLook Score and Rank

Syndication