<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Kerry D. Wong &#187; Linux/BSD</title>
	<atom:link href="http://www.kerrywong.com/category/linuxbsd/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.kerrywong.com</link>
	<description></description>
	<lastBuildDate>Fri, 03 Sep 2010 00:51:09 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>Apache2 Logrotate</title>
		<link>http://www.kerrywong.com/2010/08/11/apache2-logrotate/</link>
		<comments>http://www.kerrywong.com/2010/08/11/apache2-logrotate/#comments</comments>
		<pubDate>Thu, 12 Aug 2010 00:14:04 +0000</pubDate>
		<dc:creator>kwong</dc:creator>
				<category><![CDATA[Linux/BSD]]></category>
		<category><![CDATA[Apache]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Logrotate]]></category>
		<category><![CDATA[Ubuntu]]></category>

		<guid isPermaLink="false">http://www.kerrywong.com/?p=2422</guid>
		<description><![CDATA[Whenever I setup a new web server, I always like the idea of keeping the Apache logs roated on a daily basis. While the procedures for changing the log rotate frequency is pretty straight forward, I have seen a lot of people having problems with it so I decided to document it. To change the [...]]]></description>
			<content:encoded><![CDATA[<p>Whenever I setup a new web server, I always like the idea of keeping the <a href="http://www.apache.org">Apache</a> logs roated on a daily basis. While the procedures for changing the log rotate frequency is pretty straight forward, I have seen a lot of people having problems with it so I decided to document it.<span id="more-2422"></span></p>
<p>To change the Apache log rotate frequency, you would want to change the apache2 configuration file located under /etc/logrotate.d. Configurations located in this directory overrides the default settings in /etc/logrotate.conf (the configurations in /etc/logrotate.d are included in logrotate.conf)</p>
<p>Here&#8217;s what my settings look like (note that I had commented out compress option so that all the logs can be easily browsed):</p>
<blockquote><p>
/var/log/apache2/*.log {<br />
        daily<br />
        missingok<br />
        rotate 365<br />
        #compress<br />
        #delaycompress<br />
        notifempty<br />
        create 640 root adm<br />
        sharedscripts<br />
        postrotate<br />
                if [ -f "`. /etc/apache2/envvars ; echo ${APACHE_PID_FILE:-/var/run/apache2.pid}`" ]; then<br />
                        /etc/init.d/apache2 reload > /dev/null<br />
                fi<br />
        endscript<br />
}
</p></blockquote>
<h3>Common problem</h3>
<p>A common problem after changing the log rotate frequency is that the Apache logs do not appear to be rotating daily even though the configuration file had been changed correctly.</p>
<p>This seems to be a known issue with logrotate. If you read the manual for logrotate you&#8217;d see the following explanation:</p>
<blockquote><p>
       -f, &#8211;force<br />
              Tells logrotate to force the rotation, even if it doesn&#8217;t  think<br />
              this  is  necessary.   Sometimes this is useful after adding new<br />
              entries to a logrotate config file, or if  old  log  files  have<br />
              been removed by hand, as the new files will be created, and<br />
              logging will continue correctly.
</p></blockquote>
<p>Since the default cron daily job does not force the log rotation, you may want to add -f parameter to /etc/cron.daily/logrotate:</p>
<blockquote><p>
#!/bin/sh</p>
<p>test -x /usr/sbin/logrotate || exit 0<br />
/usr/sbin/logrotate -f /etc/logrotate.conf
</p></blockquote>
<p>With this change, the logs will be forced to rotate daily according to the setup.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kerrywong.com/2010/08/11/apache2-logrotate/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Weekend Server Upgrade</title>
		<link>http://www.kerrywong.com/2010/08/01/weekend-server-upgrade/</link>
		<comments>http://www.kerrywong.com/2010/08/01/weekend-server-upgrade/#comments</comments>
		<pubDate>Sun, 01 Aug 2010 23:47:59 +0000</pubDate>
		<dc:creator>kwong</dc:creator>
				<category><![CDATA[Linux/BSD]]></category>
		<category><![CDATA[Miscellaneous]]></category>
		<category><![CDATA[Ubuntu 10.04 Server]]></category>

		<guid isPermaLink="false">http://www.kerrywong.com/?p=2407</guid>
		<description><![CDATA[I have finally got around to upgrade my web server. The server I have been using was an old Pentium III about 10 years old. It had been serving my website quite well over the years till recently. Due to the small amount of RAM (512MB), that server was no longer able to handle the [...]]]></description>
			<content:encoded><![CDATA[<p>I have finally got around to upgrade my web server. The server I have been using was an old Pentium III about 10 years old. It had been serving my website quite well over the years till recently. <span id="more-2407"></span>Due to the small amount of RAM (512MB), that server was no longer able to handle the ever increasing net traffic and had been crashing with out-of-memory errors quite often.</p>
<p>Luckily, I got a fully equipped hand-me-down Compaq (HP) ProLiant DL360 G2 server and I decided to migrate my website over during the weekend.</p>
<p>Even though this server is a few generations behind, it has dual Pentium III S 1.4 GHz processors, 15,000 RMP SCSI harddrive and 4GB of RAM which is more than plenty to meet my near future needs.</p>
<p>Since my web server was running on Ubuntu Server 8.04, this hardware upgrade also gives me an opportunity to upgrade the OS to the latest version of Ubuntu Server (10.04) which is also an LTS version. </p>
<p>With the new hardware and software, the site should be running rather smoothly for the next couple of years&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kerrywong.com/2010/08/01/weekend-server-upgrade/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Weekend Ubuntu Upgrade</title>
		<link>http://www.kerrywong.com/2009/11/01/weekend-ubuntu-upgrade/</link>
		<comments>http://www.kerrywong.com/2009/11/01/weekend-ubuntu-upgrade/#comments</comments>
		<pubDate>Mon, 02 Nov 2009 00:52:16 +0000</pubDate>
		<dc:creator>kwong</dc:creator>
				<category><![CDATA[Linux/BSD]]></category>
		<category><![CDATA[Ubuntu]]></category>

		<guid isPermaLink="false">http://www.kerrywong.com/?p=1504</guid>
		<description><![CDATA[Karmic Koala was released into the wild last Thursday, so naturally I was going to upgrade my desktop installations at home. In this past I had been doing fresh installs whenever there was a new release, since there might be some issues with the new Ext4 file system, I decided to upgrade my current 9.04 [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://releases.ubuntu.com/karmic/">Karmic Koala</a> was released into the wild last Thursday, so naturally I was going to upgrade my desktop installations at home. In this past I had been doing fresh installs whenever there was a new release, since there might be <a href="https://bugs.launchpad.net/ubuntu/+source/linux/+bug/453579">some issues</a> with the new Ext4 file system, I decided to upgrade my current 9.04 installation in place and perform a fresh installation when the Ext4 file system stabilizes.<span id="more-1504"></span></p>
<p>Since I had quite a few unofficial packages installed on my primary PC, I was not quite sure how the upgrade would turn out and I was prepared to see a few gotchas at some point. But the upgrade process was surprisingly smooth (albeit slow since all new versions of the installed packages had to be fetched first), and when the upgrade process is done I was able to boot into the new desktop. The only thing I needed to do was to re-install the <a href="http://www.nvidia.com/object/cuda_get.html">CUDA driver</a> (190.18), but this was to be expected since every major kernel updates would require the CUDA driver to be re-installed. I certainly hope that NVidia would make their proprietary binary drivers  more robust by detecting kernel changes and reinstall automatically if necessary as opposed to require the end user&#8217;s manual intervention. </p>
<p>For my other machine running Ubuntu 9.04, the upgrade was equally smooth. One of the problems I ran into there was due to my VMWare Server installation. As it turned out, there are <a href="http://www.uluga.ubuntuforums.org/showthread.php?t=1305254">some issues</a> with the default installation package that came with VMWare Server 2. The problem however can be resolved with <a href="http://radu.cotescu.com/2009/10/30/how-to-install-vmware-server-2-0-x-on-ubuntu-9-10-karmic-koala/">Radu&#8217;s installation script</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kerrywong.com/2009/11/01/weekend-ubuntu-upgrade/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Windows Administrative Share With Samba</title>
		<link>http://www.kerrywong.com/2009/10/04/windows-administrative-share-with-samba/</link>
		<comments>http://www.kerrywong.com/2009/10/04/windows-administrative-share-with-samba/#comments</comments>
		<pubDate>Sun, 04 Oct 2009 17:55:33 +0000</pubDate>
		<dc:creator>kwong</dc:creator>
				<category><![CDATA[Linux/BSD]]></category>
		<category><![CDATA[Samba]]></category>

		<guid isPermaLink="false">http://www.kerrywong.com/?p=1482</guid>
		<description><![CDATA[In a heterogeneous computing environment (e.g. Linux and Windows), it is necessary to use Samba to share files between the Linux systems and Windows systems. Due to the security enhancements in Windows XP SP2 and above however, the administrative share access (e.g. \\{computer name}\c$ is disabled by default. To enable administrative share access from Linux [...]]]></description>
			<content:encoded><![CDATA[<p>In a heterogeneous computing environment (e.g. Linux and Windows), it is necessary to use <a href="http://samba.org/">Samba</a> to share files between the Linux systems and Windows systems. Due to the security enhancements in Windows XP SP2 and above however, the administrative share access (e.g. \\{<strong><em>computer name</em></strong>}\c$ is disabled by default.<span id="more-1482"></span></p>
<p>To enable administrative share access from Linux hosts, we need to change the Windows machine&#8217;s security policy. My assumption is that you are using Windows XP. You may need to make further changes depending on the version of Windows you are using.</p>
<p>The security policy snap-in is located at </p>
<blockquote><p>c:\windows\system32\secpol.msc</p></blockquote>
<p>Select local policies and then Security Options:<br />
<a href="http://www.kerrywong.com/blog/wp-content/uploads/2009/10/security_setting.gif"><img src="http://www.kerrywong.com/blog/wp-content/uploads/2009/10/security_setting.gif" alt="security_setting" title="security_setting" width="600" height="424" class="aligncenter size-full wp-image-1496" /></a><br />
Then select sharing and security model for local accounts:<br />
<a href="http://www.kerrywong.com/blog/wp-content/uploads/2009/10/security_setting_1.gif"><img src="http://www.kerrywong.com/blog/wp-content/uploads/2009/10/security_setting_1.gif" alt="security_setting_1" title="security_setting_1" width="418" height="505" class="aligncenter size-full wp-image-1495" /></a></p>
<p>Under the enhanced security scheme, &#8220;Guest only: local users authenticate as guests&#8221; is chosen. This policy means that administrative network access is treated as if it was done with a guest account and therefore you would get permission denied error when trying to access an administrative share &#8212; even though your account has administrative rights locally.</p>
<p>To allow administrative share on Samba network, you can simply select the other option &#8220;Classic: local users authenticate as themselves&#8221;.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kerrywong.com/2009/10/04/windows-administrative-share-with-samba/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Monodevelop on Ubuntu 9.04</title>
		<link>http://www.kerrywong.com/2009/07/22/monodevelop-on-ubuntu-9-04/</link>
		<comments>http://www.kerrywong.com/2009/07/22/monodevelop-on-ubuntu-9-04/#comments</comments>
		<pubDate>Wed, 22 Jul 2009 04:00:04 +0000</pubDate>
		<dc:creator>kwong</dc:creator>
				<category><![CDATA[Linux/BSD]]></category>
		<category><![CDATA[Miscellaneous]]></category>
		<category><![CDATA[C Sharp (C#)]]></category>
		<category><![CDATA[Mono]]></category>
		<category><![CDATA[Ubuntu]]></category>

		<guid isPermaLink="false">http://www.kerrywong.com/?p=1408</guid>
		<description><![CDATA[I am running Ubuntu 9.04 64bit. One thing I noticed is that the integrated debugger is behaving quite flaky for the included Monodevelop 2.0 package. After some research, it turned out that a lot of people are having similar issues. So I followed the instructions given in these two blogs: Install Mono 2.4 on Ubuntu, [...]]]></description>
			<content:encoded><![CDATA[<p>I am running Ubuntu 9.04 64bit. One thing I noticed is that the integrated debugger is behaving quite flaky for the included <a href="http://monodevelop.com/">Monodevelop 2.0</a> package. After some research, it turned out that a lot of people are having similar issues.<span id="more-1408"></span></p>
<p>So I followed the instructions given in these two blogs: <a href="http://blog.ruski.co.za/page/Install-Mono-on-Ubuntu.aspx">Install Mono 2.4 on Ubuntu</a>,<br />
<a href="http://synpl.blogspot.com/2009/07/building-mono-and-monodevelop-from.html">Building Mono and MonoDevelop from source on Ubuntu 9.04 64bit</a> to re-install Monodevelop 2.0 and its dependencies from the source. </p>
<p>One thing you will need to pay special attention to is that for the monodevelop-debugger-mdb-2.0 to compile successfully, you will need mono-debugger version 2.4, instead of the latest version 2.4.1. Otherwise you will receive an error on <em>Thread.AbortInvocation</em> as there is a breaking change in mono-debugger 2.4.1 which added a parameter to the method (see <a href="http://lists.ximian.com/pipermail/mono-patches/2009-May/149225.html">mono debugger 2.4.1 change log</a>).</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kerrywong.com/2009/07/22/monodevelop-on-ubuntu-9-04/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>C++ Recursive Directory Search Under Linux</title>
		<link>http://www.kerrywong.com/2009/06/12/c-recursive-directory-search-under-linux/</link>
		<comments>http://www.kerrywong.com/2009/06/12/c-recursive-directory-search-under-linux/#comments</comments>
		<pubDate>Fri, 12 Jun 2009 21:55:29 +0000</pubDate>
		<dc:creator>kwong</dc:creator>
				<category><![CDATA[Coding]]></category>
		<category><![CDATA[Linux/BSD]]></category>
		<category><![CDATA[boost]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[Linux]]></category>

		<guid isPermaLink="false">http://www.kerrywong.com/?p=1154</guid>
		<description><![CDATA[I was trying to search for some code examples on how to do a recursive directory search under Linux using C++ the other day. But to my surprise, I could not find any place that offers a complete example. So I decided to post my code here after I created my own and hopefully you [...]]]></description>
			<content:encoded><![CDATA[<p>I was trying to search for some code examples on how to do a recursive directory search under Linux using C++ the other day. But to my surprise, I could not find any place that offers a complete example. So I decided to post my code here after I created my own and hopefully you will find it helpful.<span id="more-1154"></span></p>
<p>For those who are impatient, the function to perform recursive directory search is here:</p>
<pre class="brush: cpp;">
#include &lt;sys/types.h&gt;
#include &lt;sys/stat.h&gt;
#include &lt;dirent.h&gt;
#include &lt;errno.h&gt;
#include &lt;vector&gt;
#include &lt;string&gt;
#include &lt;iostream&gt;
#include &lt;boost/regex.hpp&gt;

void GetFileListing(vector&lt;string&gt; &amp;files, string dir, string filter, bool ignoreCase) {
    DIR *d;
    if ((d = opendir(dir.c_str())) == NULL) return;
    if (dir.at(dir.length() - 1) != '/') dir += &quot;/&quot;;

    struct dirent *dent;
    struct stat st;
    boost::regex exp;

    if (ignoreCase)
        exp.set_expression(filter, boost::regex_constants::icase);
    else
        exp.set_expression(filter);

    while ((dent = readdir(d)) != NULL) {
        string path = dir;

        if (string(dent-&gt;d_name) != &quot;.&quot; &amp;&amp; string(dent-&gt;d_name) != &quot;..&quot;) {
            path += string(dent-&gt;d_name);
            const char *p = path.c_str();
            lstat(p, &amp;st);

            if (S_ISDIR(st.st_mode)) {
                GetFiles(files, (path + string(&quot;/&quot;)).c_str(), filter, ignoreCase);
            } else {
                if (filter == &quot;.*&quot;) {
                    files.push_back(path);
                } else {
                    if (boost::regex_match(string(dent-&gt;d_name), exp)) files.push_back(path);
                }
            }
        }
    }

    closedir(d);
}
</pre>
<p>I used <a href="http://www.boost.org">boost library</a> to perform regular expression matches for file names. If you just want to obtain a listing of all the files, you can do without using the boost library.</p>
<p>The following code snippet demonstrates how to use the function. The results are stored in a vector container passed in. Note that the &#8220;filter&#8221; parameter needs standard regular expressions (so if you are looking for any files, the expression should be  .* instead of just *) to work properly.</p>
<p>The code should be pretty self-explanatory. If <em>ignoreCase</em> is set to true, then the match will be case-insensitive.</p>
<pre class="brush: cpp;">
vector&lt;string&gt; files;

FileUtils::GetFiles(files,&quot;/tmp&quot;, &quot;.*&quot;, true);
for (int i = 0 ; i &lt; files.size(); i++) {
    cout &lt;&lt; files[i] &lt;&lt; endl;
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.kerrywong.com/2009/06/12/c-recursive-directory-search-under-linux/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Ubuntu 9.04 on My Main PC</title>
		<link>http://www.kerrywong.com/2009/05/13/ubuntu-904-on-my-main-pc/</link>
		<comments>http://www.kerrywong.com/2009/05/13/ubuntu-904-on-my-main-pc/#comments</comments>
		<pubDate>Wed, 13 May 2009 22:35:07 +0000</pubDate>
		<dc:creator>kwong</dc:creator>
				<category><![CDATA[Linux/BSD]]></category>
		<category><![CDATA[Miscellaneous]]></category>
		<category><![CDATA[Dual Sound Card]]></category>
		<category><![CDATA[Ubuntu]]></category>
		<category><![CDATA[WD1001FALS]]></category>
		<category><![CDATA[WD6400AAKS]]></category>

		<guid isPermaLink="false">http://www.kerrywong.com/?p=1068</guid>
		<description><![CDATA[Last month,I upgraded one of my PCs from Ubuntu 8.10 to Ubuntu 9.04 and everything went rather smoothly. Since my main PC is running Ubuntu 8.04 (LTS) and there is no option to upgrade to 9.04 directly without going through 8.10, so I decided to try a fresh installation. I have a lot of important [...]]]></description>
			<content:encoded><![CDATA[<p>Last month,I <a href="/2009/04/23/the-jaunty-jackalope/">upgraded one of my PCs</a> from Ubuntu 8.10 to Ubuntu 9.04 and everything went rather smoothly. Since my main PC is running Ubuntu 8.04 (LTS) and there is no option to upgrade to 9.04 directly without going through 8.10, so I decided to try a fresh installation.<span id="more-1068"></span></p>
<p>I have a lot of important programs on my main PC and I wanted to be extra cautious, so instead of backing up the working harddrive and install Ubuntu 9.04 on top of it, I decided to install it on a new harddrive. This way, I could always fall back to my currently working Ubuntu 8.04 installation should something not working out the way I wanted.</p>
<p>The drive I chose to install Ubuntu 9.04 on was a <a href="http://www.wdc.com/en/products/products.asp?driveid=488">Western Digital Caviar Black WD1001FALS (1TB)</a>.  The installation went smoothly and ten minutes later I was able to use the brand new Ubuntu 9.04 desktop.</p>
<p>It took me a while to customize the desktop, install the applications I needed and migrate the data over. But everything pretty much worked out-of-box. And it certainly feels a lot faster than Ubuntu 8.04.</p>
<p>The only minor &#8220;glitch&#8221; I ran into was my dual sound card setup. As I <a href="/2008/12/09/a-dual-sound-card-setup/">mentioned in an earlier post</a>, I needed to use a second sound card because my on board Intel sound board does not have a MIDI/Game port. I needed that port to hook up my old MIDI keyboard. After the installation though, the sound playback would only go through the Sound Blaster sound card, not the on board Intel HDA sound card. There are various posts (<a href="http://ubuntuforums.org/showthread.php?t=205449">1</a>,<a href="https://help.ubuntu.com/community/SoundTroubleshooting">2</a>,<a href="http://ubuntuforums.org/showthread.php?t=331072">3</a>,<a href="https://answers.launchpad.net/ubuntu/+question/13897">4</a>,<a href="http://www.linuxquestions.org/questions/ubuntu-63/how-do-you-change-the-default-sound-card-in-kubuntu-499520/">5</a>) suggesting how to fix this issue but unfortunately none of them worked in my case. No matter which sound card I initialized first, I always ended up with the Sound Blaster Live card working but not the on-board Intel sound card. Here is how I fixed it: I removed the Sound Blaster Live card first and booted up with just the on board sound. After making sure that the on board sound worked properly, I plugged in the Sound Blaster PCI card and then everything worked as desired (the sound now comes from the on-board Intel HDA instead of the Sound Blaster card).</p>
<p>As a side note, it seems that WD1001FALS does have some performance advantage over WD6400AAKS (which is the harddrive I run Ubuntu 8.04 on) even though their platter densities are quite comparable (333MB/platter versus 320MB/platter). </p>
]]></content:encoded>
			<wfw:commentRss>http://www.kerrywong.com/2009/05/13/ubuntu-904-on-my-main-pc/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Canny Edge Detection Auto Thresholding</title>
		<link>http://www.kerrywong.com/2009/05/07/canny-edge-detection-auto-thresholding/</link>
		<comments>http://www.kerrywong.com/2009/05/07/canny-edge-detection-auto-thresholding/#comments</comments>
		<pubDate>Fri, 08 May 2009 02:15:41 +0000</pubDate>
		<dc:creator>kwong</dc:creator>
				<category><![CDATA[Coding]]></category>
		<category><![CDATA[Linux/BSD]]></category>
		<category><![CDATA[Algorithm]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[Image Processing]]></category>

		<guid isPermaLink="false">http://www.kerrywong.com/?p=999</guid>
		<description><![CDATA[In the example I gave in &#8220;Interfacing IPP with Magick++&#8220;, I illustrated how to use Intel’s Integrated Performance Primitives (IPP) to perform edge detection. One issue with Canny edge detection algorithm is that we need to specify a high threshold and a low threshold. How to select those threshold values affect the quality of the [...]]]></description>
			<content:encoded><![CDATA[<p>In the example I gave in &#8220;<a href="/2009/03/17/interfacing-ipp-with-magick/">Interfacing IPP with Magick++</a>&#8220;, I illustrated how to use <a href="http://software.intel.com/en-us/intel-ipp/">Intel’s Integrated Performance Primitives (IPP)</a> to perform edge detection. One issue with <a href="http://en.wikipedia.org/wiki/Canny_edge_detector">Canny edge detection</a> algorithm is that we need to specify a high threshold and a low threshold. How to select those threshold values affect the quality of the detected edge greatly. And in my previous example, the threshold values were chosen manually. In this blog post, I will examine a couple of simple methods that can be used to automatically determine the threshold values.<span id="more-999"></span></p>
<p>The simplest way is to use the mean value of the gray scale image pixel values. As a rule of thumb, we set the low threshold to 0.66*[mean value] and set the high threshold to 1.33*[mean value]. Another way is to use the median color in the gray scale image and uses 0.66*[median value] and 1.33*[median value] accordingly. For typical images, these two methods achieve comparable results.</p>
<p>For example, the following shows the picture of a building along with its histogram (original image from <a href="ftp://ftp.research.microsoft.com/pub/download/orid">Microsoft Research Digital Image</a>. Please see <a href="http://research.microsoft.com/en-us/um/people/antcrim/data_objrec/msr%20cambridge%20eula%20for%20digital%20images_download.rtf">Microsoft Research Digital Image License Agreement</a> for more information):</p>
<table>
<tr>
<td>
<div id="attachment_1011" class="wp-caption aligncenter" style="width: 330px"><a href="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/building_gray.jpg"><img src="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/building_gray.jpg" alt="Building" title="Building" width="320" height="240" class="size-full wp-image-1011" /></a><p class="wp-caption-text">Building</p></div>
</td>
<td>
<div id="attachment_1012" class="wp-caption aligncenter" style="width: 330px"><a href="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/building_hist.png"><img src="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/building_hist.png" alt="Building Histogram" title="Building Histogram" width="320" height="240" class="size-full wp-image-1012" /></a><p class="wp-caption-text">Building Histogram</p></div>
</td>
</tr>
</table>
<p>The following shows the edge detection results using Canny algorithm (left image uses mean value auto-thresholding, right image uses median value auto-thresholding) and the results exhibit very little visible differences. </p>
<table>
<tr>
<td>
<div id="attachment_1006" class="wp-caption aligncenter" style="width: 330px"><a href="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/building_canny_mean.jpg"><img src="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/building_canny_mean.jpg" alt="Building (Canny Mean)" title="Building (Canny Mean)" width="320" height="240" class="size-full wp-image-1006" /></a><p class="wp-caption-text">Building (Canny Mean)</p></div>
</td>
<td>
<div id="attachment_1007" class="wp-caption aligncenter" style="width: 330px"><a href="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/building_canny_median.jpg"><img src="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/building_canny_median.jpg" alt="Building (Canny Median)" title="Building (Canny Median)" width="320" height="240" class="size-full wp-image-1007" /></a><p class="wp-caption-text">Building (Canny Median)</p></div>
</td>
</tr>
</table>
<p>However, for images has non-equalized histogram (see the picture of cloud and its histogram below): </p>
<table>
<tr>
<td>
<div id="attachment_1018" class="wp-caption aligncenter" style="width: 250px"><a href="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/cloud_gray.jpg"><img src="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/cloud_gray.jpg" alt="Cloud" title="Cloud" width="240" height="320" class="size-full wp-image-1018" /></a><p class="wp-caption-text">Cloud</p></div>
</td>
<td>
<div id="attachment_1019" class="wp-caption aligncenter" style="width: 330px"><a href="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/cloud_hist.png"><img src="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/cloud_hist.png" alt="Cloud Histogram" title="Cloud Histogram" width="320" height="240" class="size-full wp-image-1019" /></a><p class="wp-caption-text">Cloud Histogram</p></div>
</td>
</tr>
</table>
<p>Canny Edge detection result based on mean value auto-thresholding is pretty poor (see image on the left below), while edge detection based on median value auto-thresholding achieved much better result (see image on the right below)</p>
<table>
<tr>
<td>
<div id="attachment_1021" class="wp-caption aligncenter" style="width: 250px"><a href="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/cloud_canny_mean.jpg"><img src="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/cloud_canny_mean.jpg" alt="Cloud (Canny Mean)" title="Cloud (Canny Mean)" width="240" height="320" class="size-full wp-image-1021" /></a><p class="wp-caption-text">Cloud (Canny Mean)</p></div>
</td>
<td>
<div id="attachment_1024" class="wp-caption aligncenter" style="width: 250px"><a href="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/cloud_eq_canny_median.jpg"><img src="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/cloud_eq_canny_median.jpg" alt="Cloud (Canny Median)" title="Cloud (Canny Median)" width="240" height="320" class="size-full wp-image-1024" /></a><p class="wp-caption-text">Cloud (Canny Median)</p></div>
</td>
</tr>
</table>
<p>Alternatively, we could have performed histogram equalization on the image first before applying Canny edge detection with mean auto-thresholding:</p>
<table>
<tr>
<td>
<div id="attachment_1026" class="wp-caption aligncenter" style="width: 250px"><a href="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/cloud_eq.png"><img src="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/cloud_eq.png" alt="Cloud Equalized" title="Cloud Equalized" width="240" height="320" class="size-full wp-image-1026" /></a><p class="wp-caption-text">Cloud Equalized</p></div>
</td>
<td>
<div id="attachment_1028" class="wp-caption aligncenter" style="width: 330px"><a href="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/cloud_hist_eq.png"><img src="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/cloud_hist_eq.png" alt="Cloud Equalized Histogram" title="Cloud Equalized Histogram" width="320" height="240" class="size-full wp-image-1028" /></a><p class="wp-caption-text">Cloud Equalized Histogram</p></div>
</td>
</tr>
</table>
<p>And after image equalization, both mean and median value auto-thresholding achieved similar results.</p>
<table>
<tr>
<td>
<div id="attachment_1030" class="wp-caption aligncenter" style="width: 250px"><a href="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/cloud_eq_canny_mean.jpg"><img src="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/cloud_eq_canny_mean.jpg" alt="Cloud Equalized (Canny Mean)" title="Cloud Equalized (Canny Mean)" width="240" height="320" class="size-full wp-image-1030" /></a><p class="wp-caption-text">Cloud Equalized (Canny Mean)</p></div>
</td>
<td>
<div id="attachment_1031" class="wp-caption aligncenter" style="width: 250px"><a href="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/cloud_eq_canny_median.jpg"><img src="http://www.kerrywong.com/blog/wp-content/uploads/2009/05/cloud_eq_canny_median.jpg" alt="Cloud Equalized (Canny Median)" title="Cloud Equalized (Canny Median)" width="240" height="320" class="size-full wp-image-1030" /></a><p class="wp-caption-text">Cloud Equalized (Canny Median)</p></div>
</td>
</tr>
</table>
<p>So the Canny edge detection using median value auto-thresholding seems to adapt to different types of images very well (note selecting the median value selection can be thought as equalizing the histogram, except that the pixel values are not changed during such operation).</p>
<p>The following is the code listing for the histogram calculation using IPP (based on the <a href="/2009/04/10/an-image-class-based-on-ipp/">image class I created earlier</a>)</p>
<pre class="brush: cpp;">
/** @brief Get the min max mean value statistics for the current image
 *   @param min, max, mean: these are output parameters that are passed
 *         back by reference.
 */
void IPPGrayImage::MinMaxMean(float&amp; min, float&amp; max, double&amp; mean)
{
    IppStatus sts;
    IppiSize origImgSize = {_width, _height};

    sts = ippiMinMax_32f_C1R(_imgBuffer, _width * PIXEL_SIZE, origImgSize, &amp;min, &amp;max);
    assert(sts == ippStsNoErr);
    sts = ippiMean_32f_C1R(_imgBuffer, _width * PIXEL_SIZE, origImgSize, &amp;mean, ippAlgHintFast);
    assert(sts == ippStsNoErr);
}

/** @brief Calculate the histogram of the image
 *   @param nLevel: the number of bins in the histogram
 *        levels: this is the optional levels user can pass in (histogram will be then
 *                calculated with these levels instead of the uniform levels by default.
 *   @return an integer array which contains the histogram.
 *   @note the histogram is by default calculated using uniform bins across the color range.
 */
unsigned int * IPPGrayImage::GetHistogram(unsigned int nLevel, float levels[])
{
    IppStatus sts;
    Ipp32f* l = new Ipp32f[nLevel];
    IppiSize origImgSize = {_width, _height};

    unsigned int* bins = new unsigned int[nLevel - 1];

    float minVal = 0, maxVal = 0, stepVal = 0;
    double meanVal = 0;
    if (levels != NULL)
    {
        for (unsigned int i = 0; i &lt; nLevel; i++)
        {
            l[i] = levels[i];
        }
    }
    else
    {
        MinMaxMean(minVal, maxVal, meanVal);
        stepVal = (maxVal - minVal) / (float) nLevel;

        for (unsigned int i = 0; i &lt; nLevel; i++)
        {
            l[i] = minVal + stepVal * i;
        }
    }

    sts = ippiHistogramRange_32f_C1R(_imgBuffer, _width * PIXEL_SIZE, origImgSize, (Ipp32s*) bins, (Ipp32f*) l, nLevel);
    return bins;
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.kerrywong.com/2009/05/07/canny-edge-detection-auto-thresholding/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Kubuntu 9.04 Test Drive</title>
		<link>http://www.kerrywong.com/2009/05/01/kbuntu-904-test-drive/</link>
		<comments>http://www.kerrywong.com/2009/05/01/kbuntu-904-test-drive/#comments</comments>
		<pubDate>Sat, 02 May 2009 02:20:05 +0000</pubDate>
		<dc:creator>kwong</dc:creator>
				<category><![CDATA[Linux/BSD]]></category>
		<category><![CDATA[Gnome]]></category>
		<category><![CDATA[KDE]]></category>
		<category><![CDATA[Kubuntu]]></category>

		<guid isPermaLink="false">http://www.kerrywong.com/?p=989</guid>
		<description><![CDATA[I was traditionally a KDE user till KDE 4.0 came along. While many users are excited about many of the new features (e.g. Widgets) added in KDE 4, I am simply no big fan of fancy user interfaces. So I have been using GNOME ever since. Of course the beauty of Linux is that running [...]]]></description>
			<content:encoded><![CDATA[<p>I was traditionally a <a href="http://www.kde.org">KDE</a> user till KDE 4.0 came along. While many users are excited about many of the new features (e.g. Widgets) added in KDE 4, I am simply no big fan of fancy user interfaces. So I have been using <a href="http://www.gnome.org/">GNOME</a> ever since. Of course the beauty of Linux is that running one windows manager does not prevent you from running applications native to another windows manager. <span id="more-989"></span></p>
<p>As I mentioned <a href="/2009/04/23/the-jaunty-jackalope/">a few days ago</a>, I installed Ubuntu 9.04 on one of my home machines the day it became available and while no radical new features introduced I liked the improvements over 8.10 and 8.04. Since  haven&#8217;t tried KDE for a while, I decide to download and install the latest Kubuntu 9.04 on VMWare and give it a try.</p>
<p>Kubuntu&#8217;s installation process is as easy as it could be just like installing Ubuntu. It took only a minute or two longer than installing Ubuntu but within 10 minutes I was ready to log in.</p>
<p><strong>What I liked</strong><br />
The new Dolphin file manager is extremely fast and easy to use. I like the the places side bar where you could create shortcuts to your favorite folders. The performance of Dolphin is also much faster than Nautilus in Gnome. For smaller folder sizes (e.g. folders with less than 100 files) there is usually very little noticeable performance issue in either file managers. But with large folders (e.g. folders with a few thousands files), Nautilus becomes unbearably slow. Dolphin seems to have little trouble handling large folders.</p>
<p>I also like the &#8220;filter&#8221; capability in Dolphin. It makes finding files in folders much easier.</p>
<p>The new KDE application launcher is also very pleasant to use and I think it is certainly a big improvement over KDE 3&#8242;s start menu. The main menu usability has almost been my main issue when using GNOME. Even though alternative menus such as Gnome Main Menu and Ubuntu System Panel provide similar functionalities, they lack the polish and stability of KDE&#8217;s application launcher.</p>
<p>The overall performance of KDE 4 seems to be on par if not better than that of KDE 3.</p>
<p><strong>What I didn&#8217;t like</strong><br />
One of the main reasons the KDE community developed the Plasma desktop was to provide users a richer visual experience. Unfortunately I have never been a big fan of any UI visual sugars. Of course, this is totally a personal preference and opinion seems to be evenly divided within the Linux community&#8211;people either loves the new look and feel or think it is too cumbersome. And the borders of default windows are excessively thick which I think it is a waste of screen real estate. </p>
<p>The new task bar is a bit buggy and seems to be too easy to mess up, it would help to have a menu item called &#8220;restore to default layout&#8221;.</p>
<p>One of the problem I ran into was that Kubuntu somehow couldn&#8217;t remember the screen resolution setting under VMWare (by the way, I was using the 64bit version of Kubuntu). And every time after a reboot the screen size got automatically reset to 640&#215;480. I am not sure whether this particular issue is related to just VMWare (6.5.2), but it was very annoying as I had to manually adjust the screen resolution every time. And once the screen resolution is adjusted, font sizes on certain applications got messed up. It seems that the font in Konqueror got scaled up multiple times somehow and looks ridiculously large. </p>
<p>So I guess I will have to stick to my GNOME desktop for a long while&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kerrywong.com/2009/05/01/kbuntu-904-test-drive/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>The Jaunty Jackalope</title>
		<link>http://www.kerrywong.com/2009/04/23/the-jaunty-jackalope/</link>
		<comments>http://www.kerrywong.com/2009/04/23/the-jaunty-jackalope/#comments</comments>
		<pubDate>Thu, 23 Apr 2009 15:14:48 +0000</pubDate>
		<dc:creator>kwong</dc:creator>
				<category><![CDATA[Linux/BSD]]></category>
		<category><![CDATA[Open Source]]></category>
		<category><![CDATA[Ubuntu]]></category>

		<guid isPermaLink="false">http://www.kerrywong.com/?p=975</guid>
		<description><![CDATA[Without much fanfare, Ubuntu 9.04 (Jaunty Jackalope) was released into the wild earlier today. But Ubuntu followers are wasting no time, overloading many of the official downloading sites. This kind of enthusiasm is certainly encouraging as Linux matures. And certainly, this kind of enthusiasm is much needed for the open source community in general. Whether [...]]]></description>
			<content:encoded><![CDATA[<p>Without much fanfare, <a href="http://www.ubuntu.com/">Ubuntu 9.04 (Jaunty Jackalope)</a> was released into the wild earlier today. But Ubuntu followers are wasting no time, overloading many of the official downloading sites. <span id="more-975"></span></p>
<p>This kind of enthusiasm is certainly encouraging as Linux matures. And certainly, this kind of enthusiasm is much needed for the open source community in general. Whether it is proprietary software or free software, the community acceptance is definitely key to the success of its future. </p>
<p>I have three systems running on Ubuntu at home right now. My WordPress server is running Ubuntu 8.04 server (32 bit). One of my working machine is running Ubuntu 8.04 64 bit and the other is running Ubuntu 8.10. For my server, I will stick to the 8.04 as it is an LTS release. But I am thinking of installing the latest Ubuntu desktops onto my workstations in the next couple of weeks&#8230;</p>
<p><strong>Update 1 8:00 PM</strong><br />
I have just installed Ubuntu 9.04 using the ISO image I downloaded earlier onto a VM. The installation was made even easier (the time zone is now selected by default). The default installation took just about five minutes. The boot time is also very impressive. It took just about 20 seconds before showing the login screen, which is a big improvement over my 8.04 installation.</p>
<p><strong>Update 2 9:00 PM</strong><br />
Even though a fresh installation might be ideal, I decided to run the update on my Ubuntu 8.10 machine anyway. This is mainly because it was used as a file and VM server and I had done very little customizations on it besides that. So I think the chances of getting a successful upgrade is pretty high. The distribution upgrade is pretty slow though, and it is downloading at about 30k/s right now&#8230;<br />
<div id="attachment_985" class="wp-caption aligncenter" style="width: 391px"><a href="http://www.kerrywong.com/blog/wp-content/uploads/2009/04/ubuntu904upgrade.jpg"><img src="http://www.kerrywong.com/blog/wp-content/uploads/2009/04/ubuntu904upgrade.jpg" alt="Upgrading from Ubuntu 8.10 to 9.04" title="Upgrading from Ubuntu 8.10 to 9.04" width="381" height="318" class="size-full wp-image-985" /></a><p class="wp-caption-text">Upgrading from Ubuntu 8.10 to 9.04</p></div></p>
<p><strong>Update 3 4/24/2009 7:00 AM</strong><br />
The 8.10 to 9.04 upgrade went surprisingly well. The only problem I had has nothing to do with Ubuntu. </p>
<p>Since the machine I was doing the update on is a file and VMs server, I did not bother attach display or keyboard to it. So when I was doing the update yesterday used an SSH connection and forgot that the updates would need to shutdown SSH and other services in order to install the newer version of the daemons. Anyway, half way through the upgrade, the connection to my server was lost. At first I thought that I could just attach a monitor and keyboard to my server and continue the whole upgrade process. Of course it wouldn&#8217;t work since the upgrade process was tied to that particular terminal. </p>
<p>So I had to reboot the machine. For whatever reason, maybe because the machine was only half upgraded, the normal boot does not work and I could not log in via the graphical login screen. I restarted in &#8220;safe&#8221; mode dropping into a root shell and did a </p>
<blockquote><p>
sudo dpkg &#8211;configure -a
</p></blockquote>
<p>And the upgrade process picked up from where it was left at before. This is truly impressive! After the upgrade process finished, I rebooted into the new 9.04 installation:</p>
<blockquote><p>
sudo lsb_release -a</p>
<p>Distributor ID:	Ubuntu<br />
Description:	Ubuntu 9.04<br />
Release:	9.04<br />
Codename:	jaunty
</p></blockquote>
<p>The only thing that did not work after the upgrade was the VMWare Server (which is totally expected as it needs to link to the correct version of libraries). But after a simple re-config, the VMWare Server came back into life.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kerrywong.com/2009/04/23/the-jaunty-jackalope/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>C++ IDEs Under Linux</title>
		<link>http://www.kerrywong.com/2009/04/18/c-ides-under-linux/</link>
		<comments>http://www.kerrywong.com/2009/04/18/c-ides-under-linux/#comments</comments>
		<pubDate>Sun, 19 Apr 2009 02:27:04 +0000</pubDate>
		<dc:creator>kwong</dc:creator>
				<category><![CDATA[Coding]]></category>
		<category><![CDATA[Linux/BSD]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[Code::Blocks]]></category>
		<category><![CDATA[KDevelop]]></category>
		<category><![CDATA[NetBeans]]></category>

		<guid isPermaLink="false">http://www.kerrywong.com/?p=964</guid>
		<description><![CDATA[So far I have been mainly using KDevelop and Code::Blocks as my C++ development IDEs. Recently, I started using NetBeans IDE for C++ and I started to like it quite a bit. In my opinion, the above three IDEs all have their strengths and weaknesses depending on what kind of project you are working on. [...]]]></description>
			<content:encoded><![CDATA[<p>So far I have been mainly using <a href="http://www.kdevelop.org">KDevelop</a> and <a href="http://www.codeblocks.org">Code::Blocks</a> as my C++ development IDEs. Recently, I started using <a href="http://www.netbeans.org/">NetBeans IDE</a> for C++ and I started to like it quite a bit.<span id="more-964"></span></p>
<p>In my opinion, the above three IDEs all have their strengths and weaknesses depending on what kind of project you are working on. Here are some of my observations.<br />
<strong><br />
KDevelop is probably the most comprehensive IDE of the three</strong>. This should not come as a surprise as it has been around for more than 10 years and it bas been the de facto development environment for most of the <a href="http://www.kde.org">KDE</a> development work. It is very feature rich and you can use it to develop many different types of applications natively out of box (e.g. KDE, GTK+, Qt, wxWidgets, etc). If you are developing <a href="http://www.gnu.org">GNU</a> style applications, you will benefit from the Automake project type if provides. Besides the vast functionalities KDevelop provides, it is also very fast, efficient and stable. </p>
<p>Nonetheless, KDevelop is not the most intuitive IDE and does not suit small projects (e.g. prof of concept code) well as the initial project setup can be time consuming.</p>
<p><strong>Code::Blocks is a very capable IDE for C++ development as well</strong>. It offers many project templates even though it does not offer native KDE project types. This should not be a problem to most people however. Among the features I like the most is that it does not require explicit make file configuration and the build dependencies are inferred by default. This makes it very attractive for rapid prototyping. Debugging under Code::Blocks is also a pleasant experience. It also integrates (at least in the later SVN versions) <a href="http://valgrind.org/">Valgrind</a>&#8216;s MemCheck and Cachegrind, which are very useful for detecting memory leaks and tweaking algorithms for the maximum performance.</p>
<p>The latest stable version of Code::Blocks is 8.02, it is a little dated as a lot of functionalities have been added in the later SVN builds. If you do not require the most stability (I have run into <a href="/2009/04/15/how-to-revert-to-a-specific-svn-version-of-codeblocks/">some issues</a> recently), using SVN build should not be a problem. The editor (e.g. syntax highlighting, collapsible regions) is a little bit buggy though and the contextual help does not always work to the level of detail I desired. </p>
<p><strong>NetBeans C++ IDE is probably the most beautiful one among the three</strong>. It offers the most detailed syntax highlighting, and can be configured to display class hierarchy and library function information. The refactor tool works pretty well and is certainly a boon to large scale development. Its contextual help is also of top-notch.</p>
<p>All of these come at a cost of course. NetBeans C++ IDE is the most resource intensive among the three. It can easily use 500 MB memory when doing development and can be sluggish at times. It also comes with a very limited project template (e.g. no out-of-box project templates for Qt, wxWidgets). Certain settings <a href="http://www.netbeans.org/kb/docs/cnd/toolchain.html">are hard to get at</a> as well. Nevertheless, if I am primarily writing back-end code, NetBeans C++ IDE could easily earn my top choice. </p>
<p>Some people like <a href="http://www.anjuta.org">Anjuta</a> and compare it favorably to Code::Blocks. But I haven&#8217;t got a chance to use it yet.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kerrywong.com/2009/04/18/c-ides-under-linux/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>How to Revert to a Specific SVN Version of Code::Blocks</title>
		<link>http://www.kerrywong.com/2009/04/15/how-to-revert-to-a-specific-svn-version-of-codeblocks/</link>
		<comments>http://www.kerrywong.com/2009/04/15/how-to-revert-to-a-specific-svn-version-of-codeblocks/#comments</comments>
		<pubDate>Wed, 15 Apr 2009 23:52:45 +0000</pubDate>
		<dc:creator>kwong</dc:creator>
				<category><![CDATA[Linux/BSD]]></category>
		<category><![CDATA[Miscellaneous]]></category>
		<category><![CDATA[Code::Blocks]]></category>
		<category><![CDATA[Ubuntu]]></category>

		<guid isPermaLink="false">http://www.kerrywong.com/?p=960</guid>
		<description><![CDATA[I had set up my apt-get source to use latest SVN build of Code::Blocks. Everything has been running pretty smoothly until a couple of days ago when Code::Blocks was updated to SVN 5534. It seemed that for whatever reason, this particular build is extremely unstable on my Ubuntu 8.04 box and it would refuse to [...]]]></description>
			<content:encoded><![CDATA[<p>I had set up my apt-get source to use latest SVN build of Code::Blocks. Everything has been running pretty smoothly until a couple of days ago when Code::Blocks was updated to SVN 5534.<span id="more-960"></span> It seemed that for whatever reason, this particular build is extremely unstable on my Ubuntu 8.04 box and it would refuse to open any projects created prior to SVN 5534.</p>
<p>So naturally, I wanted to revert back to my previous working version (SVN 5489). There are some <a href="http://lgp203.free.fr/spip/spip.php?article1">instructions on lgp203.free.fr</a> that in theory should work to install any revisions of Code::Blocks from SVN. But the script provided there did not work for me as it was complaining about not able to find certain packages. So I decided to do it manually, and here are the steps I took to get SVN 5489 installed.</p>
<p>First, you will need to remove the installed version of Code::Blocks.</p>
<blockquote><p>sudo apt-get remove codeblocks<br />
sudo apt-get remove codeblocks-common</p></blockquote>
<p>Then you will need to install the Debian packages one by one from lgp203.free.fr. using </p>
<blockquote><p>wget http://lgp203.free.fr/pool/codeblocks/wxsmith-headers_8.02svn5489_all.deb<br />
wget http://lgp203.free.fr/pool/codeblocks/libwxsmithlib0_8.02svn5489_amd64.deb<br />
wget http://lgp203.free.fr/pool/codeblocks/codeblocks-headers_8.02svn5489_all.deb<br />
wget http://lgp203.free.fr/pool/codeblocks/libcodeblocks0_8.02svn5489_amd64.deb<br />
wget http://lgp203.free.fr/pool/codeblocks/codeblocks_8.02svn5489_amd64.deb<br />
wget http://lgp203.free.fr/pool/codeblocks/codeblocks-common_8.02svn5489_all.deb<br />
wget http://lgp203.free.fr/pool/codeblocks/codeblocks-contrib_8.02svn5489_amd64.deb<br />
wget http://lgp203.free.fr/pool/codeblocks/codeblocks-contrib-common_8.02svn5489_all.deb</p></blockquote>
<blockquote><p>sudo dpkg -i wxsmith-headers_8.02svn5489_all.deb<br />
sudo dpkg -i libwxsmithlib0_8.02svn5489_amd64.deb<br />
sudo dpkg -i codeblocks-headers_8.02svn5489_all.deb<br />
sudo dpkg -i libcodeblocks0_8.02svn5489_amd64.deb<br />
sudo dpkg -i codeblocks_8.02svn5489_amd64.deb<br />
sudo dpkg -i codeblocks-common_8.02svn5489_all.deb<br />
sudo dpkg -i codeblocks-contrib_8.02svn5489_amd64.deb<br />
sudo dpkg -i codeblocks-contrib-common_8.02svn5489_all.deb
</p></blockquote>
<p>You can find a full list of all the available Code::Blocks&#8217; SVN versions <a href="http://lgp203.free.fr/ubuntu/dists/">here</a>. Now, I am running version 5489 and everything is working again.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kerrywong.com/2009/04/15/how-to-revert-to-a-specific-svn-version-of-codeblocks/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>On Default Linux IO Priority</title>
		<link>http://www.kerrywong.com/2009/04/14/on-default-linux-io-priority/</link>
		<comments>http://www.kerrywong.com/2009/04/14/on-default-linux-io-priority/#comments</comments>
		<pubDate>Wed, 15 Apr 2009 01:24:02 +0000</pubDate>
		<dc:creator>kwong</dc:creator>
				<category><![CDATA[Linux/BSD]]></category>
		<category><![CDATA[Linux]]></category>

		<guid isPermaLink="false">http://www.kerrywong.com/?p=956</guid>
		<description><![CDATA[If there is any thing I think Linux distributions can definitely improve upon is to reduce the default IO task priorities while running a Windows manager (e.g. GNOME or KDE). What I have found out over time is that the default IO priorities tend to cause the UI very sluggish when doing heavy IO operations [...]]]></description>
			<content:encoded><![CDATA[<p>If there is any thing I think Linux distributions can definitely improve upon is to reduce the default IO task priorities while running a Windows manager (e.g. GNOME or KDE).<span id="more-956"></span></p>
<p>What I have found out over time is that the default IO priorities tend to cause the UI very sluggish when doing heavy IO operations (e.g. copying files between disks, etc.). While this problem is not inherent to Linux (Linux is only the kernel), The default sluggish behavior under heavy IO conditions makes popular Linux distributions (e.g. Ubuntu) less user friendly than it should.</p>
<p>While using <strong>ionice</strong> <a href="http://digg.com/d1AWma">can solve this issue</a>, it isn&#8217;t very continent as it needs to be manually set every time the user performs heavy IO tasks. </p>
<p>It is true that the performance of the main-stream hard drives/and controllers (e.g. SATA) is partially responsible for the sluggishness of the UI response and higher end disk sub-systems like serial attached SCSI alleviate this problem to a certain degree, for Linux to really go main stream nevertheless, this problem should not be overlooked as SATA is the common disk sub-system almost all main-stream users have.</p>
<p>Since copying large files is such a common task nowadays, why couldn&#8217;t we just lower the default IO priority settings instead of relying on user to use <strong>ionice</strong>? In general, most end users care more about the UI responsiveness than the background disk throughput. </p>
]]></content:encoded>
			<wfw:commentRss>http://www.kerrywong.com/2009/04/14/on-default-linux-io-priority/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>An Image Class Based On IPP</title>
		<link>http://www.kerrywong.com/2009/04/10/an-image-class-based-on-ipp/</link>
		<comments>http://www.kerrywong.com/2009/04/10/an-image-class-based-on-ipp/#comments</comments>
		<pubDate>Sat, 11 Apr 2009 00:34:55 +0000</pubDate>
		<dc:creator>kwong</dc:creator>
				<category><![CDATA[Coding]]></category>
		<category><![CDATA[Linux/BSD]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[FFT]]></category>
		<category><![CDATA[IPP]]></category>
		<category><![CDATA[Linux]]></category>

		<guid isPermaLink="false">http://www.kerrywong.com/?p=935</guid>
		<description><![CDATA[A couple of weeks ago, I wrote about how to interface Integrated Performance Primitives (IPP) with Magick++. While IPP offers excellent performance advantages, it does not come with the easiest programming model. Fortunately, it is easy enough to create a C++ wrapper on top of IPP and provide an easier to use programming interface. In [...]]]></description>
			<content:encoded><![CDATA[<p>A couple of weeks ago, I wrote about <a href="/2009/03/17/interfacing-ipp-with-magick/">how to interface Integrated Performance Primitives (IPP) with Magick++</a>. While IPP offers excellent performance advantages, it does not come with the easiest programming model. Fortunately, it is easy enough to create a C++ wrapper on top of IPP and provide an easier to use programming interface.<span id="more-935"></span></p>
<p>In this article, I will show a simple example of creating a wrapper class using <a href="http://www.intel.com/cd/software/products/asmo-na/eng/302910.htm">IPP</a> and <a href="http://www.imagemagick.org/Magick%2B%2B/">Magick++</a>. The example I am going to show can be used to calculate the 2-dimensional FFT spectrum of a gray-scale image. This framework can be easily extended to include other algorithms that can be applied to an image using IPP.</p>
<p>Before I show the implementation details, let me first show how easy it is to use the class. The code snippet below shows how to read in an image file, apply 2D FFT with and without a Hamming window and save the results into image files.</p>
<pre class="brush: cpp;">
    IPPGrayImage *img, *img1;

    img = new IPPGrayImage();
    img-&gt;LoadFromFile(IMAGE_FILE);

    img1 = img-&gt;Clone();
    img1 = img1-&gt;FFT(true);
    img1-&gt;SaveToFile(IMAGE_HOME + &quot;/test_fftmag.jpg&quot;);
    img1 = img-&gt;Clone();
    img1 = img-&gt;ApplyHammingWindow();
    img1-&gt;SaveToFile(IMAGE_HOME + &quot;/test_hamming.jpg&quot;);
    img1 = img1-&gt;FFT(true);
    img1-&gt;SaveToFile(IMAGE_HOME + &quot;/test_fftmag_hamming.jpg&quot;);
</pre>
<p>And for the following IMAGE_FILE,<br />
<div id="attachment_941" class="wp-caption aligncenter" style="width: 266px"><a href="http://www.kerrywong.com/blog/wp-content/uploads/2009/04/testimg.jpeg"><img src="http://www.kerrywong.com/blog/wp-content/uploads/2009/04/testimg.jpeg" alt="Test image used for 2D FFT" title="Test Image" width="256" height="256" class="size-full wp-image-941" /></a><p class="wp-caption-text">Test image used for 2D FFT</p></div></p>
<p>Here are the results for FFT spectrum with and without hamming window:<br />
<div id="attachment_943" class="wp-caption aligncenter" style="width: 266px"><a href="http://www.kerrywong.com/blog/wp-content/uploads/2009/04/test_hamming.jpg"><img src="http://www.kerrywong.com/blog/wp-content/uploads/2009/04/test_hamming.jpg" alt="Hamming window applied" title="Hamming window applied" width="256" height="256" class="size-full wp-image-943" /></a><p class="wp-caption-text">Hamming window applied</p></div><br />
<div id="attachment_944" class="wp-caption aligncenter" style="width: 266px"><a href="http://www.kerrywong.com/blog/wp-content/uploads/2009/04/test_fftmag.jpg"><img src="http://www.kerrywong.com/blog/wp-content/uploads/2009/04/test_fftmag.jpg" alt="FFT spectrum (without Hamming window)" title="FFT spectrum (without Hamming window)" width="256" height="256" class="size-full wp-image-944" /></a><p class="wp-caption-text">FFT spectrum (without Hamming window)</p></div><br />
<div id="attachment_945" class="wp-caption aligncenter" style="width: 266px"><a href="http://www.kerrywong.com/blog/wp-content/uploads/2009/04/test_fftmag_hamming.jpg"><img src="http://www.kerrywong.com/blog/wp-content/uploads/2009/04/test_fftmag_hamming.jpg" alt="FFT spectrum with Hamming window" title="FFT spectrum with Hamming window" width="256" height="256" class="size-full wp-image-945" /></a><p class="wp-caption-text">FFT spectrum with Hamming window</p></div></p>
<p>The header file for the class is as follows:</p>
<pre class="brush: cpp;">
#ifndef IPPGRAYIMAGE_H
#define IPPGRAYIMAGE_H

#include &lt;Magick++/Image.h&gt;
#include &lt;Magick++.h&gt;
#include &lt;ipp.h&gt;

using namespace std;
using namespace Magick;

namespace KDW
{
    class IPPGrayImage
    {
    public:
        unsigned int PIXEL_SIZE;

        IPPGrayImage();
        IPPGrayImage(const unsigned int width, const unsigned int height);
        IPPGrayImage(Ipp32f *imgBuffer, const unsigned int width, const unsigned int height);
        IPPGrayImage(const IPPGrayImage&amp; other);
        virtual ~IPPGrayImage();
        IPPGrayImage&amp; operator=(const IPPGrayImage&amp; other);

        void LoadFromFile(string fileName);
        void SaveToFile();
        void SaveToFile(string fileName);

        inline float GetPixel(unsigned int col, unsigned int row) {return _imgBuffer[row * _width + col];}
        inline void SetPixel(unsigned int col, unsigned int row, float clr) {_imgBuffer[row * _width + col] = clr;}

        unsigned int GetWidth() { return _width;}
        unsigned int GetHeight() { return _height;}

        Ipp32f* GetImageBuffer() { return _imgBuffer;}
        Image* GetImage() { return _img;}

        IPPGrayImage* Clone();
        IPPGrayImage* ApplyHammingWindow();
        IPPGrayImage* FFT(bool fftShift = false);
    protected:
    private:
        unsigned int _width;
        unsigned int _height;

        Image* _img;
        Pixels* _view;
        PixelPacket* _pixels;
        Ipp32f* _imgBuffer;
        string _fileName;

        void Init();
    };
}
#endif // IPPGRAYIMAGE_H
</pre>
<p>And here&#8217;s the implementation for the class:</p>
<pre class="brush: cpp;">
#include &lt;assert.h&gt;
#include &lt;math.h&gt;

#include &quot;IPPGrayImage.h&quot;

namespace KDW
{
    /** @brief Constructor()
     *  Initialize image buffer.
     */
    IPPGrayImage::IPPGrayImage()
    {
        Init();
    }

    /** @brief Constructor(width, height)
     *  Initialize an image size of width x height.
     */
    IPPGrayImage::IPPGrayImage(const unsigned int width, const unsigned int height)
    {
        Init();

        _width = width;
        _height = height;

        int stepByte = 0;
        _imgBuffer = ippiMalloc_32f_C1(_width, _height, &amp;stepByte);
    }

    /** @brief Constructor(imgBuffer, width, height)
     *  Initilize from an Ipp32f buffer (width x height)
     */
    IPPGrayImage::IPPGrayImage(Ipp32f *imgBuffer, const unsigned int width, const unsigned int height)
    {
        Init();

        _width = width;
        _height = height;
        _imgBuffer = imgBuffer;
    }

    /** @brief Copy Constructor
     */
    IPPGrayImage::IPPGrayImage(const IPPGrayImage&amp; other)
    {
        _width = other._width;
        _height = other._height;

        int stepByte = 0;

        _imgBuffer = ippiMalloc_32f_C1(_width, _height, &amp;stepByte);

        for (unsigned int row = 0; row &lt; _height ; row++)
        {
            for (unsigned int column = 0; column &lt; _width ; column++)
            {
                _imgBuffer[column + row * _width] =other._imgBuffer[column + row * _width];
            }
        }

        if (other._pixels == NULL) _pixels = NULL;
    }

    /** @brief Overload =
     */
    IPPGrayImage&amp; IPPGrayImage::operator=(const IPPGrayImage&amp; rhs)
    {
        if (this == &amp;rhs) return *this; // handle self assignment

        return *this;
    }

    /**
     * @brief Destructor
     */
    IPPGrayImage::~IPPGrayImage()
    {
        ippFree(_imgBuffer);
        delete _img;
        delete _view;
    }

    /**
     * @brief Common initialization code
     */
    void IPPGrayImage::Init()
    {
        PIXEL_SIZE = sizeof(Ipp32f);
        _pixels = NULL;
        _imgBuffer = NULL;
        _img = NULL;
    }

    /** @brief Load an image from file
      */
    void IPPGrayImage::LoadFromFile(string fileName)
    {
        _img = new Image(fileName);
        Geometry g = _img-&gt;size();

        _width = g.width();
        _height= g.height();

        _view = new Pixels(*_img);
        _pixels = _view-&gt;get(0,0, _width, _height);

        int stepByte = 0;
        _imgBuffer = ippiMalloc_32f_C1(_width, _height, &amp;stepByte);

        for (unsigned int row = 0; row &lt; _height ; row++)
        {
            for (unsigned int column = 0; column &lt; _width ; column++)
            {
                PixelPacket *p = &amp;_pixels[column + row * _width];
                Color c = Color(p-&gt;red, p-&gt;green, p-&gt;blue);
                _imgBuffer[column + row * _width] = c.intensity();
            }
        }
    }

    /** @brief Save the current image buffer to file
      */
    void IPPGrayImage::SaveToFile()
    {
        SaveToFile(&quot;&quot;);
    }

    /** @brief SaveToFile(fileName)
     *  Saves the current image buffer to a file (by file name).
     */
    void IPPGrayImage::SaveToFile(string fileName)
    {
        if (_img != NULL &amp;&amp; _pixels != NULL)
        {
            for (unsigned int y = 0; y&lt; _height ; y++)
            {
                for (unsigned int x = 0; x &lt; _width; x++)
                {
                    float clr = (float) _imgBuffer[x + y * _width];
                    _pixels[x+ y * _width] = Color(clr, clr, clr);
                }
            }
            _view-&gt;sync();
            _img-&gt;syncPixels();

            if (fileName == &quot;&quot;)
            {
                _img-&gt;write(_fileName);
            }
            else
            {
                _img-&gt;write(fileName);
            }
        }
        else
        {
            Image img(Geometry(_width, _height),&quot;white&quot;);

            for (unsigned int y = 0; y&lt; _height ; y++)
            {
                for (unsigned int x = 0; x &lt; _width; x++)
                {
                    Color c;
                    float clr = (float) _imgBuffer[x + y * _width];
                    img.pixelColor(x,y, Color(clr, clr, clr));
                }
            }

            img.write(fileName);
        }
    }

    /** @brief Clone
    *   Duplicate the current image to another IPPGrayImage object.
    *   @return an IPPGrayImage pointer to the cloned image
    */
    IPPGrayImage* IPPGrayImage::Clone()
    {
        IPPGrayImage *newImg;

        newImg = new IPPGrayImage(_width, _height);

        int stepByte = 0;
        newImg-&gt;_imgBuffer = ippiMalloc_32f_C1(_width,_height, &amp;stepByte);

        for (unsigned int y = 0 ; y &lt; _height ; y++)
        {
            for (unsigned int x = 0 ; x &lt; _width ; x++)
            {
                newImg-&gt;_imgBuffer[x + y * _width] = _imgBuffer[x + y * _width];
            }
        }

        return newImg;
    }

    /** @brief Apply Hamming window to the image
     *  @return an IPPGrayImage pointer to the processed image
     */
    IPPGrayImage* IPPGrayImage::ApplyHammingWindow()
    {
        IPPGrayImage *newImg;
        newImg = new IPPGrayImage(_width , _height);
        IppiSize srcImgSize = {_width, _height};

        IppStatus sts;
        int stepByte;
        Ipp32f *imgCache = ippiMalloc_32f_C1(_width , _height , &amp;stepByte);

        sts = ippiWinHamming_32f_C1R(_imgBuffer, _width * PIXEL_SIZE, imgCache, _width * PIXEL_SIZE, srcImgSize);
        assert(sts ==ippStsNoErr);

        for (unsigned int y = 0; y&lt; _height; y++)
        {
            for (unsigned int x = 0; x &lt; _width; x++)
            {
                newImg-&gt;_imgBuffer[x+ y * _width] = imgCache[x + y * _width];
            }
        }

        return newImg;
    }

    /** @brief Perform FFT on the image and returns the magnitude component
     *  @param fftShift: if it is true, the the result is with
     *         zero-frequency component shifted to center of spectrum
     *  @return the magnitude FFT component
     */
    IPPGrayImage* IPPGrayImage::FFT(bool fftShift)
    {
        IPPGrayImage *newImg;
        IppiFFTSpec_R_32f *spec;
        IppStatus sts;

        unsigned int n = (int) (logf((float) _width) / logf(2.0f));
        unsigned int m = (int) (logf((float) _height) / logf(2.0f));

        unsigned int N = pow(2, n);
        unsigned int M = pow(2, m);

        if (N &lt; _width)
        {
            n = n + 1;
            N = pow(2, n);
        }

        if (M &lt; _height)
        {
            m = m + 1;
            M = pow(2, m);
        }

        int stepByte;
        Ipp32f *src = ippiMalloc_32f_C1(M , N , &amp;stepByte);
        Ipp32f *dst = ippiMalloc_32f_C1(M , N , &amp;stepByte);
        Ipp32f *mag = ippiMalloc_32f_C1(M , N , &amp;stepByte);

        IppiSize srcImgSize = {_width, _height};
        IppiSize dstImgSize = {N, M};

        sts = ippiCopyConstBorder_32f_C1R(
                  _imgBuffer, _width * PIXEL_SIZE, srcImgSize,
                  src,  N * PIXEL_SIZE, dstImgSize,
                  0,0,0);
        assert(sts ==ippStsNoErr);

        sts = ippiFFTInitAlloc_R_32f(&amp;spec, n , m, IPP_FFT_DIV_BY_SQRTN, ippAlgHintAccurate);
        assert(sts ==ippStsNoErr);

        sts = ippiFFTFwd_RToPack_32f_C1R(src, N*PIXEL_SIZE, dst, N*PIXEL_SIZE, spec, 0);
        assert(sts ==ippStsNoErr);

        sts = ippiMagnitudePack_32f_C1R(dst, N*PIXEL_SIZE, mag, N*PIXEL_SIZE, dstImgSize);
        assert (sts ==ippStsNoErr);

        newImg = new IPPGrayImage(N , M);

        if (fftShift)
        {
#pragma omp sections
            {
#pragma omp section
                {
                    for (unsigned int y = 0 ; y &lt; M/2; y++)
                    {
                        for (unsigned int x = 0 ; x &lt; N/2; x++)
                        {
                            newImg-&gt;_imgBuffer[x+ y *N] = mag[x + N/2 + (y + M/2) * N];
                        }
                    }
                }
#pragma omp section
                {
                    for (unsigned int y = 0 ; y &lt; M/2; y++)
                    {
                        for (unsigned int x = N/2 ; x &lt; N; x++)
                        {
                            newImg-&gt;_imgBuffer[x+ y *N] = mag[x - N/2 + (y + M/2) * N];
                        }
                    }
                }
#pragma omp section
                {
                    for (unsigned int y = M/2 ; y &lt; M; y++)
                    {
                        for (unsigned int x = 0 ; x &lt; N/2; x++)
                        {
                            newImg-&gt;_imgBuffer[x+ y *N] = mag[x + N/2 + (y - M/2) * N];
                        }
                    }
                }
#pragma omp section
                {
                    for (unsigned int y = M/2 ; y &lt; M; y++)
                    {
                        for (unsigned int x = N/2 ; x &lt; N; x++)
                        {
                            newImg-&gt;_imgBuffer[x+ y *N] = mag[x - N/2 + (y - M/2) * N];
                        }
                    }
                }
            }
        }
        else
        {
            for (unsigned int y = 0; y&lt; M; y++)
            {
                for (unsigned int x = 0; x &lt;N; x++)
                {
                    newImg-&gt;_imgBuffer[x+ y * N] = mag[x + y * N];
                }
            }

        }

        return newImg;
    }
}
</pre>
<p>The above example showed only the FFT function, but virtually all IPP image routines can be accommodated using the wrapper image class above. Intel IPP utilizes many different data types (e.g. Ipp8u, Ipp16s, Ipp32f etc.), but to provide most of the flexibility and compatibility I chose to use only the 32 bit floating data type. For specific implementations, other data types can be used as well.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kerrywong.com/2009/04/10/an-image-class-based-on-ipp/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>The Obstacles to Linux Going Mainstream</title>
		<link>http://www.kerrywong.com/2009/03/29/the-obstacles-to-linux-going-mainstream/</link>
		<comments>http://www.kerrywong.com/2009/03/29/the-obstacles-to-linux-going-mainstream/#comments</comments>
		<pubDate>Mon, 30 Mar 2009 01:30:37 +0000</pubDate>
		<dc:creator>kwong</dc:creator>
				<category><![CDATA[Linux/BSD]]></category>
		<category><![CDATA[Miscellaneous]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Open Source]]></category>

		<guid isPermaLink="false">http://www.kerrywong.com/?p=889</guid>
		<description><![CDATA[It was almost one year ago when I switched my main home computer to Linux. Since then, I have been using my Ubuntu 8.04 installation daily and have not found the need to boot up Windows at all. While personally I see Linux and other UNIX variants as strong candidates to have the possibility to [...]]]></description>
			<content:encoded><![CDATA[<p>It was <a href="/2008/06/14/linux-only-two-months-later/">almost one year ago</a> when I switched my main home computer to Linux. Since then, I have been using my Ubuntu 8.04 installation daily and have not found the need to boot up Windows at all.<span id="more-889"></span></p>
<p>While personally I see Linux and other UNIX variants as strong candidates to have the possibility to eventually replace Windows in both the consumer domain and the enterprise domain, it seems that this sea change is not going to happen any time soon as <a href="http://www.linux-foundation.org/weblogs/jzemlin/2008/10/29/linux-to-ship-on-more-desktops-than-windows/">predicted by some</a>.</p>
<p>Today although Linux is widely used in the server market, in the consumer domain it remains a niche market, accounting for <a href="http://news.cnet.com/8301-13505_3-9910263-16.html">just around 2% of market shares</a>. Even thought the current economic down turn had sped up Linux&#8217;s adoption, it is still too small to make any significant impact on the overall consumer OS landscape.</p>
<p>Don&#8217;t get me wrong, I think that Linux is an excellent operating systems and in fact I think that it is a perfect Windows replacement for all the technical folks and certainly geeks. But the following issues remain some of the big obstacles for the normal consumers.</p>
<h4>Codec</h4>
<p>This is not the problem of Linux per se. Due to the open source nature and the various legal reasons, most free Linux distributions (e.g. <a href="http://www.ubuntu.com/">Ubuntu</a>, <a href="http://www.opensuse.org">openSUSE</a>)  do not come with popular codecs (e.g. the codecs for playing back MP3&#8242;s and WMV files) installed. While for geeks it might be quite trivial to dig around in the vast Linux application repositories and find the exact codecs needed in a matter of minutes, the average Joes might have a hard time figuring out why his favorite DVD won&#8217;t play on his Linux system.</p>
<h4>Consumer Applications</h4>
<p>This gap has been closing rapidly over the years. Now we have <a href="http://www.mozilla.com/en-US/firefox/firefox.html">FireFox</a>, <a href="http://www.mozillamessaging.com/en-US/thunderbird/">Thunderbird</a>, <a href="http://openoffice.org">OpenOffice</a> and many other productivity applications come as standard in almost all popular distros. And even some professional software suites have their alternatives in the Linux World. For example, <a href="http://www.gimp.org/">GIMP</a> is a capable replacement for Adobe Photoshop. However, many of the most popular consumer software products remains missing on the Linux platform. Computer game is one of them. While I do not play computer games, many people would like to use their PCs to play some popular games occasionally. And to some, because of these Windows only and no Linux equivalent applications, there is no alternative but to use Windows.</p>
<h4>Drag and Drop</h4>
<p>For those who are used to Windows, drag and drop seems to be a given. All most all applications in Windows support drag and drop, but on Linux such support is inconsistent at best.</p>
<h4>Copy and Paste</h4>
<p>This is another area frustrates a lot of Windows users. In Windows, copy and paste pretty much works among all the applications. In Linux however, it does not always work and sometimes</p>
<h4>The Command Line</h4>
<p>Most Linux distributions use BASH. For seasoned users, using the command line is just as convenient if not more so than using a GUI equivalent application. In fact I personally prefer the &#8220;terminal&#8221; as it gave me the most flexibility and none of the overhead. But we can not expect every user has the same level of comfort with the command line environment. </p>
<p>I just mentioned a few areas where I think are among some of the main obstacles to the broad adoption of Linux. And most of these are interoperability issues which are not going to be resolved anytime soon. To the advanced users, most of these issues are pretty minor and there are many ways to work around. But to the average users who have accustomed to the consistencies within the system,  even minor incontinence can mean a show stopper. </p>
<p>With all that said, I have found Linux to be a very attractive platform. While Linux might not be able to replace the proprietary Windows anytime soon for the reasons I discussed above, it has seeded deeply inside many people who believe in the free software movement and I am hopeful that one day we will see the OS world dominated by open source software, be it Linux or something else.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kerrywong.com/2009/03/29/the-obstacles-to-linux-going-mainstream/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Matrix Multiplication Performance in C++</title>
		<link>http://www.kerrywong.com/2009/03/07/matrix-multiplication-performance-in-c/</link>
		<comments>http://www.kerrywong.com/2009/03/07/matrix-multiplication-performance-in-c/#comments</comments>
		<pubDate>Sat, 07 Mar 2009 04:16:14 +0000</pubDate>
		<dc:creator>kwong</dc:creator>
				<category><![CDATA[Coding]]></category>
		<category><![CDATA[Linux/BSD]]></category>
		<category><![CDATA[Algorithm]]></category>
		<category><![CDATA[BLAS]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[MATLAB]]></category>
		<category><![CDATA[matrix multiplication]]></category>

		<guid isPermaLink="false">http://www.kerrywong.com/?p=586</guid>
		<description><![CDATA[A few days ago, I ran across this article by Dmitri Nesteruk. In his article, he compared the performance between C# and C++ in matrix multiplication. From the data he provided, matrix multiplication using C# is two to three times slower than using C++ in comparable situations. Even though a lot of optimizations have been [...]]]></description>
			<content:encoded><![CDATA[<p>A few days ago, I ran across <a href="http://mindstudies.psy.soton.ac.uk/dmitri/blog/index.php/archives/160">this article by Dmitri Nesteruk</a>. In his article, he compared the performance between C# and C++ in <a href="http://en.wikipedia.org/wiki/Matrix_multiplication">matrix multiplication</a>. From the data he provided, matrix multiplication using C# is two to three times slower than using C++ in comparable situations.<span id="more-586"></span></p>
<p>Even though a lot of optimizations have been done in the .Net runtime to make it more efficient, it is apparent that scientific programming still favors C and C++ because that the performance advantage is huge.</p>
<p>In this article, I will examine some matrix multiplication algorithms that are commonly used and illustrate the efficiencies of the various methods. All the tests are done using C++ only and matrices size ranging from 500&#215;500 to 2000&#215;2000. When the matrix sizes are small (e.g. &lt;50), you can pretty much use any matrix multiplication algorithms without observing any significant performance differences. This is largely due to the fact that the typical stable matrix multiplication algorithms are O(n^3) and sometimes array operation overheads outweigh the benefit of algorithm efficiencies. But for matrices of larger dimensions, the efficiency of the multiplication algorithm becomes extremely important.</p>
<p>Since <a href="http://mindstudies.psy.soton.ac.uk/dmitri/blog/index.php/archives/160">Dmitri&#8217;s article</a> has already captured pretty detailed data using the standard matrix multiplication algorithm, I will not repeat his findings in this article. What I intended to show was the performance data of <a href="http://www.boost.org/doc/libs/1_38_0/libs/numeric/ublas/doc/overview.htm">uBLAS</a>, <a href="http://openmp.org/wp/">OpenMP</a>, <a href="http://en.wikipedia.org/wiki/Basic_Linear_Algebra_Subprograms">cBLAS</a> and <a href="http://www.mathworks.com/">MATLAB</a>.</p>
<p>The following sample code are compiled under Ubuntu 8.10 64 bit (kernel 2.6.24.23) on Intel Q9450@3.2GHz.</p>
<h4>Standard Matrix Multiplication (Single Threaded)</h4>
<p>This is our reference code. Later on, I will only show the critical portion of the code and not repeat the common portion of code that initializes/finalizes the arrays. Similarly, the timing method used is also the same across all the tests and will be omitted later on.</p>
<pre class="brush: cpp;">
float **A, **B, **C;

A = new float*[matrix_size];
B = new float*[matrix_size];
C = new float*[matrix_size];

for (int i = 0 ; i &lt; matrix_size; i++)
{
    A[i] = new float[matrix_size];
    B[i] = new float[matrix_size];
    C[i] = new float[matrix_size];
}

for (int i=0; i&lt;matrix_size; i++)
{
    for (int j = 0 ; j &lt; matrix_size; j++)
    {
        A[i][j]=rand();
        B[i][j]=rand();
    }
}

timeval t1, t2, t;
gettimeofday(&amp;t1, NULL);

for (int i = 0 ; i &lt; matrix_size; i++)
{
    for (int j = 0;  j &lt; matrix_size; j++)
    {
        C[i][j] = 0;
        for (int k = 0; k &lt; matrix_size; k++)
        {
            C[i][j] += A[i][k] * B[k][j];
        }
    }
}

gettimeofday(&amp;t2, NULL);
timersub(&amp;t2, &amp;t1, &amp;t);

cout &lt;&lt; t.tv_sec + t.tv_usec/1000000.0 &lt;&lt; &quot; Seconds -- Standard&quot; &lt;&lt; endl;

for (int i = 0 ; i &lt; matrix_size; i++)
{
    delete A[i];
    delete B[i];
    delete C[i];
}

delete A;
delete B;
delete C;
</pre>
<h4>OpenMP With Two Dimensional Arrays</h4>
<p>Using OpenMP, we are able to multiple threads via the #pragma omp directives. For the simple algorithm we used here, the speed increase is almost proportional to the number of available cores within the system.</p>
<pre class="brush: cpp;">
...
#pragma omp parallel for shared(a,b,c)
for (long i=0; i&lt;matrix_size; i++)
{
    for (long j = 0; j &lt; matrix_size; j++)
    {
        float sum = 0;
        for (long k = 0; k &lt; matrix_size; k++)
        {
            sum +=a[i][k]*b[k][j];
        }
        c[i][j] = sum;
    }
}
...
</pre>
<h4>OpenMP With One Dimensional Arrays</h4>
<p>Cache locality is poor using the simple algorithm I showed above. The performance can be easily improved however by improving the locality of the references. One way to achieve better cache locality is to use one dimensional array instead of two dimensional array and as you will see later, the performance of the following implementation has as much as 50% speed gains over the previous OpenMP implementation using two dimensional arrays.</p>
<pre class="brush: cpp;">
float *a, *b, *c;

a = new float[matrix_size * matrix_size];
b = new float[matrix_size * matrix_size];
c = new float[matrix_size * matrix_size];

for (long i=0; i&lt;matrix_size * matrix_size; i++)
{
    a[i]=rand();
    b[i] = rand();
    c[i] = 0;
}

#pragma omp parallel for shared(a,b,c)
for (long i=0; i&lt;matrix_size; i++)
{
    for (long j = 0; j &lt; matrix_size; j++)
    {
        long idx = i * matrix_size;
        float sum = 0;
        for (long k = 0; k &lt; matrix_size; k++)
        {
            sum +=a[idx + k]*b[k * matrix_size +j];
        }
        c[idx + j] = sum;
    }
}

delete a;
delete b;
delete c;
</pre>
<h4>Boost Library uBLAS (Single Threaded)</h4>
<p>Boost library provides a convenient way to perform matrix multiplication. However, the performance is very poor compared to all other approaches mentioned in this article. The performance of the uBLAS implementation is largely on par with that using C# (see benchmarks towards the end of the article). Intel&#8217;s Math Kernal Library (MKL) 10.1 does provide functionality to dynamically convert code using uBLAS syntax into highly efficient code using MKL by the inclusion of header file mkl_boost_ublas_matrix_prod.hpp. I have not tried it myself though, but the performance should be comparible to algorithms using the native MKL BLAS interface.</p>
<p>By default (without using MKL&#8217;s uBLAS capability) though, uBLAS is single threaded and due to its poor performance and I would strongly suggest avoid using uBLAS in any high performance scientific applications.</p>
<pre class="brush: cpp;">
matrix&lt;float&gt; A, B, C;

A.resize(matrix_size,matrix_size);
B.resize(matrix_size,matrix_size);

for (int i = 0; i &lt; matrix_size; ++ i)
{
    for (int j = 0; j &lt; matrix_size; ++ j)
    {
        A(i, j) = rand();
        B(i, j) = rand();
    }
}

C =prod(A, B);
</pre>
<h4>Intel Math Kernel Library (MKL) cBLAS</h4>
<p>Intel&#8217;s Math Kernel Library (MKL) is highly optimized on Intel&#8217;s microprocessor platforms. Given that Intel developed this library for its own processor platforms we can expect significant performance gains. I am still surprised at how fast the code runs using cBLAS though. In fact, it was so fast that I doubted the validity of the result at first. But after checking the results against those obtained by other means, those doubts were putting into rest.</p>
<p>The cBLAS matrix multiplication uses blocked matrix multiplication method which further improves cache locality. And it is more than thirty times faster then the fastest OMP 1D algorithm listed above! Another benefit is that by default it automatically detects the number of CPUs/cores available and uses all available threads. This behavior greatly simplifies the code since threading is handled transparently within the library.</p>
<pre class="brush: cpp;">
float *A, *B, *C;

A = new float[matrix_size * matrix_size];
B = new float[matrix_size * matrix_size];
C = new float[matrix_size * matrix_size];

for (int i = 0; i &lt; matrix_size * matrix_size; i++)
{
    A[i] = rand();
    B[i] = rand();
    C[i] = 0;
}

cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans,
    matrix_size,  matrix_size,  matrix_size, 1.0, A,matrix_size,
    B, matrix_size, 0.0, C, matrix_size);
</pre>
<h4>MATLAB (Single Threaded)</h4>
<p>MATLAB is known for its efficient algorithms. In fact it uses BLAS libraries for its own matrix calculation routines. The version of MATLAB I have is a little dated (7.0.1), but nevertheless it would be interesting to see how its performance compares with that of latest MKL&#8217;s. MATLAB 7 is single threaded, and given the same matrix size, it runs roughly three times slower than the fastest MKL routine listed above (per core).</p>
<pre>    a = rand(i,i);
    b = rand(i,i);
    tic;
    c = a*b;
    t = toc</pre>
<p>
The following table shows the results I obtained by running the code listed above. The results are time in seconds. (note, S.TH means single threaded and M.TH means multi-threaded).</p>
<table border="0" cellspacing="0" frame="void" rules="none">
<colgroup>
<col width="116"></col>
<col width="116"></col>
<col width="116"></col>
<col width="116"></col>
<col width="116"></col>
<col width="116"></col>
<col width="116"></col>
</colgroup>
<tbody>
<tr>
<td style="border: 1px solid #000000;" width="116" height="17" align="center" bgcolor="#008080"><strong><span style="color: #ffffff;">Size/Algorithm</span></strong></td>
<td style="border: 1px solid #000000;" width="116" align="center" bgcolor="#008080"><strong><span style="color: #ffffff;">uBLAS S.TH</span></strong></td>
<td style="border: 1px solid #000000;" width="116" align="center" bgcolor="#008080"><strong><span style="color: #ffffff;">STD S.TH</span></strong></td>
<td style="border: 1px solid #000000;" width="116" align="center" bgcolor="#008080"><strong><span style="color: #ffffff;">OMP 2D</span></strong></td>
<td style="border: 1px solid #000000;" width="116" align="center" bgcolor="#008080"><strong><span style="color: #ffffff;">OMP 1D</span></strong></td>
<td style="border: 1px solid #000000;" width="116" align="center" bgcolor="#008080"><strong><span style="color: #ffffff;">MATLAB S.TH</span></strong></td>
<td style="border: 1px solid #000000;" width="116" align="center" bgcolor="#008080"><strong><span style="color: #ffffff;">cBLAS M.TH</span></strong></td>
</tr>
<tr>
<td style="border: 1px solid #000000;" height="17" align="right" bgcolor="#008080"><strong><span style="color: #ffffff;">500&#215;500</span></strong></td>
<td style="border: 1px solid #000000;" align="right">3.2435</td>
<td style="border: 1px solid #000000;" align="right">0.5253</td>
<td style="border: 1px solid #000000;" align="right">0.1939</td>
<td style="border: 1px solid #000000;" align="right">0.0536</td>
<td style="border: 1px solid #000000;" align="right">0.0810</td>
<td style="border: 1px solid #000000;" align="right">0.0206</td>
</tr>
<tr>
<td style="border: 1px solid #000000;" height="17" align="right" bgcolor="#008080"><strong><span style="color: #ffffff;">600&#215;600</span></strong></td>
<td style="border: 1px solid #000000;" align="right">5.7854</td>
<td style="border: 1px solid #000000;" align="right">0.9349</td>
<td style="border: 1px solid #000000;" align="right">0.3223</td>
<td style="border: 1px solid #000000;" align="right">0.1655</td>
<td style="border: 1px solid #000000;" align="right">0.1410</td>
<td style="border: 1px solid #000000;" align="right">0.0093</td>
</tr>
<tr>
<td style="border: 1px solid #000000;" height="17" align="right" bgcolor="#008080"><strong><span style="color: #ffffff;">700&#215;700</span></strong></td>
<td style="border: 1px solid #000000;" align="right">9.2292</td>
<td style="border: 1px solid #000000;" align="right">1.2928</td>
<td style="border: 1px solid #000000;" align="right">0.3529</td>
<td style="border: 1px solid #000000;" align="right">0.2797</td>
<td style="border: 1px solid #000000;" align="right">0.2230</td>
<td style="border: 1px solid #000000;" align="right">0.0122</td>
</tr>
<tr>
<td style="border: 1px solid #000000;" height="17" align="right" bgcolor="#008080"><strong><span style="color: #ffffff;">800&#215;800</span></strong></td>
<td style="border: 1px solid #000000;" align="right">13.7711</td>
<td style="border: 1px solid #000000;" align="right">2.3746</td>
<td style="border: 1px solid #000000;" align="right">0.7259</td>
<td style="border: 1px solid #000000;" align="right">0.4135</td>
<td style="border: 1px solid #000000;" align="right">0.3320</td>
<td style="border: 1px solid #000000;" align="right">0.0310</td>
</tr>
<tr>
<td style="border: 1px solid #000000;" height="17" align="right" bgcolor="#008080"><strong><span style="color: #ffffff;">900&#215;900</span></strong></td>
<td style="border: 1px solid #000000;" align="right">20.3245</td>
<td style="border: 1px solid #000000;" align="right">3.4983</td>
<td style="border: 1px solid #000000;" align="right">1.0146</td>
<td style="border: 1px solid #000000;" align="right">0.7449</td>
<td style="border: 1px solid #000000;" align="right">0.4740</td>
<td style="border: 1px solid #000000;" align="right">0.0306</td>
</tr>
<tr>
<td style="border: 1px solid #000000;" height="17" align="right" bgcolor="#008080"><strong><span style="color: #ffffff;">1000&#215;1000</span></strong></td>
<td style="border: 1px solid #000000;" align="right">28.8345</td>
<td style="border: 1px solid #000000;" align="right">3.4983</td>
<td style="border: 1px solid #000000;" align="right">1.4748</td>
<td style="border: 1px solid #000000;" align="right">1.0548</td>
<td style="border: 1px solid #000000;" align="right">0.6530</td>
<td style="border: 1px solid #000000;" align="right">0.0700</td>
</tr>
<tr>
<td style="border: 1px solid #000000;" height="17" align="right" bgcolor="#008080"><strong><span style="color: #ffffff;">1100&#215;1100</span></strong></td>
<td style="border: 1px solid #000000;" align="right">38.2545</td>
<td style="border: 1px solid #000000;" align="right">7.0240</td>
<td style="border: 1px solid #000000;" align="right">1.9383</td>
<td style="border: 1px solid #000000;" align="right">1.6257</td>
<td style="border: 1px solid #000000;" align="right">0.8620</td>
<td style="border: 1px solid #000000;" align="right">0.1250</td>
</tr>
<tr>
<td style="border: 1px solid #000000;" height="17" align="right" bgcolor="#008080"><strong><span style="color: #ffffff;">1200&#215;1200</span></strong></td>
<td style="border: 1px solid #000000;" align="right">50.4964</td>
<td style="border: 1px solid #000000;" align="right">9.9319</td>
<td style="border: 1px solid #000000;" align="right">2.8411</td>
<td style="border: 1px solid #000000;" align="right">2.1215</td>
<td style="border: 1px solid #000000;" align="right">1.1170</td>
<td style="border: 1px solid #000000;" align="right">0.0440</td>
</tr>
<tr>
<td style="border: 1px solid #000000;" height="17" align="right" bgcolor="#008080"><strong><span style="color: #ffffff;">1300&#215;1300</span></strong></td>
<td style="border: 1px solid #000000;" align="right">64.5064</td>
<td style="border: 1px solid #000000;" align="right">12.8344</td>
<td style="border: 1px solid #000000;" align="right">3.6277</td>
<td style="border: 1px solid #000000;" align="right">2.9720</td>
<td style="border: 1px solid #000000;" align="right">1.4250</td>
<td style="border: 1px solid #000000;" align="right">0.0440</td>
</tr>
<tr>
<td style="border: 1px solid #000000;" height="17" align="right" bgcolor="#008080"><strong><span style="color: #ffffff;">1400&#215;1400</span></strong></td>
<td style="border: 1px solid #000000;" align="right">81.1826</td>
<td style="border: 1px solid #000000;" align="right">17.1119</td>
<td style="border: 1px solid #000000;" align="right">4.8309</td>
<td style="border: 1px solid #000000;" align="right">3.5977</td>
<td style="border: 1px solid #000000;" align="right">1.7760</td>
<td style="border: 1px solid #000000;" align="right">0.0938</td>
</tr>
<tr>
<td style="border: 1px solid #000000;" height="17" align="right" bgcolor="#008080"><strong><span style="color: #ffffff;">1500&#215;1500</span></strong></td>
<td style="border: 1px solid #000000;" align="right">100.1330</td>
<td style="border: 1px solid #000000;" align="right">21.0622</td>
<td style="border: 1px solid #000000;" align="right">6.1689</td>
<td style="border: 1px solid #000000;" align="right">4.8022</td>
<td style="border: 1px solid #000000;" align="right">2.1870</td>
<td style="border: 1px solid #000000;" align="right">0.1111</td>
</tr>
<tr>
<td style="border: 1px solid #000000;" height="17" align="right" bgcolor="#008080"><strong><span style="color: #ffffff;">1600&#215;1600</span></strong></td>
<td style="border: 1px solid #000000;" align="right">120.3400</td>
<td style="border: 1px solid #000000;" align="right">26.4316</td>
<td style="border: 1px solid #000000;" align="right">7.3189</td>
<td style="border: 1px solid #000000;" align="right">5.0451</td>
<td style="border: 1px solid #000000;" align="right">2.6490</td>
<td style="border: 1px solid #000000;" align="right">0.1699</td>
</tr>
<tr>
<td style="border: 1px solid #000000;" height="17" align="right" bgcolor="#008080"><strong><span style="color: #ffffff;">1700&#215;1700</span></strong></td>
<td style="border: 1px solid #000000;" align="right">145.8550</td>
<td style="border: 1px solid #000000;" align="right">31.2706</td>
<td style="border: 1px solid #000000;" align="right">8.7525</td>
<td style="border: 1px solid #000000;" align="right">6.8915</td>
<td style="border: 1px solid #000000;" align="right">3.1870</td>
<td style="border: 1px solid #000000;" align="right">0.1452</td>
</tr>
<tr>
<td style="border: 1px solid #000000;" height="17" align="right" bgcolor="#008080"><strong><span style="color: #ffffff;">1800&#215;1800</span></strong></td>
<td style="border: 1px solid #000000;" align="right">174.6860</td>
<td style="border: 1px solid #000000;" align="right">38.9293</td>
<td style="border: 1px solid #000000;" align="right">11.1060</td>
<td style="border: 1px solid #000000;" align="right">8.1316</td>
<td style="border: 1px solid #000000;" align="right">3.7940</td>
<td style="border: 1px solid #000000;" align="right">0.1989</td>
</tr>
<tr>
<td style="border: 1px solid #000000;" height="17" align="right" bgcolor="#008080"><strong><span style="color: #ffffff;">1900&#215;1900</span></strong></td>
<td style="border: 1px solid #000000;" align="right">206.0520</td>
<td style="border: 1px solid #000000;" align="right">45.8589</td>
<td style="border: 1px solid #000000;" align="right">13.0832</td>
<td style="border: 1px solid #000000;" align="right">9.9527</td>
<td style="border: 1px solid #000000;" align="right">4.4450</td>
<td style="border: 1px solid #000000;" align="right">0.2725</td>
</tr>
<tr>
<td style="border: 1px solid #000000;" height="17" align="right" bgcolor="#008080"><strong><span style="color: #ffffff;">2000&#215;2000</span></strong></td>
<td style="border: 1px solid #000000;" align="right">240.7820</td>
<td style="border: 1px solid #000000;" align="right">55.4392</td>
<td style="border: 1px solid #000000;" align="right">16.0542</td>
<td style="border: 1px solid #000000;" align="right">11.0314</td>
<td style="border: 1px solid #000000;" align="right">5.1820</td>
<td style="border: 1px solid #000000;" align="right">0.3359</td>
</tr>
</tbody>
</table>
<p style="text-align: left;">
The following figure shows the results. Since uBLAS and single threaded matrix multiplications took significantly longer to compute, I did not include them in the figure below.
</p>
<p style="text-align: center;">
<a href="http://www.kerrywong.com/blog/wp-content/uploads/2009/03/linearplot.png"><img class="size-full wp-image-645" title="Matrix Multiplication (Linear)" src="http://www.kerrywong.com/blog/wp-content/uploads/2009/03/linearplot.png" alt="Matrix Multiplication (Linear)" width="560" height="420" /></a></p>
<p style="text-align: left;">
The following figure shows the same data but uses log-scale Y axis instead so that all the data can show up nicely. You can get a sense of various algorithms&#8217; efficiencies here:</p>
<p style="text-align: center;">
<a href="http://www.kerrywong.com/blog/wp-content/uploads/2009/03/logplot.png"><img class="alignnone size-full wp-image-646"  title="Matrix Multiplication (Log)" src="http://www.kerrywong.com/blog/wp-content/uploads/2009/03/logplot.png" alt="Matrix Multiplication (Log)" width="560" height="420" /></a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.kerrywong.com/2009/03/07/matrix-multiplication-performance-in-c/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>Setting up WordPress on Ubuntu Server &#8212; Step by Step</title>
		<link>http://www.kerrywong.com/2009/02/22/setting-up-wordpress-on-ubuntu-server-step-by-step/</link>
		<comments>http://www.kerrywong.com/2009/02/22/setting-up-wordpress-on-ubuntu-server-step-by-step/#comments</comments>
		<pubDate>Mon, 23 Feb 2009 03:00:28 +0000</pubDate>
		<dc:creator>kwong</dc:creator>
				<category><![CDATA[Linux/BSD]]></category>
		<category><![CDATA[setup]]></category>
		<category><![CDATA[tutorial]]></category>
		<category><![CDATA[Ubuntu Server]]></category>
		<category><![CDATA[WordPress]]></category>

		<guid isPermaLink="false">http://www.kerrywong.com/?p=552</guid>
		<description><![CDATA[I wrote a tutorial on how to setup WordPress on Ubuntu Server back in 2007. While the setup steps have largely remained the same from version 6.10 to 8.04 (both are long term support LTS versions), I decided to create an other guide focusing on the setup processes with Ubuntu Server 8.04. The setup steps [...]]]></description>
			<content:encoded><![CDATA[<p>I wrote a tutorial on <a href="/2007/09/26/setting-up-wordpress-on-ubuntu-server/">how to setup WordPress on Ubuntu Server back in 2007</a>. While the setup steps have largely remained the same from version 6.10 to 8.04 (both are long term support LTS versions), I decided to create an other guide focusing on the setup processes with Ubuntu Server 8.04.<span id="more-552"></span> The setup steps are actually a bit easier in 8.04 as Samba has been fully integrated into the initial setup process. I have also included some screen shots from the text-mode Ubuntu Server setup so that newbies would find the task of setting up the server less daunting.</p>
<p><strong>1. Ubuntu Server Setup</strong></p>
<p>The following screen shots are taken while installing Ubuntu Server 8.04 (note the latest Ubuntu Server Edition is 8.04.2)</p>
<p><img alt="" src="http://www.kerrywong.com/blog/wp-content/uploads/2009/02/wp_001.png" /></p>
<p><img alt="" src="http://www.kerrywong.com/blog/wp-content/uploads/2009/02/wp_002.png" /></p>
<p><img alt="" src="http://www.kerrywong.com/blog/wp-content/uploads/2009/02/wp_003.png" /></p>
<p><img alt="" src="http://www.kerrywong.com/blog/wp-content/uploads/2009/02/wp_004.png" /></p>
<p>While the default choice is Yes (Detect keyboard layout), I have found that that for the keyboards used here in the US, skipping the auto-detection step is actually easier.</p>
<p><img alt="" src="http://www.kerrywong.com/blog/wp-content/uploads/2009/02/wp_005.png" /></p>
<p><img alt="" src="http://www.kerrywong.com/blog/wp-content/uploads/2009/02/wp_006.png" /></p>
<p><img alt="" src="http://www.kerrywong.com/blog/wp-content/uploads/2009/02/wp_007.png" /></p>
<p>This is the host name you want to use. For whatever reason, the default Ubuntu Server setting does not set the host-name correctly using DHCP (for instance, you can not use vm-wordpress to locate the server from a Windows machine). This can be addressed easily by ensuring that the following lines are present in <strong><em>/etc/dhcp3/dhclient.conf</em></strong>:</p>
<p>send host-name &quot;&lt;hostname&gt;&quot;;<br />
request subnet-mask, broadcast-address, time-offset, routers, domain-name, domain-name-servers, host-name, ntp-servers;</p>
<p><img alt="" src="http://www.kerrywong.com/blog/wp-content/uploads/2009/02/wp_008.png" /></p>
<p><img alt="" src="http://www.kerrywong.com/blog/wp-content/uploads/2009/02/wp_009.png" /></p>
<p>For a dedicated server, the default partitioning scheme (use entire disk) should be suffice.</p>
<p><img alt="" src="http://www.kerrywong.com/blog/wp-content/uploads/2009/02/wp_010.png" /></p>
<p><img alt="" src="http://www.kerrywong.com/blog/wp-content/uploads/2009/02/wp_011.png" /></p>
<p><img alt="" src="http://www.kerrywong.com/blog/wp-content/uploads/2009/02/wp_012.png" /></p>
<p><img alt="" src="http://www.kerrywong.com/blog/wp-content/uploads/2009/02/wp_013.png" /></p>
<p><img alt="" src="http://www.kerrywong.com/blog/wp-content/uploads/2009/02/wp_014.png" /></p>
<p><img alt="" src="http://www.kerrywong.com/blog/wp-content/uploads/2009/02/wp_015.png" /></p>
<p><img alt="" src="http://www.kerrywong.com/blog/wp-content/uploads/2009/02/wp_016.png" /></p>
<p><img alt="" src="http://www.kerrywong.com/blog/wp-content/uploads/2009/02/wp_017.png" /></p>
<p>If you are setting it up on your home computer, this is usually blank.</p>
<p><img alt="" src="http://www.kerrywong.com/blog/wp-content/uploads/2009/02/wp_018.png" /></p>
<p><img alt="" src="http://www.kerrywong.com/blog/wp-content/uploads/2009/02/wp_019.png" /></p>
<p>For the WordPress server setup LAMP and OpenSSH are the only required components. However, I usually add the Print Server (CUPS) so that the whole home network can share the printer on the WordPress server (since it is always on) and Samba File Server so that I can copy files from a Windows machine.</p>
<p><img alt="" src="http://www.kerrywong.com/blog/wp-content/uploads/2009/02/wp_020.png" /></p>
<p><img alt="" src="http://www.kerrywong.com/blog/wp-content/uploads/2009/02/wp_021.png" /></p>
<p>Don&#8217;t confuse MySQL root user password with the Ubuntu login password. They can be the same but it is recommended that you use a different strong password for each.</p>
<p><img alt="" src="http://www.kerrywong.com/blog/wp-content/uploads/2009/02/wp_022.png" /></p>
<p><img alt="" src="http://www.kerrywong.com/blog/wp-content/uploads/2009/02/wp_023.png" /></p>
<p>We are almost done!</p>
<p><img alt="" src="http://www.kerrywong.com/blog/wp-content/uploads/2009/02/wp_024.png" /></p>
<p>If everything goes well, you should be greeted with this login screen after your machine is re-booted.</p>
<p>As a best practice, you should always ensure that your distribution is up-to-date by getting the latest patches/software:</p>
<p>This can be achieved by issuing the following commands upon log in:</p>
<div>sudo apt-get update</div>
<div>sudo apt-get dst-upgrade</div>
<p>A reboot maybe necessary if kernel image is updated.</p>
<p><strong>2. Create Samba Share</strong></p>
<p>We have already installed Samba during the Ubuntu Server install. To use it we need to first configure it properly.</p>
<p>sudo vi /etc/samba/smb.conf</p>
<p>at the end add the following section:</p>
<div>[<em>{Samba Share Name}</em>]</div>
<div>path=/</div>
<div>valid users= <em>{User Name}</em></div>
<div>read only = no</div>
<div>create mask = 0666</div>
<div>directory mask = 0777</div>
<p>Now we can add a user for Samba share:</p>
<div>
<div>sudo smbpasswd -a <em>{user name}</em></div>
<div>sudo /etc/init.d/samba restart</div>
<p>After Samba is configured, you should be able to see it via smb://vm-wordpress/sambaroot/ (using the user name and password you just added) in Linux or \\vm-wordpress\sambaroot\ in Windows.</p>
</div>
<p><strong>3. Install WordPress</strong></p>
<p>In your user directory (e.g. /home/<em>{user name}</em>):</p>
<div>wget http://wordpress.org/latest.tar.gz</div>
<div>tar -zxvf latest.tar.gz</div>
<div>sudo mv ./wordpress/ /var/www/blog</div>
<p><strong>4. Configure Database<br />
</strong></p>
<div>mysql -u root -p</div>
<p>mysql&gt; create database wordpress;<br />
mysql&gt; grant all privileges on wordpress.* to &quot;wpuser&quot;@&quot;localhost&quot; identified by &quot;password&quot;;<br />
mysql&gt; flush privileges;<br />
mysql&gt; exit</p>
<p>In my <a href="/2007/09/26/setting-up-wordpress-on-ubuntu-server/">previous tutorial</a>, I used <strong>phpMyAdmin</strong> as the SQL tool. While it is much easier to use then the mysql command line, it nevertheless adds some risk if you allow database logins remotely. So I would not recommand installing it on the production server.</p>
<p><strong>5. Configure WordPress</strong></p>
<div>mv /var/www/blog/wp-config-sample.php /var/www/blog/wp-config.php</div>
<p>
Edit the content of wp-config.php:</p>
<div>define(&#8216;DB_NAME&#8217;, &#8216;wordpress&#8217;);&nbsp;&nbsp;&nbsp; // The name of the database</div>
<div>define(&#8216;DB_USER&#8217;, &#8216;username&#8217;);&nbsp;&nbsp;&nbsp;&nbsp; // Your MySQL username</div>
<div>define(&#8216;DB_PASSWORD&#8217;, &#8216;password&#8217;); // &#8230;and password</div>
<div>define(&#8216;DB_HOST&#8217;, &#8216;localhost&#8217;);</div>
<p>
Since our WordPress installation is located in the blog directory, the default url will be http://yourdomain/blog/. If you want your blog to be accessible from the root (e.g. http://yourdomain/) you will need to modify /var/www/blog/index.php.</p>
<p>First copy it to www root:</p>
<div>cp /var/www/blog/index.php /var/www/</div>
<p>
And change the require() line to point to the new path: require(&#8216;blog/wp-blog-header.php&#8217;).</p>
<p><strong>a. for new setup</strong></p>
<p>Run the setup script at http://yourdomain/blog/wp-admin/install.php</p>
<p>After running the installation script, we want to make sure that our blog is pointed at the correct location. go to http://yourdomain/blog/wp-adming/, select options. In General Options section, you will see one line like &quot;WordPress address (URL)&quot; and another line like &quot;Blog address (URL)&quot;. This is the place where you specify how your blog main url looks to the outside world. If you want your blog to appear as http://yourdomain/ then make sure WordPress address is pointed to http://yourdomain/blog and Blog address is pointed to http://yourdomain. If you want your blog to appear as http://yourdomain/blog/ then make sure both entries are the same (http://yourdomain/blog/).</p>
<p>Now the installation of WordPress is complete.</p>
<p>If you need URL rewrite capability, you will need to load the URL rewrite module:</p>
<div>sudo a2enmod rewrite</div>
<div>sudo /etc/init.d/apache2 force-reload</div>
<p><strong>b. for existing setup (restoring a previously backed-up database)</strong></p>
<div>mysql -u [username] -p [password] [database_to_restore] &lt; [backupfile]</div>
<div>http://yourdomain/blog/wp-admin/upgrade.php</div>
<p>For a detailed discussing on how to perform backup/restore on MySQL database, please refer to <a href="http://www.devshed.com/c/a/MySQL/Backing-up-and-restoring-your-MySQL-Database/">this article</a>.</p>
<p>If you need to access the MySQL database from another machine that is on the LAN, you will want to change /etc/mysql/my.cnf and modify the bind-address to the server address, <a href="http://www.cyberciti.biz/tips/how-do-i-enable-remote-access-to-mysql-database-server.html">you can read more here</a>.</p>
<p>I omitted some content which are less commonly used in this article. You can refer to my&nbsp;<a href="/2007/09/26/setting-up-wordpress-on-ubuntu-server/">previous tutorial</a> for more information.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kerrywong.com/2009/02/22/setting-up-wordpress-on-ubuntu-server-step-by-step/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
		<item>
		<title>Simple WordPress Standby Server</title>
		<link>http://www.kerrywong.com/2009/02/08/simple-wordpress-standby-server/</link>
		<comments>http://www.kerrywong.com/2009/02/08/simple-wordpress-standby-server/#comments</comments>
		<pubDate>Mon, 09 Feb 2009 02:52:52 +0000</pubDate>
		<dc:creator>kwong</dc:creator>
				<category><![CDATA[Linux/BSD]]></category>
		<category><![CDATA[Backup]]></category>
		<category><![CDATA[Server]]></category>
		<category><![CDATA[Standby]]></category>
		<category><![CDATA[VMWare]]></category>
		<category><![CDATA[WordPress]]></category>

		<guid isPermaLink="false">http://www.kerrywong.com/?p=535</guid>
		<description><![CDATA[I mentioned in one previous post on how to do a physical to VM backup using simple Linux command. The method works pretty well as long as the kernel versions are kept in sync. But for my Web server running WordPress,this backup method seemed to be an overkill. I use the following method to setup [...]]]></description>
			<content:encoded><![CDATA[<p>I mentioned in one previous post on how to do a<a href="/2007/10/05/a-physical-to-vm-backup-strategy-for-ubuntu-server/"> physical to VM backup</a> using simple Linux command. The method works pretty well as long as the kernel versions are kept in sync. <span id="more-535"></span></p>
<p>But for my Web server running WordPress,this backup method seemed to be an overkill. I use the following method to setup a standby WordPress server (can be either a VM or a physical machine). The backup server is always running and it&#8217;s content is synced up with the production server. In the event of a production server failure, all I need to do is to re-route the port 80 traffic to the standby server, which can be achieved in a matter of seconds.</p>
<p>To setup the standby server, I first installed Ubuntu server 8.04.1 (the same version as on my production server, with LAMP), and copied the WordPress content over (of course, most of the configurations need to be identical to the production server as well). To sync up the database content and the custom contents (e.g. uploaded images), the following command is scheduled to run on the server on a daily basis, which essentially backs up the content database:</p>
<div>mysqldump <em>[wordpress db name]</em> -p<em>[password]</em> &gt;&nbsp; /<em>[backup directory root]</em>/<em>[backup file name]</em></div>
<p>&nbsp;</p>
<p>on the standby server, the following commands are scheduled using cron daily to pick up the changes in production environment:</p>
<div>rcp <em>[user name]</em>@<em>[production server]</em>:<em>[backup directory root]</em>/<em>[backup file name]</em> ./dbbackup/</div>
<div>mysql -u<em>[user name]</em> -p<em>[password]</em> <em>[wordpress db name]</em> &lt; ./dbbackup/<em>[backup file name]</em></div>
<div>rcp -r <em>[user name]</em>@<em>[production server]</em>:/var/www/<em>[wp installation dir]</em>/wp-content/uploads /var/www/blog/wp-content</div>
<p>&nbsp;</p>
<p>The above commands essentially restore the production database snapshot onto the standby server and sync up the custom contents.</p>
<p>One benefit of this hot sync technique is that the standby server is always live and can be tested readily by changing the host entry in /etc/hosts to point the production url to the standby server.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kerrywong.com/2009/02/08/simple-wordpress-standby-server/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Parsing Apache Server Log for Unique IPs</title>
		<link>http://www.kerrywong.com/2009/02/01/parsing-apache-server-log-for-unique-ips/</link>
		<comments>http://www.kerrywong.com/2009/02/01/parsing-apache-server-log-for-unique-ips/#comments</comments>
		<pubDate>Sun, 01 Feb 2009 16:41:01 +0000</pubDate>
		<dc:creator>kwong</dc:creator>
				<category><![CDATA[Linux/BSD]]></category>
		<category><![CDATA[Miscellaneous]]></category>
		<category><![CDATA[Apache]]></category>
		<category><![CDATA[Perl]]></category>
		<category><![CDATA[script]]></category>
		<category><![CDATA[Server Log]]></category>
		<category><![CDATA[Unique IP]]></category>

		<guid isPermaLink="false">http://www.kerrywong.com/?p=531</guid>
		<description><![CDATA[I created this short Perl script to tally the number of unique IPs from the Apache server log. Since by default, the logrotate daemon compresses Apache logs, we need to disable the compression option for the Apache log configuration. In Ubuntu, this setting can be changed in /etc/logrotate.d/apache2. To disable compression, just comment out the [...]]]></description>
			<content:encoded><![CDATA[<p>I created this short Perl script to tally the number of unique IPs from the Apache server log.<span id="more-531"></span> Since by default, the logrotate daemon compresses Apache logs, we need to disable the compression option for the Apache log configuration. In Ubuntu, this setting can be changed in /etc/logrotate.d/apache2. To disable compression, just comment out the following lines:</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #compress<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #delaycompress</p>
<p>The Perl script is quite straight forward: it basically builds a hash table and use the IP address as the key:</p>
<div>#!/usr/bin/perl</div>
<div>
$apacheLogDir = &quot;/var/log/apache2&quot;;</div>
<div>
opendir(DIR, $apacheLogDir);</div>
<div>@files = grep(/^access/, readdir(DIR));</div>
<div>closedir(DIR);</div>
<div>
%hashTable=();</div>
<div>$uniqueIP = 0;</div>
<div>foreach $file (@files) {</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; open FILE, &quot;$apacheLogDir&quot; . &quot;/&quot; . &quot;$file&quot; or die $!;</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @lines = &lt;FILE&gt;;</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; foreach $line (@lines) {</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @IP = split(/\s+/, $line);</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (exists($hashTable{$IP[0]})) {</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $hashTable{$IP[0]}++;</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else {</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $hashTable{$IP[0]} = 1;</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $uniqueIP++;</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #$resolved = qx{resolveip $IP[0]};</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #print &quot;$resolved&quot;;</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print &quot;$IP[0]\n&quot;;</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; close(FILE);</div>
<div>}</div>
<div>
print &quot;Total unique IPs: $uniqueIP.\n&quot;;</div>
<p>&nbsp;</p>
<p>The two lines commented out:</p>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #$resolved = qx{resolveip $IP[0]};<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #print &quot;$resolved&quot;;</div>
<p>can be used to identify the domain name the IP is associated with. Since reverse DNS lookup is a rather slow process, it will take a long time to resolve all the IPs. I may change it to use multiple threads to speed up the DNS lookup in the further.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kerrywong.com/2009/02/01/parsing-apache-server-log-for-unique-ips/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Site Up Again</title>
		<link>http://www.kerrywong.com/2008/12/23/site-up-again/</link>
		<comments>http://www.kerrywong.com/2008/12/23/site-up-again/#comments</comments>
		<pubDate>Wed, 24 Dec 2008 02:15:55 +0000</pubDate>
		<dc:creator>kwong</dc:creator>
				<category><![CDATA[Linux/BSD]]></category>
		<category><![CDATA[Miscellaneous]]></category>
		<category><![CDATA[ddrescure]]></category>

		<guid isPermaLink="false">http://www.kerrywong.com/?p=493</guid>
		<description><![CDATA[This afternoon I began to receive &#34;Error establishing a database connection&#34; error from my site. And when trying to login via ssh, the server hangs after the password is entered.As I tried a few more times to log in, it became clear that something was wrong, as I was getting &#34;ssh_exchange_identification: Connection closed by remote [...]]]></description>
			<content:encoded><![CDATA[<p>This afternoon I began to receive &quot;Error establishing a database connection&quot; error from my site. And when trying to login via ssh, the server hangs after the password is entered.<span id="more-493"></span>As I tried a few more times to log in, it became clear that something was wrong, as I was getting &quot;ssh_exchange_identification: Connection closed by remote host&quot; error instead. After I got home, I tried to reboot the server. Then came the dreadful clicking sound, I realized that the hard disk had failed. Luckily, I have a Virtual Machine on standby, and it took less than an hour to get the website back and running again.</p>
<p>Even though I have up-to-date backup of my site and I restored it without too much a fuss, I was still interested in recovering the data from the failed drive.</p>
<p>I mounted the drive on another computer and used ddrescure to dump the primary Linux partition into a file. It seems that the partition is pretty much intact as I did not get any error while retrieving the image from the failed disk. Note that if you want to mount the dumped image, you would need to dump only the primary partition and not the whole disk (in my case it was /dev/sdd1 instead of /dev/sdd). After successfully dumped the disk image, I was able to see everything on the failed drive by mounting the image to a directory:</p>
<p><font face="Courier New">sudo mount -o loop ./prod.img /mnt -t ext3</font></p>
<p>Over the next couple of days I will rebuild my production box and transfer the site back from the VM.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kerrywong.com/2008/12/23/site-up-again/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
