2010年2月24日星期三

讓工作管理員進行繪圖

興之所致,找來一個有趣面試題目來解答:如何能讓CPU 的使用率表現為一條正弦曲線?

這個題目來自《编程之美--微软技术面试心得》,有機會的我會話買下來當作娛樂。

// This program try to burn cpu cycles with a sinusoid function against the time.
#include <MCD/Core/System/TaskPool.h>
#include <MCD/Core/System/Timer.h>
#include <math.h>

class Task : public MCD::TaskPool::Task {
public:
Task(float frequency) : MCD::TaskPool::Task(0), mFrequency(frequency) {}

sal_override void run(MCD::Thread& thread) throw() {
MCD::Timer timer;
while(thread.keepRun()) {
double current = timer.get().asSecond();
double busy = (sin(current * mFrequency) + 1) / 2;
double idle = 1 - busy; // We base on a 1 second interval
while(timer.get().asSecond() < current + busy) {} // Burn CPU cycles
MCD::mSleep(int(idle * 1000)); // Let CPU idle
}

// The Task instance is not needed any more, destroy it.
delete this;
}

float mFrequency;
}; // Task

int main(int, char const*[])
{
const size_t cpuCount = 4; // Set this to match your hardware thread count
const float frequency = 0.1f;

MCD::TaskPool taskPool;
taskPool.setThreadCount(cpuCount);

for(size_t i=0; i<cpuCount; ++i)
taskPool.enqueue(*new Task(frequency));

std::cout << "Press enter to quit...\n";
std::cin.get();

taskPool.stop();

return 0;
}

sinusoidTaskManager

註:中間的那個高峰因突如其來的 MSN 短訊耗掉了整整一個 CPU Core 而缺了一角。

2009年12月15日星期二

Virtual box shared folders 備忘

Virtual box 3.1 已支援 OpenGl 2.1,MCore 的 Linux port 也可繼續。

已往我使用 smbfs 來 mount Windows 的網絡磁碟機:

//192.168.0.1/mcore3d /home/ricky/mcore3d smbfs iocharset=utf8,unicode,username=xxx,password=xxx 0 0

不過很容易會有權限或 encoding 的問題,而某個 library 更新之後又會有機會使原有設定失效。

比較之下,Virtual box 提供的 shared folders 就可靠得多:

mcore3d /home/ricky/mcore3d vboxsf defaults 0 0

2009年12月2日星期三

為灰鼠捉蟲

使用了 Squirrel 已好一些日子,終於給我遇到了臭蟲。這臭蟲的表徵是不確定的行為,debug/release/,run-with/without debugger 都會產生不同的結果。這一類不確定的行為不是多緒就是錯誤記憶體存取做成,而面對著陌生的 code base,最好的捉蟲方法就是使用專門的工具,如 Intel Parallel Studio 提供的 Parallel Inspector

最終找到錯誤源於一處被 reference 的記憶體給 realloc 了;餘下的就交給 Squirrel 作者處理

Invalid memory access

2009年10月21日星期三

Squirrel remote debugger

以下是完成了大約三分之一的 Squirrel 遠程除錯器。當中的製作過程比想像中簡單,皆因 Squirrel 已有一個除錯伺服器的實作 sqdbg,而我需要做的只是客戶端與 Gui。

Sqdbg 的編程界面僅有四個函數,使用 Xml 來進行客戶端溝通。如今加減中斷點,下一行,跳出跳入和暫停繼續等等已經完成,接下來還有堆疊,變量監視等工夫。

除了結合自家的遊戲引擎,它也會獨立出來貢獻給 Squirrel 社群。

SqdbgClient

2009年9月17日星期四

In-Game C++ Memory Profiler

忙碌了兩個多月,這篇有可出現在 Gems 系列的 "In-Game Memory Profiler" 終於完成。製作這類 Profiler 已經不知多少次了,但這次是最滿意的;可以媲美需付款的產品 (其實類似的 C++ memory profiler 產品真的少之又少),甚至更好。就是它,讓我知道 Bullet 每次 step simulation 都有好幾千個即用即棄的 memory allocation/de-allocation;後來我把它的 default pool size 增大,問題也就解決了。

In-Game-Memory-Profiler

2009年9月5日星期六

弱指針的重要性

雖然有許多有關智能指針 (Smart pointer) 的文章和範例,但關於弱指針的就相對寥寥無幾。縱然有所提及,也只是草草幾筆說弱指針是用來避免 "循環引用 (Cyclic reference)" 云云。就此問題我嘗試用一些例子去補充說明弱指針何時用,為什麼用。

首先讓我們看一個遊戲引擎中典型的資料管理系統例子:

