Jun13

Bypassing the Python GIL with ctypes

python programming gil ctypes c | comments

I recently read an interesting article (actually, the slides linked to) about the horror that is the Global Interpreter Lock in Python, especially with multicore CPUs. And I agree — in these cases, the GIL is painful.

My favorite way of bypassing the GIL is to use ctypes, a wonderful library that allows you to easily link to dynamic libraries and call the functions from C, with only a small amount of boilerplate (to map function calls, argument types, and return types).

The best feature of ctypes is that when a program is executing a ctypes function, it releases the GIL. Meaning that if you have more than one thread threads, and one of them is busy with a ctypes call, the other threads can go along their merry way.

In the slides above, he shows that Python CPU-intensive multithreaded applications slow down as the number of CPUs increase. Well, I decided to use a quick counterexample.

First, I create a C file to do some work for me, called test.c:
int test(int from, int to)
{
  int i;
  int s = 0;
 
  for (i = from; i < to; i++)
    if (i % 3 == 0)
      s += i;
 
  return s;
}
To compile this as a dynamic shared library under OS X, the following two commands can be used:
gcc -g -fPIC -c -o test.o test.c
ld -dylib -o libtest.dylib test.o
(Under Linux, replace this last line with ld -shared -o libtest.so test.o)

Then, we can use the following Python program to load the dynamic library and run a quick test (should work in Linux or OS X):
import ctypes
import ctypes.util
import threading
import time
 
testname = ctypes.util.find_library('test')
testlib = ctypes.cdll.LoadLibrary(testname)
 
test = testlib.test
test.argtypes = [ctypes.c_int, ctypes.c_int]
 
def t():
  test(0, 1000000000)
 
if __name__ == '__main__':
  start_time = time.time()
  t()
  t()
  print "Sequential run time: %.2f seconds" % (time.time() - start_time)
 
  start_time = time.time()
  t1 = threading.Thread(target=t)
  t2 = threading.Thread(target=t)
  t1.start()
  t2.start()
  t1.join()
  t2.join()
  print "Parallel run time: %.2f seconds" % (time.time() - start_time)
On my quad-core OS X box, I get the following output:
Sequential run time: 13.27 seconds
Parallel run time: 6.66 seconds
A pretty solid doubling of performance, which is what we would hope.

May17

Presentations now up on slideshare

presentations | comments

Someone recently requested that I post some of my older presentations on miscellaneous topics.

When I was more actively involved with teaching and research at The University of Tulsa, I would often give presentations on random things. Programming, research I was doing, my anal retentiveness, whatever. We often had weekly meetings for most of my graduate program, so I would give talks there, or at ACM meetings.

