<?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"
	>

<channel>
	<title>robert zubek / blog</title>
	<atom:link href="http://robert.zubek.net/blog/feed/" rel="self" type="application/rss+xml" />
	<link>http://robert.zubek.net/blog</link>
	<description></description>
	<pubDate>Sat, 28 Jun 2008 02:34:57 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.5.1</generator>
	<language>en</language>
			<item>
		<title>Commentary on &#8220;A History of Erlang&#8221;</title>
		<link>http://robert.zubek.net/blog/2008/06/21/commentary-on-a-history-of-erlang/</link>
		<comments>http://robert.zubek.net/blog/2008/06/21/commentary-on-a-history-of-erlang/#comments</comments>
		<pubDate>Sat, 21 Jun 2008 21:10:40 +0000</pubDate>
		<dc:creator>robert</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<category><![CDATA[erlang]]></category>

		<category><![CDATA[programming]]></category>

		<guid isPermaLink="false">http://robert.zubek.net/blog/?p=26</guid>
		<description><![CDATA[Some time ago I found a fascinating paper on the history of Erlang, written by one of its main creators:
http://www.cs.chalmers.se/Cs/Grundutb/Kurser/ppxt/HT2007/general/languages/armstrong-erlang_history.pdf
It showed up on programming.reddit for about a day, and then disappeared right back into the deep waters - which is too bad, because the paper is illuminating.
Erlang is a language with strong built-in support for [...]]]></description>
			<content:encoded><![CDATA[<p>Some time ago I found a fascinating paper on the history of Erlang, written by one of its main creators:</p>
<p><a href="http://www.cs.chalmers.se/Cs/Grundutb/Kurser/ppxt/HT2007/general/languages/armstrong-erlang_history.pdf">http://www.cs.chalmers.se/Cs/Grundutb/Kurser/ppxt/HT2007/general/languages/armstrong-erlang_history.pdf</a></p>
<p>It showed up on <em>programming.reddit </em>for about a day, and then disappeared right back into the deep waters - which is too bad, because the paper is illuminating.</p>
<p>Erlang is a language with strong built-in support for <a href="http://www.lindaspaces.com/book/chap2.htm#a222">concurrency based on message passing</a>. As the author describes it:</p>
<blockquote><p>Erlang was designed for writing concurrent programs that &#8220;run forever.&#8221; Erlang uses concurrent processes to structure the program. These processes have no shared memory and communicate by asynchronous message passing. Erlang processes are lightweight and belong to the language, not the operating system.</p></blockquote>
<p>Reading the paper, I had several delightful &#8220;aha!&#8221; moments, where the details of Erlang design just clicked together. And I don&#8217;t mean the mundane issues, like single-assignment variables (hello, Prolog!), or interactively modifiable code (hello, Lisp!). Rather, the history put some architectural bits in perspective.</p>
<p>At a certain level, I always knew that Erlang was designed for programming telephone exchanges. But this paper really conveys the feel for what that means: huge collections of loosely coupled finite-state machines, all running in parallel, doing little bits of protocol validation here and there, but mostly staying dormant.</p>
<blockquote><p>Typically, the software for call control is modeled using finite state machines that undergo state transitions in response to protocol messages. From the software point of view, the system behaves as a very large collection of parallel processes. At any point in time, most of the processes are waiting for an event caused by the reception of a message or the triggering of a timer. When an event occurs, the process does a small amount of computation, changes state, possibly sends messages to other processes and then waits for the next event. The amount of computation involved is very small.</p></blockquote>
<p>Which definitely explains a number of their implementation choices which, without this motivation, look highly unusual. Let me highlight just three of them.</p>
<p><span id="more-26"></span></p>
<p> </p>
<p>First, there&#8217;s strong emphasis on easily tracking and manipulating the flow of control, rather than data. Everything is based on message passing. This makes Erlang perfect for control problems: monitoring the state of the world, dispatching events, tracking protocols. It also makes perfect sense, given the pragmatic need to support massive collections of concurrent FSMs.</p>
<p>But here&#8217;s the flip side: there is little support for processing large, shared data structures - unless they come from an external source, such as a database, which takes responsibility for locking and synchronization.</p>
<p>So when it comes to problems that really want to be done in a straightforward shared-data style (<a href="http://en.wikipedia.org/wiki/SIMD">SIMD</a> scientific computing comes to mind), that&#8217;s not as easy anymore - even if the problem itself is inherently parallelizable. One gets a sense that Erlang&#8217;s concurrency primitives are general-purpose, but they&#8217;re definitely oriented towards very specific applications of concurrency.</p>
<p> </p>
<p>Second, I enjoyed the explanation of one particular detail of Erlang: in the message-passing implementation, individual processes don&#8217;t handle messages immediately. Instead, there&#8217;s a concept of &#8220;mailboxes&#8221; where messages get queued up, and handled only when the process is in the right state, and has all the messages it needs.</p>
<p>This seems unusual, especially coming from the traditional programming background, where messages are merely a vehicle for doing RPC. But that&#8217;s not how this language uses them.</p>
<p>Erlang&#8217;s finite state machines implement and validate message-oriented protocols. Immediate message handling would make this much more complicated:</p>
<blockquote><p>[W]e observed that handling out-of-order messages in a finite state machine led to an explosion in the state space of the finite state machine. This happens more often than you think, in particular when processing remote procedure calls. Most of the telecommunications programs we write deal with message-oriented protocols.</p></blockquote>
<p>This is a pretty nice idea. In my own previous <a href="http://robert.zubek.net/publications/hierarchical-parallel-markov.pdf">attempts</a> at building large collections of parallel FSMs, I encountered similar problems, caused by immediate input processing. I ended up having to split a single &#8220;conceptual FSM&#8221; into multiple concrete FSMs, and broadcast all inputs to all of them, just to be able to deal with out-of-order messages. But then coordinating them all turned into a major problem. In retrospect, a message-based approach with mailboxes and pattern matching over those mailboxes would have been a clear win.</p>
<p> </p>
<p>Third, Erlang is somewhat infamous for its distributed error handling mechanism. Most languages are very specific about how errors propagage - Java and C# errors, for example, always travel up the call stack; one could imagine a similar scheme for a message-passing system.</p>
<p>Erlang, however, completely washes its hands of this responsibility. Failed processes post an error, but it&#8217;s up to the programmer to make sure some other process somewhere else is looking for those errors.</p>
<p>I was always curious what prompted such an unusual mechanism, and the paper illuminates that:</p>
<blockquote><p>The idea of links and of the mechanism by which all processes in a set die was due to Mike Williams. Mike&#8217;s idea was inspired by the design of the release mechanism used in old analogue telephones and exchanges. In the analogue telephones and in the early electromechanical exchanges, three wires called A, B and C were connected to the phones. The C wire went back to the exchange and through all the electromechanical relays involved in setting up a call. If anything went wrong, or if either partner terminated the call, then the C wire was grounded. Grounding the C wire caused a knock-on effect in the exchange that freed all resources connected to the C line.</p></blockquote>
<p>This is very powerful. You want errors to propagate via some unexpected route, to a master controller that shuts down all sorts of things, and then maybe attempts recovery? You can set that up fairly easily.</p>
<p>At the same time, it feels dangerous, since one is no longer relying on the runtime for error handling. Now it&#8217;s up to the programmers to design an error propagation network. But relying on convention doesn&#8217;t scale very well; and besides, isn&#8217;t this why we have compilers, to do the boring enforcement work for us? <img src='http://robert.zubek.net/blog/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
<p>All in all, a fascinating read!</p>
<p> </p>
]]></content:encoded>
			<wfw:commentRss>http://robert.zubek.net/blog/2008/06/21/commentary-on-a-history-of-erlang/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Clojure Web Server (in less than 100 lines)</title>
		<link>http://robert.zubek.net/blog/2008/04/26/clojure-web-server/</link>
		<comments>http://robert.zubek.net/blog/2008/04/26/clojure-web-server/#comments</comments>
		<pubDate>Sun, 27 Apr 2008 07:06:09 +0000</pubDate>
		<dc:creator>robert</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<category><![CDATA[clojure]]></category>

		<category><![CDATA[lisp]]></category>

		<category><![CDATA[programming]]></category>

		<category><![CDATA[web]]></category>

		<guid isPermaLink="false">http://robert.zubek.net/blog/?p=23</guid>
		<description><![CDATA[(Edit: Welcome reddit readers. The following is a howto on setting up an embedded Jetty server in Clojure, and writing a minimal servlet that serves up dynamic content.)
Last week I discovered a very nice language named Clojure. It&#8217;s based on Lisp, but hosted on the Java platform, and running inside the JVM. It has some [...]]]></description>
			<content:encoded><![CDATA[<p>(Edit: Welcome reddit readers. The following is a howto on setting up an embedded Jetty server in Clojure, and writing a minimal servlet that serves up dynamic content.)</p>
<p>Last week I discovered a very nice language named <a title="Clojure" href="http://clojure.sourceforge.net/" target="_blank">Clojure</a>. It&#8217;s based on Lisp, but hosted on the Java platform, and running inside the JVM. It has some lovely features, like native support for Java collections, and a clean API that ditches a lot of the old ANSI CL library cruft.</p>
<p>But the best part for me was language interop: Clojure can effortlessly load and use any external Java library. And there is a ton of good ones out there, including a rich set that comes with the Java platform. (This may seem like a minor point, but most free Lisps lack good libraries; only the non-free Allegro CL has really extensive, cross-platform ones.)</p>
<p>So I decided to take the language for a spin, especially exercising the language interop bit. For example, by building a little web server to serve up some dynamic content. You know, web development 101 kind of stuff. This turns out to be really easy to do. <img src='http://robert.zubek.net/blog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>Since Clojure is backed by Java, we can use an existing server library to do the heavy lifting for us. Several high-quality ones are available - I went with Jetty, a servlet-based engine that&#8217;s really easy to embed in custom applications. Using a servlet container will take care of all the mundane bookkeeping for us.</p>
<p>In the following posting, I&#8217;m reproducing the steps required to get a Clojure-based web server up and running, in much less than 100 lines of code. Hope you find it interesting!</p>
<p><span id="more-23"></span></p>
<p>I&#8217;ll split this into three parts:</p>
<ol>
<li>Getting Jetty and setting up your Clojure environment</li>
<li>Creating a server that only serves up static pages</li>
<li>Modifying the server to provide dynamic content</li>
</ol>
<p>Let&#8217;s go through them.</p>
<p> </p>
<h4>1. Getting Jetty and environment setup.</h4>
<p>(The following requires Clojure release 2008/03/29 or later.)</p>
<p>Jetty is a huge library, but we&#8217;ll only need three jars from it. So let&#8217;s do the following:</p>
<ul>
<li>Download <a title="Downloading Jetty" href="http://docs.codehaus.org/display/JETTY/Downloading+Jetty" target="_blank">Jetty 6.1.9</a>, or whichever is latest, from the official site</li>
<li>Get the following jar files from the archive, and place them in some preferred directory (I put them under clojure/lib):
<ul>
<li>jetty-6.1.9.jar</li>
<li>jetty-util-6.1.9.jar</li>
<li>servlet-api-2.5-6.1.9.jar</li>
</ul>
</li>
<li>In whatever script / batch file / elisp command you use to start up your Clojure instance, modify your classpath to include them. In my case, I end up with the enormous:</li>
</ul>
<blockquote>
<li>&#8220;java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=4096 -cp c:/users/rob/documents/clojure/clojure.jar;c:/users/rob/documents/clojure/lib/<strong>jetty-6.1.9.jar</strong>;c:/users/rob/documents/clojure/lib/<strong>jetty-util-6.1.9.jar</strong>;c:/users/rob/documents/clojure/lib/<strong>servlet-api-2.5-6.1.9.jar</strong> clojure.lang.Repl c:/users/rob/documents/clojure/src/boot.clj&#8221;</li>
</blockquote>
<p>To verify, start up your Clojure instance, and try creating a new Jetty Server instance - it should look something like the following:</p>
<pre style="padding-left: 30px;">Clojure
user=&gt; (import '(org.mortbay.jetty Server))
nil
user=&gt; (new Server)
Server@1f0f8ff
user=&gt;</pre>
<p>All right, now we&#8217;re ready to have some fun.</p>
<p> </p>
<h4>2. Writing a server for static pages</h4>
<p>Jetty comes with a default servlet for static html pages. We just need to configure it.</p>
<p>We could do this from an xml file, as recommended for Jetty deployment in production environments. But that&#8217;s more work than is needed for this experiment.  :)  Instead, we&#8217;ll configure a small and minimal server instance from Clojure code itself.</p>
<p>First, a big bunch of imports:</p>
<pre style="padding-left: 30px;">(import
 '(java.io File)
 '(javax.servlet.http HttpServlet HttpServletRequest HttpServletResponse)
 '(org.mortbay.jetty Server Handler Connector NCSARequestLog)
 '(org.mortbay.jetty.handler HandlerCollection ContextHandlerCollection RequestLogHandler)
 '(org.mortbay.jetty.nio BlockingChannelConnector)
 '(org.mortbay.jetty.servlet Context DefaultServlet ServletHolder SessionHandler))</pre>
<p>Now let&#8217;s add a few constants that we&#8217;ll need later on (customize as needed, of course):</p>
<pre style="padding-left: 30px;">;; server port
(def *port* 8080)</pre>
<pre style="padding-left: 30px;">;; static pages will be served from this directory
(def *wwwdir* "C:/Users/Rob/Documents/Projects/Clojure Http Server/pages")</pre>
<pre style="padding-left: 30px;">;; log files go here
(def *logdir* "C:/Users/Rob/Documents/Projects/Clojure Http Server/log")</pre>
<pre style="padding-left: 30px;">;; specifies naming pattern for log files
(def *logfiles* "log.yyyy_mm_dd.txt")</pre>
<p>The Jetty server itself contains some connectors, which read data from sockets and feed it to a bunch handlers. Let&#8217;s make one.</p>
<pre style="padding-left: 30px;">;; create connector on the specified port
(defn make-connectors [port]
  (let [conn (new BlockingChannelConnector)]
    (. conn (setPort port))
    (into-array [conn])))</pre>
<p>One of the built-in handlers is a logger, which just writes out an access log file. Another built-in one is a context handler, which contains a set of servlets. It preprocesses any received data, and then hands it over to one of its servlets, based on the path being requested in the URL.</p>
<p>Here&#8217;s how I configure both of them:</p>
<pre style="padding-left: 30px;">;; configures a default servlet to serve static pages from the "/" directory
(defn configure-context-handlers [contexts]
  (let [context (new Context contexts "/" (. Context NO_SESSIONS))]
    (. context (setWelcomeFiles (into-array ["index.html"])))
    (. context (setResourceBase (. (new File *wwwdir*) (getPath))))
    (. context (setSessionHandler (new SessionHandler)))
    (. context (addServlet (new ServletHolder (new DefaultServlet)) &#8220;/*&#8221;))
    context))</pre>
<pre style="padding-left: 30px;">;; set up handlers: the context ones, and a logger to track all http requests
(defn make-handlers [contexts]
  (let [handlers (new HandlerCollection)
        logger (new RequestLogHandler)
        logfile (new File *logdir* *logfiles*)]</pre>
<pre style="padding-left: 30px;">    (. logger (setRequestLog (new NCSARequestLog (. logfile (getPath)))))
    (. handlers (addHandler logger))
    (. handlers (addHandler contexts))
    handlers))</pre>
<p>All that&#8217;s left is standing up the actual server. That&#8217;s frighteningly easy:</p>
<pre style="padding-left: 30px;">;; make an empty collection of context handlers - we'll configure it later
(defn make-contexts []
  (new ContextHandlerCollection))

;; make an instance of the http server
(defn make-server
  ([] (make-server *port*))
  ([port]
     (let [contexts (make-contexts)
           connectors (make-connectors port)
           handlers (make-handlers contexts)
           server (new Server)]
       (. server (setConnectors connectors))
       (. server (setHandler handlers))
       (configure-context-handlers contexts)
       server)))</pre>
<p>And that&#8217;s it! Go ahead, try it!</p>
<p>First, place an &#8220;index.html&#8221; page in the directory you specified under <strong>*wwwdir*.</strong> Then run the server:</p>
<pre style="PADDING-LEFT: 30px">user=&gt; (def server (make-server))
2008-04-26 23:32:02.752::INFO:  Logging to STDERR via org.mortbay.log.StdErrLog
#&lt;Var: user/server&gt;
user=&gt; (. server (start))
2008-04-26 23:32:10.863::INFO:  jetty-6.1.9
2008-04-26 23:32:10.898::INFO:  Opened C:\Users\Rob\Documents\Projects\Clojure Http Server\log\log.2008_04_27.txt
2008-04-26 23:32:10.962::INFO:  Started BlockingChannelConnector@0.0.0.0:8080
nil</pre>
<p>&#8230; and then navigate over to the right page:</p>
<p><a href="http://robert.zubek.net/blog/wp-content/uploads/2008/04/clojure-server-1.jpg"><img class="aligncenter size-full wp-image-24" title="clojure-server-1" src="http://robert.zubek.net/blog/wp-content/uploads/2008/04/clojure-server-1.jpg" alt="" /></a></p>
<p>To stop the server, just call the method with the same name:</p>
<pre style="padding-left: 30px;">user=&gt; (. server (stop))
nil
 </pre>
<h4>3. Servlet that produces dynamic content</h4>
<p>The next step is writing our own servlet, which will serve arbitrary, dynamic content. The Java Servlet API is quite powerful, and makes it easy to build all sorts of wacky web wonders.</p>
<p>Let&#8217;s start by writing our own request processing function: it will take an HTTP request and response objects, and just write out current time as the response:</p>
<pre style="PADDING-LEFT: 30px">(defn process [#^HttpServletRequest req #^HttpServletResponse resp]
  (let [out (. resp (getOutputStream))]
    (. out (println (str &#8220;Test Successful at &#8221; (new java.util.Date)))))) </pre>
<p>(The type hints aren&#8217;t strictly necessary, but they&#8217;re there for my sanity, to remind me what Java classes are hiding behind the various variables.)</p>
<p>Also, make a new servlet that will use this function to process each request:</p>
<pre style="padding-left: 30px;">;; implementation of an HttpServlet, overriding just one function:
;;   protected void doGet(HttpServletRequest req, HttpServletResponse resp)
(defn make-test-servlet []
     (proxy [HttpServlet] []
       (doGet [#^HttpServletRequest req #^HttpServletResponse resp]
          (process req resp))))</pre>
<p>Finally, hook it up to the context handler, so that it will serve all requests for the &#8220;/test&#8221; URL. We just modify the function mentioned above, by adding a single line highlighted below:</p>
<pre style="padding-left: 30px;">(defn configure-context-handlers [contexts]
  (let [context (new Context contexts "/" (. Context NO_SESSIONS))]
    (. context (setWelcomeFiles (into-array ["index.html"])))
    (. context (setResourceBase (. (new File *wwwdir*) (getPath))))
    (. context (setSessionHandler (new SessionHandler)))
    (. context (addServlet (new ServletHolder (new DefaultServlet)) &#8220;/*&#8221;))
    <span style="text-decoration: underline;"><strong>(. context (addServlet (new ServletHolder (make-test-servlet)) &#8220;/test&#8221;))</strong></span>
    context))</pre>
<p>That&#8217;s that! Now run the server again, and check out the results:</p>
<p><a href="http://robert.zubek.net/blog/wp-content/uploads/2008/04/clojure-server-2.jpg"><img class="aligncenter size-full wp-image-25" title="clojure-server-2" src="http://robert.zubek.net/blog/wp-content/uploads/2008/04/clojure-server-2.jpg" alt="" /></a></p>
<p> </p>
<h4>Final Words</h4>
<p>This is clearly just an experiment, and not a production-ready system. But using Jetty as the server engine makes the system easy to build and very robust - it&#8217;s easy to imagine how one could go about building an actual, fully-featured web service around this technology.</p>
<p>Hope this was amusing!</p>
<p> </p>
]]></content:encoded>
			<wfw:commentRss>http://robert.zubek.net/blog/2008/04/26/clojure-web-server/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Techniques: dealing with blocking operations</title>
		<link>http://robert.zubek.net/blog/2008/04/20/dealing-with-blocking-operations/</link>
		<comments>http://robert.zubek.net/blog/2008/04/20/dealing-with-blocking-operations/#comments</comments>
		<pubDate>Mon, 21 Apr 2008 05:17:43 +0000</pubDate>
		<dc:creator>robert</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<category><![CDATA[programming]]></category>

		<category><![CDATA[techniques]]></category>

		<guid isPermaLink="false">http://robert.zubek.net/blog/?p=22</guid>
		<description><![CDATA[Game logic often gets split into &#8220;time-sensitive&#8221; and &#8220;blocking&#8221; parts. When dealing with I/O-bound components, we often want to let them run separately from the main game loop. This way they won&#8217;t block the whole game as they&#8217;re waiting for their data.
The typical example is reading from disk, or accessing a database. Disk operations are orders [...]]]></description>
			<content:encoded><![CDATA[<p>Game logic often gets split into &#8220;time-sensitive&#8221; and &#8220;blocking&#8221; parts. When dealing with I/O-bound components, we often want to let them run separately from the main game loop. This way they won&#8217;t block the whole game as they&#8217;re waiting for their data.</p>
<p>The typical example is reading from disk, or accessing a database. Disk operations are orders of magnitude slower than operations on memory. If the data we need is already loaded and in memory, that’s great, we can use it immediately - but if it isn’t, we’ll have to load it first. And we wouldn&#8217;t want to put the whole game on hold, while we wait for the drive to seek to the appropriate sector on the disk.</p>
<p>The standard solution is to split the work into multiple threads: the fast main thread, which handles time-sensitive processing, and a worker thread (or a pool of threads), which will handle anything that could potentially block. But how to implement this?</p>
<p><span id="more-22"></span></p>
<p>In an ideal world, we&#8217;d just hand over the current continuation to a new lightweight process, operating on immutable data, and that would take care of everything. But ours is not an ideal world; we have to deal with messy details. <img src='http://robert.zubek.net/blog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>In practice, there are significant differences of opinion on how to go about farming out slow tasks to asynchronous workers. This will depend greatly on what the language allows, and what kind of bookkeeping we&#8217;re willing to live with.</p>
<h4>Pool of work unit processors</h4>
<p>Some higher-level languages have very nice abstractions for dealing with units of work. At the heart of this abstraction is a dispatcher: we give it a work unit, and it will take care of executing it independently of the main thread. Dispatchers typically manage their own pool of worker threads - each incoming work unit gets assigned to the next available thread, or queued up for future execution if all workers are busy.</p>
<p>An example of a dispatcher in Java is the Executor interface. One might say something like:</p>
<pre>  final ProxyObject proxy = ProxyFactory.get(creatureType);
  executor.execute(new Runnable() {
    // this might take a while:
    ObjectData data = db.load(proxy.id);
    proxy.update(data);
  }</pre>
<p>The code snippet creates a new anonymous Runnable object with a specific payload (loading data from the database, updating the proxy object with the results). The call to execute() returns immediately - and the work unit will be executed sometime in the future on a separate thread. (Anonymous class instances are a particular feature of Java, of course, but it’s possible to achieve the same effect by passing in callback functions or their equivalents.)</p>
<p>This level of abstraction is very convenient. Having customized work units whenever we need them lets us keep all game logic in the same place: the work unit code is written in the same place as the rest of game logic, even though it will be executed on a different thread, at some other point in time. This makes the code easy to follow.</p>
<p>It’s also easy to chain work units together. For example, suppose we have a multi-step process: read from the database, do some processing on the results, then use that to read something else from the database. It’s very clear how to write a work unit that does the processing, and in turn creates another work unit.</p>
<p>But that&#8217;s also a problem: any shared data will now require explicit synchronization, since it will be accessed from other threads at unpredictable points in the future. In the example above, the proxy object will have to be made safe for updates from multiple threads. Ad hoc work units mean we have to pay the cost of making shared data thread-safe.</p>
<h4>Standalone processor</h4>
<p>Sometimes sharing work units across threads is unavailable (eg. because of platform limitations) or undesirable. We can then use a more traditional solution: a dedicated processor that runs on a separate thread, which accepts work requests, and takes care of everything.</p>
<p>For example, a game thread might ask a resource loader for some resource by its id, and get a proxy back:</p>
<pre>  ProxyObject proxy = app.resourceLoader.startLoading(creatureType);</pre>
<p>…and that’s it. An empty proxy object is now ready to use, albeit lacking any useful data. At some point in the future, the processor will do all the blocking operations, and process their results: read from the database, lock the proxy, and clobber its contents with updated data. But this will be invisible to the caller. (Async proxies are another interesting abstraction, one deserving its own topic.)</p>
<p>Performance and access control are the main benefits of this approach. If the processor runs only its own processing code, instead of arbitrary work units, it’s much easier to inspect what it does and tune its performance. As for access control, the loader does not exist in the same “code space” as the game logic, and has no access to local game data. We need to explicitly give it access to anything that it might need. This way it’s easier to keep track of which threads are accessing what kind of data, and see where we need to provide locks or other synchronization mechanisms.</p>
<p>But its generality is also a problem. Ad-hoc work units are very easy to customize - we can do custom processing depending on the surrounding game logic (eg. a work unit for an agent might do one thing, a work unit for a scenery object might do something else, and so on). A generic processor, however, will not have this kind of game-specific logic.</p>
<p>Since it will be used by all parts of the game (and possibly more than one game), developers will be tempted to treat it as a library, and keep it general, abstract, and reusable. While that’s an excellent approach for a library, it limits what it can do for a specific game.</p>
<p>If customization is desirable, it will be up to the game system to find a way to manipulate the results of asynchronous processing. As a result, client objects (eg. proxies) may end up inheriting a lot of responsibility for post-processing the results.</p>
<h4>Separation</h4>
<p>These two cases illustrate two popular approaches to dealing with slow, I/O-bound operations:</p>
<ol>
<li>Having a separate pool of worker threads and ad hoc asynchronous work units</li>
<li>Having a dedicated subsystem that is completely responsible for a limited set of desirable asynchronous operations</li>
</ol>
<p>Dedicated system approach is more popular with performance-minded applications, such as console games, because the system’s performance can be more precisely managed. Tight control also allows for certain optimizations, such as pre-fetching data before it’s actually needed.</p>
<p>Ad hoc execution of asynchronous work units affords greater flexibility. They are easier to customize than a generic library, and there are no concerns about how exactly to split the work between the library and game code. At the same time, the solution requires a sacrifice of control. It’s more common in applications where it’s acceptable to trade some performance in exchange for readability and customizability.</p>
<p> </p>
]]></content:encoded>
			<wfw:commentRss>http://robert.zubek.net/blog/2008/04/20/dealing-with-blocking-operations/feed/</wfw:commentRss>
		</item>
		<item>
		<title>SBCL + Emacs + Windows Vista</title>
		<link>http://robert.zubek.net/blog/2008/04/09/sbcl-emacs-windows-vista/</link>
		<comments>http://robert.zubek.net/blog/2008/04/09/sbcl-emacs-windows-vista/#comments</comments>
		<pubDate>Thu, 10 Apr 2008 04:49:05 +0000</pubDate>
		<dc:creator>robert</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<category><![CDATA[emacs]]></category>

		<category><![CDATA[lisp]]></category>

		<category><![CDATA[vista]]></category>

		<category><![CDATA[windows]]></category>

		<guid isPermaLink="false">http://robert.zubek.net/blog/?p=21</guid>
		<description><![CDATA[Here are my steps for setting up SBCL (Steel Bank Common Lisp) and Slime (Emacs Lisp mode) to work under Windows Vista.
It&#8217;s mostly straightforward, except for dealing with spaces in path names. It turns out that slime.el uses the (split-string) function to pull apart the Lisp command line, which won&#8217;t work with the default &#8220;c:\program files\&#8230;&#8221; [...]]]></description>
			<content:encoded><![CDATA[<p>Here are my steps for setting up SBCL (Steel Bank Common Lisp) and Slime (Emacs Lisp mode) to work under Windows Vista.</p>
<p>It&#8217;s mostly straightforward, except for dealing with spaces in path names. It turns out that <em>slime.el</em> uses the (split-string) function to pull apart the Lisp command line, which won&#8217;t work with the default &#8220;c:\program files\&#8230;&#8221; location. Here&#8217;s how to fix that, using symbolic links (yes, Windows supports them too!).</p>
<p>The following was tested on a Vista box, SBCL 1.0.13, and Slime 2.0 on Emacs 22.2.1.</p>
<p>Installing SBCL:</p>
<ol>
<li>Download and install SBCL. By default, it will install to <em>c:\Program Files\Steel Bank Common Lisp\1.0.13</em></li>
<li>Run <em>cmd.exe as Administrator</em> (required for symbolic links)</li>
<li>Set up a link from SBCL install directory, to some location without spaces. Note that the syntax is an inverse of the Unix &#8216;ln&#8217; command.<br />
<blockquote><p>C:\Users\Rob\Documents&gt;ver<br />
Microsoft Windows [Version 6.0.6000]</p>
<p>C:\Users\Rob\Documents&gt;mklink /d SBCL &#8220;c:\Program Files\Steel Bank Common Lisp\1.0.13&#8243;<br />
symbolic link created for SBCL &lt;&lt;===&gt;&gt; c:\Program Files\Steel Bank Common Lisp\1.0.13</p>
<p>C:\Users\Rob\Documents&gt;dir sbcl*<br />
&#8230;<br />
04/09/2008 09:07 PM &lt;SYMLINKD&gt; SBCL [c:\Program Files\Steel Bank Common Lisp\1.0.13]</p></blockquote>
</li>
</ol>
<p>Installing Emacs and Slime:</p>
<ol>
<li>Download Emacs, unzip it to any location (eg. <em>c:\Program Files\emacs</em>). If desired, run the <em>emacs\bin\addpm.exe</em> program to add a link to the start menu.</li>
<li>Download Slime, and unzip it under your <em>emacs/site-lisp</em> directory.</li>
<li>Add the following to your <em>~/.emacs</em> file:<br />
<blockquote>
<div>(setq inferior-lisp-program &#8220;c:/users/rob/documents/sbcl/sbcl.exe &#8211;core c:/users/rob/documents/sbcl/sbcl.core&#8221;)<br />
(require &#8217;slime)<br />
(slime-setup) </div>
</blockquote>
</li>
<li>Restart Emacs, and then run <em>M-x run-lisp</em> to test whether Lisp starts up.</li>
<li>Start slime with<em> M-x slime</em>. Happy hacking!</li>
</ol>
<p> </p>
]]></content:encoded>
			<wfw:commentRss>http://robert.zubek.net/blog/2008/04/09/sbcl-emacs-windows-vista/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Hey, Young Whirled!</title>
		<link>http://robert.zubek.net/blog/2008/03/25/hey-young-whirled/</link>
		<comments>http://robert.zubek.net/blog/2008/03/25/hey-young-whirled/#comments</comments>
		<pubDate>Tue, 25 Mar 2008 20:22:23 +0000</pubDate>
		<dc:creator>robert</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<category><![CDATA[flash]]></category>

		<category><![CDATA[three rings]]></category>

		<category><![CDATA[whirled]]></category>

		<guid isPermaLink="false">http://robert.zubek.net/blog/2008/03/25/hey-young-whirled/</guid>
		<description><![CDATA[Here&#8217;s what I&#8217;ve been working on all this time: Whirled is now in public beta!
Below is a live view into my room - stop by and say hello!  

]]></description>
			<content:encoded><![CDATA[<p>Here&#8217;s what I&#8217;ve been working on all this time: <a href="http://www.whirled.com">Whirled</a> is now in public beta!</p>
<p>Below is a live view into my room - stop by and say hello! <img src='http://robert.zubek.net/blog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p><object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="100%" height="550" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="wmode" value="opaque" /><param name="FlashVars" value="sceneId=12" /><param name="src" value="http://www.whirled.com/clients/world-client.swf" /><embed type="application/x-shockwave-flash" width="100%" height="550" src="http://www.whirled.com/clients/world-client.swf" flashvars="sceneId=12" wmode="opaque"></embed></object></p>
]]></content:encoded>
			<wfw:commentRss>http://robert.zubek.net/blog/2008/03/25/hey-young-whirled/feed/</wfw:commentRss>
		</item>
		<item>
		<title>GDC: Funding models for casual and web-based games</title>
		<link>http://robert.zubek.net/blog/2008/03/02/gdc-funding-models-for-casual-and-web-based-games/</link>
		<comments>http://robert.zubek.net/blog/2008/03/02/gdc-funding-models-for-casual-and-web-based-games/#comments</comments>
		<pubDate>Mon, 03 Mar 2008 07:33:36 +0000</pubDate>
		<dc:creator>robert</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<category><![CDATA[games]]></category>

		<category><![CDATA[gdc]]></category>

		<guid isPermaLink="false">http://robert.zubek.net/blog/2008/03/02/gdc-funding-models-for-casual-and-web-based-games/</guid>
		<description><![CDATA[Casual games are fun to play and fun to make, but can a developer make a living from them? Funding the casual business was one of the prominent questions at this year&#8217;s GDC. I want back and forth between the Casual Games and Worlds in Motion summits, and managed to gather a few interesting tidbits. Here are my impressions.
The [...]]]></description>
			<content:encoded><![CDATA[<p>Casual games are fun to play and fun to make, but can a developer make a living from them? Funding the casual business was one of the prominent questions at this year&#8217;s GDC. I want back and forth between the <em>Casual Games</em> and <em>Worlds in Motion</em> summits, and managed to gather a few interesting tidbits. Here are my impressions.</p>
<p>The popular funding models are: </p>
<ul>
<li>Indirect ad sales - ad rotator networks, etc., which let people participate in a larger pool of ad space providers</li>
<li>Direct ad sales - such as doing a deal with a specific client, for banner or in-game ads</li>
<li>Shareware model - aka try-and-buy, typically with a content lock (e.g. first level free) or a time lock (first hour free)</li>
<li>Subscription - the standard MMO approach, pay per month</li>
<li>Virtual goods model - aka free to play, pay for items</li>
</ul>
<p>Of these, advertising is the most popular entry-level option, but it has its pros and cons.</p>
<p>Indirect ads are the easiest to get into - just sign up with AdSense or one of the other ad networks, and watch the money come flowing in! Except apparently it&#8217;s not that much money. People were mentioning ad revenue figures on the order of a few cents per thousand impressions. <a href="#1"><sup>1</sup></a> </p>
<p>Direct ad sales seem to fare better, since you can deal with clients who want to target specific demographics. In their Worlds in Motion session, the WarBook devs praised this model. But they also admitted to 700 million pageviews per month (across all of their games), and their access to players&#8217; demographic info makes them very appealing to advertisers. A brand new project won&#8217;t be able to play in that league.</p>
<p>The other popular model is shareware - downloadable games with free trial versions, and full versions available for purchase. It&#8217;s a classic model (Doom and Quake come to mind), with the trial version typically limited in content (e.g. first level or chapter free) or game time (e.g. first hour free).</p>
<p>This model was the focus of much discussion, since it&#8217;s the primary model for the established players. It&#8217;s clear that it can do wonders for hit games, especially since the full game price is customarily set at $10 or $20. But standard games don&#8217;t fare as well. Only ~1% of players end up buying the full version, and even then, distributors take a <a href="http://www.hollywoodreporter.com/hr/search/article_display.jsp?vnu_content_id=1000535245">significant chunk</a>. Additionally, most downloadable games get their players from portals, which exhibit significant churn due to a large number of very similar games being created - so differentiation and getting to the top of listings are both difficult.</p>
<p>The other two models are subscriptions, and &#8220;free to play, pay for items.&#8221; The latter has worked very well for <a href="http://threerings.net">us</a>, while the former works for others, including the <a href="http://www.worldofwarcraft.com/index.xml">800-pound gorilla of MMOs</a>. Both systems have a significant optimal case, however - they&#8217;re best for games where players are compelled to keep coming back to the game, over and over again. Fitting this against &#8221;consumable&#8221; games is non-trivial.</p>
<p>So the dominant model is still content-limited shareware, but it&#8217;s not an optimal case: budgets can be brutal unless your game becomes a huge hit. But banking on blockbusters is a classic hit-driven studio funding scheme, with familiar drawbacks, only recapitulated on a smaller scale. A more stable model is something like subscriptions or virtual items, which scale nicely, but they require a larger, continuous kind of a game.</p>
<hr /><a title="1" name="1"></a>1. This roughly fits with announcements I&#8217;ve seen elsewhere, e.g. MSN Games offering developers <a href="http://www.microsoft.com/presspass/press/2007/feb07/02-07CasualGamesAdSharePR.mspx">10% of ad revenue</a> from the games they publish.</p>
]]></content:encoded>
			<wfw:commentRss>http://robert.zubek.net/blog/2008/03/02/gdc-funding-models-for-casual-and-web-based-games/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Viral Coefficient Calculation</title>
		<link>http://robert.zubek.net/blog/2008/01/30/viral-coefficient-calculation/</link>
		<comments>http://robert.zubek.net/blog/2008/01/30/viral-coefficient-calculation/#comments</comments>
		<pubDate>Thu, 31 Jan 2008 07:09:27 +0000</pubDate>
		<dc:creator>robert</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<category><![CDATA[business]]></category>

		<category><![CDATA[games]]></category>

		<category><![CDATA[web]]></category>

		<guid isPermaLink="false">http://robert.zubek.net/blog/2008/01/30/viral-coefficient-calculation/</guid>
		<description><![CDATA[Viral Coefficient and Growth
The viral coefficient is a measure of how many new users are brought in by each existing user. It&#8217;s a quick and easy way to measure growth: if the coefficient is 1.0, the site grows linearly, and if it&#8217;s less than that, it will slow down. And if the coefficent is higher than 1.0, you [...]]]></description>
			<content:encoded><![CDATA[<p><strong>Viral Coefficient and Growth</strong></p>
<p>The viral coefficient is a measure of how many new users are brought in by each existing user. It&#8217;s a quick and easy way to measure growth: if the coefficient is 1.0, the site grows linearly, and if it&#8217;s less than that, it will slow down. And if the coefficent is higher than 1.0, you have superlinear growth of a runaway hit.</p>
<p>In an invite-only situation (e.g. gmail closed beta), it&#8217;s easiest to calculate this directly, based on how many people are being invited by each new user, and how many of the invitees create new accounts themselves. The viral coefficient is simply:</p>
<p><em>v = new user invites accepted / new users<br />
   = accepts<sub>t</sub> / δp<sub>t-1</sub></em></p>
<p>where <em>δp<sub>t</sub></em> denote the number of new users who join in time slice <em>t</em> (ie, the increase in population between <em>p<sub>t-1</sub></em> and <em>p<sub>t</sub></em>). </p>
<p>But most sites have an open account creation policy. For those, we&#8217;ll have to estimate population acceleration from raw population deltas. Let&#8217;s assume that each accepted invite is quivalent to creating a new account at the next time slice. Then we can estimate virability as:</p>
<p><em>v ≈ δp<sub>t</sub> / δp<sub>t-1<br />
</sub>   = (p<sub>t</sub> - p<sub>t-1</sub>) / (p<sub>t-1</sub> - p<sub>t-2</sub>)</em></p>
<p>which is an acceleration metric, easy to compute from historical data. <br />
<strong> </strong></p>
<p><strong>Population Forecasting</strong> </p>
<p>Viral estimate calculated as momentary acceleration will fluctuate over time. But we can use it for some short term forecasting.</p>
<p>To calculate expected future population <em>p<sub>t</sub></em> some <em>t</em> steps in the future, given the viral coefficient and present population <em>p<sub>0</sub></em>, we first invert the above:</p>
<p><em>δp<sub>t</sub> = v δp</em><sub><em>t-1 </em></sub>= &#8230; = <em>v<sup>t</sup> δp<sub>0</sub></em></p>
<p>and plug this right back in:</p>
<p><em>p<sub>t</sub> = δp<sub>t</sub> + p<sub>t-1</sub><br />
</em><em>    = Σ<sub>k≤t</sub>  δp<sub>k</sub> + p</em><em><sub>0<br />
</sub>    = Σ<sub>k≤t</sub>  v<sup>k</sup> δp<sub>0</sub> + p<sub>0</sub></em></p>
<p>This describes a geometric series. When <em>v ≠ 1,</em> <em>p<sub>t</sub>  = δp<sub>0 </sub>(1 - v<sup>t+1</sup>) / (1 - v) + p<sub>0</sub> </em></p>
]]></content:encoded>
			<wfw:commentRss>http://robert.zubek.net/blog/2008/01/30/viral-coefficient-calculation/feed/</wfw:commentRss>
		</item>
		<item>
		<title>ActionScript 3 bytecode performance</title>
		<link>http://robert.zubek.net/blog/2008/01/12/actionscript-3-bytecode-performance/</link>
		<comments>http://robert.zubek.net/blog/2008/01/12/actionscript-3-bytecode-performance/#comments</comments>
		<pubDate>Sat, 12 Jan 2008 18:07:27 +0000</pubDate>
		<dc:creator>robert</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<category><![CDATA[actionscript]]></category>

		<category><![CDATA[flash]]></category>

		<guid isPermaLink="false">http://robert.zubek.net/blog/2007/09/29/actionscript-3-bytecode-performance/</guid>
		<description><![CDATA[Here are the results of a little benchmark I ran a while back, trying to figure out how expensive are the different primitive operations in Flash 9 (ie. bytecode compiled ActionScript 3).
Quick note on methodology: I ran these primitive operations in a tight loop of a few million iterations, to get average runtime per iteration (subtracting out the [...]]]></description>
			<content:encoded><![CDATA[<p>Here are the results of a little benchmark I ran a while back, trying to figure out how expensive are the different primitive operations in Flash 9 (ie. bytecode compiled ActionScript 3).</p>
<p>Quick note on methodology: I ran these primitive operations in a tight loop of a few million iterations, to get average runtime per iteration (subtracting out the runtime of an empty loop, of course), and then repeated this a handful of times. I&#8217;m reporting mean numbers below, and standard deviations only when they&#8217;re unusual.</p>
<p>The raw times aren&#8217;t very important (benchmarks were done on an old Athlon 2400+ that&#8217;s living out its retirement years under my kitchen table). But the proportional differences between them can be surprising - especially the order-of-magnitude ones.</p>
<p>Now, on to some delicious results!</p>
<p><strong>Numbers</strong></p>
<p>Some obligatory numeric operation benchmarks:</p>
<blockquote><p>Signed integer addition: 6.3ns<br />
Signed integer multiplication: 13.1ns<br />
Signed integer division: 15.3ns</p>
<p>Unsigned integer addition: 9.6ns<br />
Unsigned integer multiplication: 28.3ns<br />
Unsigned integer division: 32.4ns</p>
<p>Double addition: 9.4ns<br />
Double multiplication: 7.4ns<br />
Double division: 7.1ns</p></blockquote>
<p>There&#8217;s really no reason not to use doubles for virtually all math operations; they&#8217;re much cheaper (possibly because they don&#8217;t try to do any bounds checks).</p>
<p>The one exception would be array indexing, where one really wants to guarantee that the index is an integer. Fortunately, conversions are pretty inexpensive as well:</p>
<blockquote><p>Integer -&gt; Number conversion: 7.3ns<br />
Number -&gt; integer conversion: 5.0ns</p></blockquote>
<p>Now for some math library operations:</p>
<blockquote><p>Math.sqrt: 137.9ns<br />
Math.sin: 183.3ns<br />
Math.round: 197.9ns<br />
Math.floor: 160.2ns</p></blockquote>
<p>That&#8217;s pretty good, much faster than regular function calls (see below) - probably because these are native, instead of bottoming out in bytecode like everything else. Still, a square root is on the order of about 20 times more expensive than arithmetic operations.</p>
<p><strong>Function Calls</strong></p>
<p>Speaking of which, here are the regular function calls - just the call, with no body:</p>
<blockquote><p>Function call (no arguments): 239.8ns<br />
Function call (unary): 257.6ns<br />
Function call (binary): 269.0ns</p></blockquote>
<p>Pretty standard stuff, with cost increasing (linearly?) as a function of the number of arguments. It&#8217;s more expensive than calling native functions, but not significantly higher. Still, it does put an upper limit of about 4 million function calls a second, or 100,000 calls per frame, on my test machine.</p>
<p>By the way, this was completely unexpected:</p>
<blockquote>
<p align="left">Anonymous function declaration: 11368.8ns (s = 105.3)</p>
</blockquote>
<p>Creating an instance of an anonymous function costs <em>11 microseconds?</em> Ouch. I&#8217;m a big fan of treating everything as lists of data, and map-filtering them with ad-hoc anonymous functions. But now I have to be more careful. <img src='http://robert.zubek.net/blog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p><strong>Arrays</strong></p>
<p>For array access, I&#8217;ve only checked access of a single array element, many times over. Some numbers:</p>
<blockquote><p>Writing from variable to array: 69.0ns, s = 1.9<br />
Reading from array to variable: 106.3ns, s = 105.5<br />
Reading from object with integer keys: 72.0ns, s = 1.9<br />
Reading from object with string keys: 242.8ns, s = 1.8</p></blockquote>
<p>First, I thought it was interesting that an object with integer keys is just as fast as an array with integer keys, suggesting a shared underlying implementation. An object with string keys is slower, however.</p>
<p>Two, the array reading measurement is not only pretty high, it also has a very high standard deviation, as if the measurements included a few really expensive outliers. Am I running into intermittent delays when accessing array data; perhaps cache stalls?</p>
<p>Finally, some results trying to iterate over a large array:</p>
<blockquote><p>Iteration over array using &#8216;for each&#8217; special syntax: 8576.8ns, s = 2.4<br />
Iteration over array using int: 20483.5ns, s = 4.6<br />
Iteration over array using uint: 19665.3ns, s = 2.8<br />
Iteration over array using forEach: 31416.8ns, s = 47.7</p></blockquote>
<p>I&#8217;m not susprised that code inside a for loop is faster than code inside a function called via forEach. What I am surprised about, is that the &#8220;for each ( &#8230; )&#8221; special form is <em>much </em>faster than the standard &#8220;for ( ; ; )&#8221; iteration with array lookup. That&#8217;s not what you&#8217;d expect in a language like C; I&#8217;m curious what&#8217;s going on under the hood.</p>
]]></content:encoded>
			<wfw:commentRss>http://robert.zubek.net/blog/2008/01/12/actionscript-3-bytecode-performance/feed/</wfw:commentRss>
		</item>
		<item>
		<title>AIIDE</title>
		<link>http://robert.zubek.net/blog/2007/06/05/aiide/</link>
		<comments>http://robert.zubek.net/blog/2007/06/05/aiide/#comments</comments>
		<pubDate>Wed, 06 Jun 2007 04:27:15 +0000</pubDate>
		<dc:creator>robert</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<category><![CDATA[ai]]></category>

		<category><![CDATA[aiide]]></category>

		<category><![CDATA[games]]></category>

		<guid isPermaLink="false">http://robert.zubek.net/blog/?p=11</guid>
		<description><![CDATA[AIIDE starts tomorrow at Stanford. We have a very impressive array of speakers this year, and an intriguing collection of papers. Hope to see you there!
]]></description>
			<content:encoded><![CDATA[<p>AIIDE starts tomorrow at Stanford. We have a <a href="http://www.aiide.org/speakers.html">very impressive array of speakers</a> this year, and an intriguing collection of <a href="https://www.aaai.org/Library/AIIDE/aiide07contents.php">papers</a>. Hope to see you there!</p>
]]></content:encoded>
			<wfw:commentRss>http://robert.zubek.net/blog/2007/06/05/aiide/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Talk at Yahoo! Research Berkeley</title>
		<link>http://robert.zubek.net/blog/2007/04/08/talk-at-yahoo-research-berkeley/</link>
		<comments>http://robert.zubek.net/blog/2007/04/08/talk-at-yahoo-research-berkeley/#comments</comments>
		<pubDate>Mon, 09 Apr 2007 06:14:37 +0000</pubDate>
		<dc:creator>robert</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<category><![CDATA[three rings]]></category>

		<category><![CDATA[yahoo]]></category>

		<guid isPermaLink="false">http://robert.zubek.net/blog/?p=8</guid>
		<description><![CDATA[Ayman invited me to give a talk at Y!RB this week, about our web-based game whirled world. The talk was a lot of fun, though a little technical - I did go into fairly painful details on both our software and community development efforts.

Afterwards, I got to hang out with some nice Yahoo people, and [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://research.yahoo.com/~shamma">Ayman</a> invited me to give a <a href="http://upcoming.yahoo.com/event/167038/">talk at Y!RB</a> this week, about our web-based game <span style="text-decoration: line-through;">whirled</span> world. The talk was a lot of fun, though a little technical - I did go into fairly painful details on both our software and community development efforts.</p>
<p><a href="http://www.flickr.com/photos/rnair/448775540/in/photostream/"><img src="http://farm1.static.flickr.com/226/448775540_4b26e75f80.jpg?v=0" alt="" width="500" height="375" /></a></p>
<p>Afterwards, I got to hang out with some nice Yahoo people, and chat about their research and the future of web-based social interaction. Very stimulating. I wish I could do that more often. <img src='http://robert.zubek.net/blog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /></p>
]]></content:encoded>
			<wfw:commentRss>http://robert.zubek.net/blog/2007/04/08/talk-at-yahoo-research-berkeley/feed/</wfw:commentRss>
		</item>
	</channel>
</rss>