因為要節省內存的需求,Texture (紋理) 被設計成能夠給多個 Model (三維模型) 共享。而 Texture 的存在目的,就是留給 Model 作為繪畫時使用;明顯地,Model 可用 Smart Pointer 來引用 Texture;相對地 NameToTextureMap (方便用名稱來作資料搜尋) 該使用甚麼來引用 Texture 就沒有那麼明顯了。先看看使用不同的指針會有什麼的後果吧。
  • 裸指針 - 這將會是災難性的選擇,因為一旦再沒有 Model 引用 Texture, NameToTextureMap 就會在毫無知會的情況下指向一件非法的物件。

  • 智能指針 - 為了解決裸指針的問題,選擇智能指針像是合理的做法。不過請大家想一想,一旦 NameToTextureMap 引用了 Texture 以後,何時才可刪除引用呢?假如不去刪除引用,Texture 的生命期就會與 NameToTextureMap 同步化,失去了原先使用智能指針作為純粹被 Model 共享的意義。
這時就是讓弱指針發揮的時候。弱指針的特性就是當它指向的物件被銷毀之時,弱指針的值就會瞬間變為零;因此,讓 NameToTextureMap 使用弱指針,再定期把零值的弱指針由容器中刪除,以上的 UML 設計就可忠實地實作出來。

總結來說,智能指針旨不在於提供程序員不加思索就可使用的工具,它是一種能幫助你正確地實行擁有權共享的工具。就算是聲稱擁有自動內存管理的語言如 Java, C#, Python, Ruby 等等都一定會有弱指針這個概念,否則內存洩漏是很難避免的。假若你在開發 C++ 程序的時候遇到許多關於物件擁有權的問題,請記得弱指針的存在。

2009年9月3日星期四

Syntax highlight in Notepad++

從互聯網找到個給 Squirrel 語言著色的 Notepad++ config 檔案,也可作為日後 MCore3D Studio 結合 Scintilla Control 的藍圖。把以下的 Xml 儲存為 "userDefineLang.xml" 再放入 Notepad++ 的安置目錄就行了。

<NotepadPlus>
<UserLang name="Squirrel" ext="nut">
<Settings>
<Global caseIgnored="no" />
<TreatAsSymbol comment="yes" commentLine="yes" />
<Prefix words1="no" words2="no" words3="no" words4="no" />
</Settings>
<KeywordLists>
<Keywords name="Delimiters">&quot;00&quot;00</Keywords>
<Keywords name="Folder+"></Keywords>
<Keywords name="Folder-"></Keywords>
<Keywords name="Operators">- ! ( ) , . : ; ? [ ] { } + &lt; = &gt;</Keywords>
<Keywords name="Comment">1/* 2*/ 0//</Keywords>
<Keywords name="Words1">break case catch class clone continue const default delegate delete else enum extends for function if in null resume return switch this throw try typeof parent yield constructor instanceof true false static do while foreach</Keywords>
<Keywords name="Words2">local</Keywords>
<Keywords name="Words3">ARGS</Keywords>
<Keywords name="Words4"></Keywords>
</KeywordLists>
<Styles>
<WordsStyle name="DEFAULT" styleID="11" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" />
<WordsStyle name="FOLDEROPEN" styleID="12" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" />
<WordsStyle name="FOLDERCLOSE" styleID="13" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" />
<WordsStyle name="KEYWORD1" styleID="5" fgColor="0000FF" bgColor="FFFFFF" fontName="" fontStyle="1" />
<WordsStyle name="KEYWORD2" styleID="6" fgColor="006262" bgColor="FFFFFF" fontName="" fontStyle="1" />
<WordsStyle name="KEYWORD3" styleID="7" fgColor="007777" bgColor="FFFFFF" fontName="" fontStyle="3" />
<WordsStyle name="KEYWORD4" styleID="8" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" />
<WordsStyle name="COMMENT" styleID="1" fgColor="008000" bgColor="FFFFFF" fontName="" fontStyle="2" />
<WordsStyle name="COMMENT LINE" styleID="2" fgColor="008000" bgColor="FFFFFF" fontName="" fontStyle="2" />
<WordsStyle name="NUMBER" styleID="4" fgColor="FF8000" bgColor="FFFFFF" fontName="" fontStyle="0" />
<WordsStyle name="OPERATOR" styleID="10" fgColor="000080" bgColor="FFFFFF" fontName="" fontStyle="1" />
<WordsStyle name="DELIMINER1" styleID="14" fgColor="7E7E7E" bgColor="FFFFFF" fontName="" fontStyle="2" />
<WordsStyle name="DELIMINER2" styleID="15" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" />
<WordsStyle name="DELIMINER3" styleID="16" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" />
</Styles>
</UserLang>
</NotepadPlus>