I have been tracking some of these down and posting them to my slideshare account, although I will maintain a list of them (as well as descriptions) here on this site (you can always see them on the slide bar.

As a sample, here is one that is based on some (abandoned) research I did on applying genetic programming to chess.

Apr25

Netbook

netbook wireless drivers linux ubuntu | comments

I finally convinced myself to buy a netbook recently. I am currently typing this post on it.

I got an Asus EeePC 1000HA – that is, it's got a 10" screen, 160 GB hard drive (regular, not solid state), and a 6-cell battery (4.5 hours or so battery life under real-world conditions, I estimate). I'm pretty thrilled about it: typing is fine (though the right shift key has some adjustment time), the screen is nice and crisp, and it is very snappy under Windows XP and in Linux. The 1.6 GHz Atom CPU is just enough to play Netflix Instant videos, which was the only thing I was worried about performance-wise.

My biggest complaint was the wireless performance in various types of Ubuntu Linux (the newest 9.04 Netbook Remix is by far my favourite). I'd see 500+ ms ping time to my router, and drop about half of my packets, etc. It was miserable, and pretty much unusable. It turns out that the open source Atheros driver doesn't support this particular chipset very well, and the proprietary madwifi-based driver from Atheros doesn't support it at all. The only decent solution was to use the ndiswrapper package with Asus's Windows driver, which makes me feel a bit dirty.

But regardless, with all that sorted out, I won't be booting up Windows much more.

Overall, I'm impressed and very happy with my purchase.

Apr19

Responsible Development Practices: Storing Sensitive Information

development passwords practices tiddlywiki | comments

I build and maintain web sites. I manage probably hundreds of online accounts or logins or passwords that I need access to semi-regularly. Organizing all of this information can be a bit of a pain.

I've heard people swear by spreadsheets or plain text files for storing this information, and there are some specialized programs out there meant to catalog and organize this for you.

My solution is one of the simpler: TiddlyWiki. It's a small (few hundred K), self-contained (one HTML file), free file you can carry with you to store lots of information. I simply create some big list tiddlers (what they call their wiki pages), each one of which links to a specific tiddler for one particular task. So, I have a list for all of the web sites I maintain, and the tiddler for each has MySQL information, passwords, and any other crucial information.

This is great, but there are two problems. One, I am putting all of critical info in one place. To fix this, I maintain copies of this wiki file in several places, such as my thumb drive, laptop, and DropBox.

The second catch is then, what if someone finds this file? Wouldn't they have access to everything? This is why encryption is important. Whenever the file needs to be transported somewhere (like, on my thumb drive or put into DropBox), I first encrypt it. GPG is nice for this, but in a pinch, you can just use the ever-present OpenSSL to encrypt a file with something like:

openssl enc -aes-256-cbc -salt -in file.html -out file.html.encrypted


Now, as long as I remember this password, I can safely pass this encrypted file around without any worries about losing my thumb drive in the airport.

For most of the actual machines I log into via SSH, I simply use public key authentication, that way I don't have to remember the passwords.

Feb16

The perfect programming language

ruby python programming c c++ gcc | comments

I've been doing a lot of programming lately, and in a multitude of different languages. For the most part, these are pretty "decent" languages: C, Python, Ruby, etc. However, none of the languages really makes me happy.

It's hard to define exactly what I mean by "happy". They're all, for the most part, fun to program in to various degrees. But I don't really feel like I have all of the power at my fingertips in any of them. For example:

  • Python is used for general purpose whatever. It's powerful, easy to read, easy to write, and fast.
  • C is used for speed. When I need to be screaming down the highway, I break down and code in C (perhaps callable in a higher-level language).
  • Ruby is used for much of my web development.


I don't really find Python that well-suited for web development, although I use it that way quite often, just as much as I find Ruby to be a bit odd at general purpose computing (say, doing a Project Euler problem). And I don't really break out the C unless I have to. It makes a two-week-long Python program turn into a three-month adventure in passing structs around.

Some people have commented that maybe what I need is C++, especially with the Boost libraries. It's not as easy as Python, but not as ridiculous as C all the time. And they may be right, but I'm not quite sold yet.

The primary reason for my displeasure with C++ is primarily two things. First, it's syntax is stuck back in 1969. Why do we need so many braces, so many semi-colons? The overused and overloaded &, *, ^, etc. operators really bother me, as they make things just so terse and ugly. I mean, if I wanted to see a statement that looked like line noise, I'd use Perl or APL.

Second, related to the above, C++ is extremely powerful and large, and it takes a long time to master. I don't believe that languages should take a long time to master.

Which brings me to Ruby. Ruby is not quite as bad as C++ in the long time to master, though it's still a far cry from Python. And Ruby is almost what I need (I think it has some of the best syntax of any language ever), but it's still not quite there.

  • Ruby is slow. I can't really compile it, and it has so many language features that really bog it down, not all of which I feel are necessary (see next bullet).
  • Ruby is weird. Most people who have been exposed to OOP know what a class variable and an instance variable is, but what the hell is a class instance variable? Reading through The Ruby Programming Language (which is a great book, btw), Ruby has a lot of really complicated things going on when doing variable and method name resolution involved with the myriad of different ways things are inherited. It bothers me a lot.
  • Ruby makes it worse by attempting to explicitly declare the scope of a variable with var, $var, @var, @@var. Often, this just makes things worse.


So, Ruby isn't quite there.

Python? Well, it's much faster (an order of magnitude or thereabouts). It doesn't have a lot of weird inheritance issues (only some).

My biggest problem is the direction that Python is taking with Python 3.0. Changing map() and friends to return iterators? Making print a function? Further confusing me regard to text vs. unicode vs. data vs. bytes vs. strings vs. regular expressions? And so forth.

And even syntactically there are problems: Why are there colons at the end of def/if/for/... statements? And is it really necessary to have syntactically important whitespace? I don't mind it, but a lot of people hate this with a passion.

So, what I need is a language that can be compiled (and run at great speeds), with Ruby-like syntax, and a Python-like typing and object model.

That would be nice, thank you.




And yes, if I get some time, I might consider forking a project related to one of the above to get what I want.

Jan24

Project Euler

programming fun | comments

One of my new favorite hobbies / time sinks is Project Euler. Simply stated: a few hundred programming problems that are somewhat math-related. They are sort of like brain teasers for programming nerds.

One of my favorite features is that most of the problems have many ways to approach it. Usually there is a terrible way, such as brute force, and then there is some elegant way. Or, maybe, the elegant way is just clever uses of data structures.

For example, here is problem 45:

Triangle, pentagonal, and hexagonal numbers are generated by the following formulae:

Triangle: T(n)=n(n+1)/2    1, 3, 6, 10, 15, ...

Pentagonal: P(n)=n(3n−1)/2    1, 5, 12, 22, 35, ...

Hexagonal: H(n)=n(2n−1)    1, 6, 15, 28, 45, ...

It can be verified that T(285) = P(165) = H(143) = 40755.

Find the next triangle number that is also pentagonal and hexagonal.


When I first wrote this up in Python, I did it in a naive manner: simply constructing giant lists of each of the numbers and checking intersections.

Let's put it this way. The next intersection does not happen for a long, long time. Doing it this way takes about 6.5 minutes to run on my machine. You can just feel the quadratic nature of the running time.

There is actually a slightly more clever way to do it in linear time, which lowers this to about 0.1 seconds to run. See if you can find it.

Dec31

Fail Mail

rails fail plugin | comments

I do some freelance web development, and one of the things I've found most annoying is when a a 500 error occurs on my Rails sites, I hear about I from the customer. What's even worse is that I often have no idea what the erorr that occurred is, and the customer may not know exactly how the application failed. Pouring through the server logs can be a bit tedious, especially if there has been a lot of traffic.

Enter Fail Mail, a.k.a., the Exception Notification plugin for Rails. Assuming you have ActionMailer installed and configured, there are about three steps needed to ensure that you automatically receive an email message every time your application crashes. (It doesn't notify you if it was a 404, which is probably for the best.)

Looks like I need to go reinvent the wheel for to make it work on this blog, now …

Dec27

The Curious Case of Benjamin Button

typesetting literature | comments

I intend to go see The Curious Case of Benjamin Button this weekend. While quickly looking it up on the Internet, I realized that the original short story was published in 1921, and would therefore have fallen out of copyright.

So, I decided to read it. I found it on Project Gutenberg, but the copy is (of course) in plain, vanilla ASCII, and has several typos.

I decided then to quickly typeset it so that I would feel more better when reading it.

Feel free to download it here.

I typeset it with Adobe InDesign in Adobe Garamond Pro 12pt. It's about 11 pages and change long, double-column format.

I fixed some of the Gutenberg-isms (like using "_whatever_" to indicate italics), and corrected what I could as far as typos. There's one case where the word "array" is used, and I can't find an original source to tell me if that is a mis-OCR for "army" (which would make more sense today, but array could be correct, if a bit arcane).

I liked the short story, but I feel that the movie will give a sense of time passing that the short story rather lacks.

Dec25

Bingo! Distance between two points on Earth

programming distance ruby | comments

As a follow-up to my previous post, I have found two ways of finding the distance between two cities in the US. One is slow and more correct, one is less slow and less correct.

First, the slow one. You can take the third formula from the Wikipedia entry for finding the great-circle distance. It translates pretty naturally into any language, but involves many trigonometric functions, squares, square roots, etc. It's quite ugly an inelegant in code, and quite slow.

In Ruby, it looks something like this (the lat and rlat accessors return the latitude in degrees and radians, respectively, and similarly for longitude with lon and rlon).

  def distance_miles_slow(other)
    3956.54938 * Math.atan2(Math.sqrt((Math.cos(other.rlat) *
        Math.sin(other.rlon - rlon)) ** 2 +
      (Math.cos(rlat) * Math.sin(other.rlat) - Math.sin(rlat) *
        Math.cos(other.rlat) * Math.cos(other.rlon - rlon)) ** 2),
      (Math.sin(rlat) * Math.sin(other.rlat) + Math.cos(rlat) *
        Math.cos(other.rlat) * Math.cos(other.rlon - rlon)))
  end


Ugh.

It turns out that my naïve method of simply using the Pythagorean theorem after finding the "linear" distance in latitude and longitude (from here) is actually pretty good. Not as accurate, as the rougher figures seem to be off by about 2% at moderate distances (less than 1,000 miles), which is good enough for me at the moment.

Here's what the Ruby for that look like:

  def distance_miles_fast(other)
    dlat = lat - other.lat
    drlon = rlon - other.rlon
    Math.sqrt((68.9100652 * dlat)**2 +
      (drlon * 3956.54938 * Math.cos(rlat))**2)
  end


A little bit better, and at least a bit faster.

Dec25

US ZIP Code Data

location ruby | comments

Recently, I was thinking to myself, "Where do all of these wonderful sites on the web get information to map cities, towns, and US ZIP codes to actual locations?" I then though, "How much do they pay?" In reality, I don't know the answer, though there seem to be many companies you can pay for the information.

The best data I could find easily was from the US Census, though it is in a terse, fixed-column format. Clearly then, the next step is to read it into a database.

So, naturally, I created a small Ruby script to do just that. If you are interested in such things, you can view it over at github.

The next question I am curious about is calculating the distance between two points on the Earth's surface given their latitude and longitude pairs. This isn't trivial, but it shouldn't be too hard to get a rough estimate.