« May 2004 | Main | July 2004 »

June 26, 2004

Network That Printer

Listen to this articleListen to this article

Today I shall depart from my usual software development rants. It's not often that a piece of hardware tickles my geek bone but I found something recently that did just that.

I have a 1.5Mbps DSL connection to the internet and run everything on a wireless laptop running Gentoo Linux and Windows under VM Ware for the times when I absolutely need to test something on windows.

The only piece of equipment I need to connect to my laptop is my ancient HP Laserject 5MP printer. Or should I say, needed. On Thursday I picked up a NetGear PS101 Mini PrintServer. A tiny (half-cigarette packet-sized) device that attaches directly to the printer and then via a CAT-3 cable to my hub. Granted it's not wireless (though NetGear and LinkSys do have them) but at AU$115 it's sensational value for money.

It supports both static and DHCP (the default) IP address assignment. It comes with Windows drivers (if you really feel the need hehe) but importantly took no more than about 5-mins to get working with my CUPS installation on Gentoo. I just needed to work out its IP address and internal print server name and set CUPS to print to:

lpd://ip_address/internel_print_server_name

Don't forget to set your iptables configuration to allow printing on port 515 (printer):

iptables -A OUTPUT -p tcp --dport printer  -m state --state NEW -j ACCEPT

And you're done. Sweeeet.

June 25, 2004

Well Behaved Rules

Listen to this articleListen to this article

I have previously made a comparison between rule engines (and the RETE algorithm in particular) and SQL databases. Business rule languages are declarative as is SQL, both being based on predicate calculus. Both suffer (or at least have suffered) similar problems in terms of performance and optimisation.

I recall many years ago, tuning my queries within an in inch of their (or my more likely) life. Re-ordering the WHERE clause, changing JOIN conditions, even changing the order in which columns were returned.

Thankfully these days, even the simplest of SQL database engines have some form of optimisation built-in. High-end systems such as Oracle have very sophisticated optimisation techniques. I can pretty much write any old SQL (with caveats) and know that I'll get at least acceptable performance in most cases.

The RETE algorithm (and it's successor RETE-II) is amazingly good and rule engines have also come a long way but certainly not as much as ye-olde RDBMS. So there are still some things you need to consider when writing rules.

Without going into too much detail, the RETE algorithm builds a network of nodes representing the conditions of your rules and the matching facts. In general, the smaller the network, the better the performance.

The first thing to note is that any rules sharing common conditons are optimised into a single node. However, with many rule engines, this is sometimes only possible if the conditions are listed in the same order. So for any N rules having M conditions in common, order the conditions so that the first M are the same.

Now that your are conditions are in the same order, you'll be interested to know that the exact order is in fact important. Because each condition is like an SQL JOIN, you need to place the MOST restrictive conditions first. That is, place the condition that is LEAST likely to be matched FIRST. This is no doubt familiar to anyone who has ever tuned SQL.

Iimagine we're trying to find two people with the same parent. We could do this (JRules code examples, just ask me if you want to see JESS as well):

?a: Person()
Person(getParent() == ?a.getParent())

This has one glaring problem: It's essentially a cross product! So we need to fix it:

?a: Person()
?b: Person(getParent() == ?a.getParent())
evaluate(?a != ?b)

Now, as we've shown above, the number of conditions evaluated is also important. Anytime we can short-curcuit the conditions, we save ourselves another join. So once again, we can re-write our conditions:

?a: Person()
Person(?this != ?a; getParent() == ?a.getParent())

I've found these simple techniques can result in the difference between rules running in seconds versus OutOfMemoryErrors!

To be continued...

June 23, 2004

Trivia

Listen to this articleListen to this article

I've just found out that Amazonian women cut off their right breast so as to be able to use the bow better. Guess I'll stop lusting after Amazonian women from now on!

In other news, an old friend of mine, Mike Lee, was down from Sydney today and passed on a piece of obscure Java knowledge.

He had spent some considerable time debugging a junior developers code, trying to work out what was going on with a static initialiser. He couldn't work out why it was executing just prior to, and every time, the constructor of the class being called.

Eventually he worked it out. The developer had mistakenly omitted the static keyword from the initialiser block.

Well blow me down, Java has anonymous constructors (well that's the name I've given them at least)! Yup, that's right, this is perfectly legal Java:

class Foo {
    private final String _bar;

    {
        _bar = "Hello, World";
    }
}

In fact, just like static initialisers, you can declare any number of anonymous constructors and they will all be run (in declaration order) prior to invoking a "regularly" declared constructor.

So, to all you JLS weenies out there who already knew this, you don't deserve to get a life. For the rest of us mere mortals, WTF?! :-)