<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-4326142759936870351</id><updated>2012-01-07T19:14:34.978-08:00</updated><category term='Official'/><category term='PHP'/><category term='Python'/><category term='App::perlbrew'/><category term='Revision'/><category term='Research'/><category term='green threads'/><category term='comparison'/><category term='BrightScript'/><category term='enterprise'/><category term='Distributed'/><category term='Language Design'/><category term='Projects'/><category term='Catharsis'/><category term='Update'/><category term='Roku'/><category term='Perl'/><category term='Tutorial'/><category term='Programming'/><category term='threading'/><category term='coroutines'/><title type='text'>Yakety Hack</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://yaketyhack.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4326142759936870351/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://yaketyhack.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>kbenson</name><uri>http://www.blogger.com/profile/14874291376019276497</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>13</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-4326142759936870351.post-4750135763233201824</id><published>2011-05-31T14:25:00.000-07:00</published><updated>2011-06-07T16:00:53.999-07:00</updated><title type='text'>Maintainability, Security vs Perfectly Deployed</title><content type='html'>I was part of an interesting conversation today.  Specifically, a client for the &lt;a href="http://a-1networks.com"&gt;day job&lt;/a&gt; wanted some feedback on their proposed push to the cloud.  Their plan was architected by their main developer, but the client wanted our input since we manage their existing set of servers (an eclectic set of differently configured and versioned FreeBSD boxes and a CentOS 5 web server that we had migrated their main web presence (and store) to a few years back).&lt;br /&gt;&lt;br /&gt;The developer's server layout was simple enough, and should cause no problems; Using goGrid's cloud offering, a set of web servers and app servers behind the included F5 load balancer (comes with the cloud), with a MySQL cluster on the back-end.  Now, I'm sure it's going to start as just a single server of each type, if even that, but that's a fairly vanilla scalable cloud architecture, no surprises there.  The problem is, of course, in the details.  It even starts innocuous enough, the developer wants to run Debian on the systems.&lt;br /&gt;&lt;br /&gt;Now, I don't have anything against Debian.  It's a fine distribution, one of the big few, in fact.  The &lt;i&gt;suggestion&lt;/i&gt; I had though, was to use a corporate backed distribution in lieu of Debian.  My reasoning goes like so; having market pressure to fix problems is a wonderful incentive to get important things fixed FAST.  In this respect, for an enterprise deployment, I view Ubuntu (LTS) as a more fitting if a debian-like distribution is desired.  When running a business, it's important to know that the company responsible for your server software is tied financially to providing the support you need.&lt;br /&gt;&lt;br /&gt;That's simple enough, and hardly a point to get caught up on for anyone not trying to take a hardline stance on software freedom (a point which would be hard to defend for a company that develops and sells software as the client does, and that wasn't their intention).  The real problem comes next.  The developer wants to install a minimal debian root and manually compile all software needed into /srv along with the content.  His reasons are simple, but in my opinion, very, very wrong.&lt;br /&gt;&lt;br /&gt;I'll outline them here, and explain in detail why from an administration and maintenance standpoint, these are the wrong choices.&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;He wants the extra performance compiling a lean Apache with only required modules compiled in directly allows&lt;/li&gt;&lt;br /&gt;&lt;li&gt;He doesn't want to use a package manager, as it makes everything too hard to deal with.  He would prefer a few hand written shell scripts to do building.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;He wants more control over security updates, to only apply and reload services when the security problem affects them&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;I'm not going to spend much time addressing the specific incident that spurred this.  Based on the conversation, I think this developer just needs some education as to what features are available.  I'm much more interested in addressing the idea as a whole that a customized environment is much better than using what you get from the enterprise distributions.  As such, I want to focus on addressing the generalities of these statements, and why they either don't hold true, or need to be weighed in context of both their positive and negative consequences.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;i&gt;Note: This is all aimed at small to medium businesses, where a large set of &lt;b&gt;system administrators&lt;/b&gt; on staff is not a requirement nor desired.  At a certain scale (or in certain markets), it DOES make sense to start doing your own system engineering to get a competitive edge.  If you are one of those companies, this doesn't apply to you.  If you don't know if you are one of those companies, then you either aren't one, or you aren't in a position to care&lt;/i&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Let's start by addressing the idea that compiling a custom version of apache with components and features excluded will result in extra performance.  First, this is true.  My experience, common sense, and the &lt;a href="http://httpd.apache.org/docs/2.2/misc/perf-tuning.html"&gt;developers themselves&lt;/a&gt; state there are compilation options that will result in a faster program.  &lt;i&gt;My&lt;/i&gt; point is that it's generally not worth it.  If you are building a system and you expect load to increase, scale out, don't scale up.  That is, design the system so extra nodes can be easily added, don't overly optimize each node yet.  All optimization, especially early optimization, limits your future choices (or at a minimum makes certain choices harder than they normally would) in some respect, while enhancing the feasibility of others.  This isn't conducive for the best solutions later.&lt;br /&gt;&lt;br /&gt;The next item is about package management.  The claim is that it makes everything too hard to deal with (the actual words used included "nightmare").  I can only attribute this towards lack of experience with package management systems.  The RPM format, for instance, is quite flexible and allows for everything from compiled binaries, scripts, documentation or even an empty packages that just act as a node to pull in all the required packages for a complete subsystem of functionality.  In an RPM spec file, you specify the requirements, the build process (how to unpackage, patch, build and deploy), and pre or post-install procedures, and a list of all installed files and their locations and types.  This last bit is really helpful, as it lets the package manager know what files are considered configuration files, and how to treat them (backup existing and overwrite, place new file next to existing with new extension, etc).  With this you get complete package file listings, file integrity and permission checks, easy installation and removal, dependency tracking, and a complete separation of your runtime environment from your content in a manageable manner.&lt;br /&gt;&lt;br /&gt;Finally, there is apparently a desire by the developer to manage all security updates manually, so security updates don't negatively impact the production environment.  This shows the biggest ignorance of what an enterprise distribution really provides, which is reliability.  There's a few things to address where, so I'll spread it out into a few areas; security management, testing, and finally division of roles.&lt;br /&gt;&lt;br /&gt;Something it seems many people don't understand is that enterprise distributions back-port patches to the versions of programs they shipped with so they don't need to change their environment.  That is, if RHEL5 shipped with Apache 2.0.52, chances are it will stay version 2.0.52.  Specifically, Red Hat will handle any security problems, and back-port any fixes from the version they were implemented in to the version they shipped.  This allows for a stable environment, where all you have to worry about is that bugs and security fixes are dealt with, but all other functionality stays the same.  API changes in newer versions?  Removed features?  New bugs?  Not your problem.  Note: In RHEL systems, &lt;i&gt;some&lt;/i&gt; packages may be considered for new versions on point releases, generally every six months.  You can rely on they updates to not reduce functionality, and to be compatible with the prior version in all aspects you may rely on.  Additionally, instead of upgrading the version, they may choose to back-port an entire feature into the older version.  These changes all go through extensive QA processes, and are deployed to thousands of systems, which happens to be the next point.  In the end, what this really means is that &lt;b&gt;you get security updates without having to worry unduly about whether they will cause a problem in your environment.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Testing is important.  By rolling your own environment, you are saying that you believe you can integrate all the components, and test them sufficiently that you feel confident that the environment should perform as needed without problem.  Most developers get this part right, as they know their core needs better than anyone else.  The problem is on all the little changes.  &lt;i&gt;You need to re-certify this environment after any change.&lt;/i&gt;  How many developers take the time to do this?  It's drudge work, it's hard to find all the edge cases (much lest test them), and most of the time, it just works.  Or appears to, at least.  One of the single biggest advantages to using a pre-packaged binary with wide distribution is that any problems are likely to have been encountered before you, and they might have been documented and fixed already as well.  You are essentially leveraging the QA, testing and pre-existing deployments of both the ditributer (such as Red Har or Ubuntu), and the thousands of companies that rely on them.&lt;br /&gt;&lt;br /&gt;Finally, there's the division of roles.  Should a developer really be responsible for tracking down, examining, applying and testing security updates?  These should be fire-and-forget procedures that cause no worry, and nothing but a slight blip on the meters are services are restarted as needed.  A developer should be building new products or supporting old products, not performing the role of a sys-admin because they decided they needed a little bit more control.  That just means the company isn't getting their money's worth for the developer, as that's all wasted time.&lt;br /&gt;&lt;br /&gt;In the end, this all boils down to a trade-off in terms of functionality for other features, such as ease of maintenance and security.  In most cases, you don't want to skimp on the maintainability, and especially not the security.  Those can be real liabilities for a company later.  In most cases, it's much easier to become a little bit less flexible but much more stable and secure.  And also, who's to say there's not a middle ground here.  Some packages may very well be better running with cutting edge versions than are manually compiled, but that doesn't necessarily mean that EVERYTHING must be.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4326142759936870351-4750135763233201824?l=yaketyhack.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://yaketyhack.blogspot.com/feeds/4750135763233201824/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://yaketyhack.blogspot.com/2011/05/maintainability-security-vs-perfectly.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4326142759936870351/posts/default/4750135763233201824'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4326142759936870351/posts/default/4750135763233201824'/><link rel='alternate' type='text/html' href='http://yaketyhack.blogspot.com/2011/05/maintainability-security-vs-perfectly.html' title='Maintainability, Security vs Perfectly Deployed'/><author><name>kbenson</name><uri>http://www.blogger.com/profile/14874291376019276497</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4326142759936870351.post-3674013351101008426</id><published>2011-05-27T11:00:00.001-07:00</published><updated>2011-05-27T11:06:12.902-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Catharsis'/><category scheme='http://www.blogger.com/atom/ns#' term='PHP'/><category scheme='http://www.blogger.com/atom/ns#' term='Language Design'/><title type='text'>PHP Saddens me.</title><content type='html'>It has for a long time.  Unfortunately, I'm often forced to work with it.  Often, in trying to explain my sadness (which often alternates to anger), I reach for the same few examples of PHP's brain-dead design.  &lt;a href="http://www.phpsaddness.com"&gt;PHP Sadness&lt;/a&gt; does a good job of explaining my pain.  I have personally griped about at least half the things on their list.&lt;br /&gt;&lt;br /&gt;If you are primarily a PHP coder, visit the site for a good indication of why your life is harder than it need be.  If you are a sometimes PHP coder that specializes or prefers another language, visit for more reasons to shun PHP, and hope your language doesn't share more than a smattering of these problems.  If you never program PHP, visit for the smug satisfaction of a landmine evaded.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4326142759936870351-3674013351101008426?l=yaketyhack.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://yaketyhack.blogspot.com/feeds/3674013351101008426/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://yaketyhack.blogspot.com/2011/05/php-saddens-me.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4326142759936870351/posts/default/3674013351101008426'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4326142759936870351/posts/default/3674013351101008426'/><link rel='alternate' type='text/html' href='http://yaketyhack.blogspot.com/2011/05/php-saddens-me.html' title='PHP Saddens me.'/><author><name>kbenson</name><uri>http://www.blogger.com/profile/14874291376019276497</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4326142759936870351.post-8517043120022490774</id><published>2011-05-26T17:15:00.000-07:00</published><updated>2011-05-26T17:36:54.075-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Distributed'/><category scheme='http://www.blogger.com/atom/ns#' term='Perl'/><category scheme='http://www.blogger.com/atom/ns#' term='Projects'/><category scheme='http://www.blogger.com/atom/ns#' term='Research'/><title type='text'>Distributed Workers</title><content type='html'>I have a project coming up where I'll need to utilize distributed workers.  It's a bit odd in that the workers will come and go, and they'll most likely need a copy of the subset of the data they are working on so they can efficiently process it, but the workload is very time dependent.  Put another way, I need to keep data synchronized in some fashion between the master server and the remote client in a way that is testable.&lt;br /&gt;&lt;br /&gt;I'm thinking I'm going to see problems where two works are working on the same data set but in doing so get slightly different results.  Returning different results is not only possible, but probable, since processing the data requires them check a resource that sometimes flaps between values when in transition, for minutes at a time. &lt;br /&gt;&lt;br /&gt;Here's the criteria for the system as I see it so far:&lt;br /&gt;&lt;br /&gt;Server:&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Canonical data source; Data stored in some sort of DB&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Accepts registrations from clients/workers&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Creates jobs/tasks in a work queue&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Assigns jobs/tasks from work queue to registered workers&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Accepts results from workers or times out task after appropriate wait&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Client (worker): &lt;ul&gt;&lt;br /&gt;&lt;li&gt;Mostly shared code base (re-use modules defining data as objects)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Registers with server&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Accepts tasks from server, processes data, returns result&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Keeps copy of current set of data it is responsible for processing, only returns changes to data, not whole update&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Here's what I'm wondering:&lt;br /&gt;&lt;br /&gt;How much of this is based in my assumptions for what I'll need underneath?  I've already thought of the DB structure needed to support this, and how I'll link between all the structures in the data.  If I assume I'm using some sort of NoSQL solution, such as MongoDB, CouchDB (or whatever it's called now), or something else, are there assumptions I can make about the system that reduces complexity?&lt;br /&gt;&lt;br /&gt;Are there modules available (preferably in Perl) to manage some of the work assignment tasks for me?&lt;br /&gt;&lt;br /&gt;I would prefer to pass object state back and forth for the tasks.  I can imagine passing an object name and a way to initialize that object to the state defined, that's not too hard.  I DO want to have the objects that I'm passing easily abstracted to the DB on the server side.  If I have the workers contain the same object code, can I do that without requiring the client deal with DB code?  That is, can I easily abstract the object ORM layer out from the client?  Maybe with roles using Moose?&lt;br /&gt;&lt;br /&gt;If I use Moose, I know there's a startup speed penalty, which is not a problem.  I'm more worried about any execution inefficiencies, since this is time dependent (to a sub-second level, but not quite ms dependent level.  I haven't had a chance to use Moose in a project yet, so I'm not aware of the specifics.  I do hear it's tunable so I can omit features for speed, which is a nice trade-off.&lt;br /&gt;&lt;br /&gt;Some representation for the changes in a data structure, or just JSON if that works as a common format, would be very useful.  If I can find a module that provides this, great.  Otherwise, I suspect I'll be writing my own after researching data diffs.&lt;br /&gt;&lt;br /&gt;In any case, I'll update here as I come to conclusions or find solutions.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4326142759936870351-8517043120022490774?l=yaketyhack.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://yaketyhack.blogspot.com/feeds/8517043120022490774/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://yaketyhack.blogspot.com/2011/05/distributed-workers.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4326142759936870351/posts/default/8517043120022490774'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4326142759936870351/posts/default/8517043120022490774'/><link rel='alternate' type='text/html' href='http://yaketyhack.blogspot.com/2011/05/distributed-workers.html' title='Distributed Workers'/><author><name>kbenson</name><uri>http://www.blogger.com/profile/14874291376019276497</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4326142759936870351.post-4829726071498071148</id><published>2011-05-25T15:42:00.000-07:00</published><updated>2011-05-25T15:48:25.102-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Tutorial'/><category scheme='http://www.blogger.com/atom/ns#' term='Roku'/><category scheme='http://www.blogger.com/atom/ns#' term='BrightScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Official'/><title type='text'>Roku's own tutorials</title><content type='html'>Over at the &lt;a href="http://blog.roku.com"&gt;Roku Blog&lt;/a&gt; &lt;span style="font-weight:bold;"&gt;RokuChris&lt;/span&gt; has posted a set of &lt;a href="http://blog.roku.com/developer/2011/05/23/hello-world-2/"&gt;"Hello World" tutorials&lt;/a&gt; for the Roku using different components.  For anyone that understands &lt;a href="http://yaketyhack.blogspot.com/2011/05/start-developing-for-roku-part-4.html"&gt;Part 4&lt;/a&gt; of my tutorial set, they shouldn't be too hard to fully grok.&lt;br /&gt;&lt;br /&gt;These are different that my tutorials in that they use different components that are more likely to be in use in the average video streaming channel.  My tutorials are aimed more at using one component that has a lot of variability to examine the language and give people a solid start on the language.&lt;br /&gt;&lt;br /&gt;In any case, if you've been following my tutorials, I advise you take a look!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4326142759936870351-4829726071498071148?l=yaketyhack.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://yaketyhack.blogspot.com/feeds/4829726071498071148/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://yaketyhack.blogspot.com/2011/05/rokus-own-tutorials.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4326142759936870351/posts/default/4829726071498071148'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4326142759936870351/posts/default/4829726071498071148'/><link rel='alternate' type='text/html' href='http://yaketyhack.blogspot.com/2011/05/rokus-own-tutorials.html' title='Roku&apos;s own tutorials'/><author><name>kbenson</name><uri>http://www.blogger.com/profile/14874291376019276497</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4326142759936870351.post-9209576341535649259</id><published>2011-05-23T23:56:00.000-07:00</published><updated>2011-05-24T00:05:41.677-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Tutorial'/><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Roku'/><category scheme='http://www.blogger.com/atom/ns#' term='BrightScript'/><title type='text'>Start developing for the Roku Part 4: Iterate!</title><content type='html'>This is the fourth part of a multi-part series. Please be sure to check out Parts 1 through 3.&lt;br /&gt;&lt;br /&gt;So far our channel displays a few rectangles we've defined previously.  Now, let's explore some code constructs that allow us to process work in a more effective manner.&lt;br /&gt;&lt;br /&gt;For now, we'll keep working with the same main.brs file as in the previous parts.  This time, we're going to add the following code after the last setLayer() method call, and before the canvas.show() call:&lt;br /&gt;&lt;blockquote&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;        ' Create iteratively smaller boxes using a loop&lt;br /&gt;        ' Array of colors to use&lt;br /&gt;        colors = [ "#AA0000", "#0000AA", "#A0A0A0", "#F0F0F0" ]&lt;br /&gt;        ' Initial location data&lt;br /&gt;        shapeLocation = { x: 100, y: 100 }&lt;br /&gt;        shapeSize = { w: 400, h: 300 }&lt;br /&gt;        for i=0 to colors.count() -1&lt;br /&gt;                ' Access desired color&lt;br /&gt;                c = colors[i]&lt;br /&gt;                ' Update the location and size data&lt;br /&gt;                shapeLocation.x = shapeLocation.x + 25&lt;br /&gt;                shapeLocation.y = shapeLocation.y + 25&lt;br /&gt;                shapeSize["w"] = shapeSize["w"] - 50&lt;br /&gt;                heightVar = "h"&lt;br /&gt;                shapeSize[heightVar] = shapeSize[heightVar] - 50&lt;br /&gt;                ' Add shape to canvas&lt;br /&gt;                print "Adding shape"&lt;br /&gt;                print shapeLocation&lt;br /&gt;                print shapeSize&lt;br /&gt;                canvas.setLayer(1+i, {&lt;br /&gt;                        color: c,&lt;br /&gt;                        targetRect: shapeSize,&lt;br /&gt;                        targetTranslation: shapeLocation,&lt;br /&gt;                })&lt;br /&gt;        end for&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;/blockquote&gt;&lt;br /&gt;That's a doozy, huh?  Well, let's get to work explaining this.&lt;br /&gt;&lt;br /&gt;The first new non-comment line is assignment of an array.  Not an associative array, as we saw previously, but a regular array, which is of set of sequential items.  In this case, we are assigning four strings, each a hex color code, to a variable called &lt;b&gt;colors&lt;/b&gt;.  We'll be using these colors later.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;i&gt;Note: BrightScript isn't picky about what items go in an like some languages.  For example, we can easily create an array with the first item being an integer, the second being a string, and the third being another array or associative array.&lt;/i&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Next we assign two associative arrays to the variables shapeLocation and shapeSize.  These hold the same things their names suggest, but we'll be changing the values we set here later.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Loopty Loop&lt;/h4&gt;Here we've gotten to something really new, a &lt;a href='http://en.wikipedia.org/wiki/For_loop'&gt;for loop&lt;/a&gt;.  If you don't know what that is, I'll let Wikipedia explain the gory details, and summarize it here as a way to repeat a chunk of code multiple times.  Here we are looping by initializing a variable &lt;b&gt;i&lt;/b&gt; to 0, and looping until it's no longer true that &lt;b&gt;i&lt;/b&gt; is less than &lt;b&gt;colors.count()&lt;/b&gt; (&lt;b&gt;i&lt;/b&gt; is automatically incremented by one each pass through the loop).&lt;br /&gt;&lt;br /&gt;Now, there's one other thing not explained about the loop statement we just went over, and that's what &lt;b&gt;colors.count()&lt;/b&gt; means.  In this case, since &lt;b&gt;colors&lt;/b&gt; contains an array, &lt;b&gt;count()&lt;/b&gt; is a method provided for arrays which returns the number of items in the array.  Here the value returned is &lt;i&gt;4&lt;/i&gt; since that's the number of items we set in the array on creation.  If we had added or removed items since then, the &lt;b&gt;count()&lt;/b&gt; method would represent the current number of items in the array at this point.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;i&gt;Note: By convention we indent while within the loop.  This provides an easily identifiable visible clue that this code is slightly different than the surrounding code (it may execute multiple times).  Indentation and other non-enforced formatting are a very important part of the source.  Ignoring the benefits they bring to the readability of the program source will most likely cost you later.&lt;/i&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;The first thing we do within the loop is assign the color we want this box to be.  In this case, we take the loop variable &lt;b&gt;i&lt;/b&gt; and use it as an index into the &lt;b&gt;colors&lt;/b&gt; array using square brackets.  The first time through the loop, &lt;b&gt;i&lt;/b&gt; is &lt;i&gt;0&lt;/i&gt;, so we access the &lt;i&gt;0th&lt;/i&gt; (first for all you non-computer science people out there) item of the array.  The first pass through the loop that's the string &lt;i&gt;#AA0000&lt;/i&gt;, which is what the variable &lt;b&gt;c&lt;/b&gt; now contains.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;i&gt;Note: It may seem odd that we are looping from 0 to the number of items in the array &lt;b&gt;colors&lt;/b&gt; minus one, but that's because of a very particular fact of history; the C proggramming language is the most commone one on earth, and 40 years ago it was defined with arrays accessed in this manner (it actually makes &lt;i&gt;some&lt;/i&gt; sense in context).  We've been living with it ever since in many, many languages that claim some C heritage.  Just remember when accessing array elements that item 0 is the first item, and the number of items in the array less one is the last item.&lt;/i&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Accessing and Changing Arrays and Associative Arrays&lt;/h4&gt;The next 5 lines of code are all accessing and setting the elements of the shapeLocation and shapeSize associative arrays in various ways.  I'll cover them in order:&lt;ul&gt;&lt;li&gt;&lt;b&gt;shapeLocation.x = shapeLocation.x + 25&lt;/b&gt;&lt;br /&gt;&lt;i&gt;Element x of shapeLocation is set to the value of element x plus 25 more using dot notation.&lt;/i&gt;&lt;/li&gt;&lt;li&gt;&lt;b&gt;shapeLocation.y = shapeLocation.y + 25&lt;/b&gt;&lt;br /&gt;&lt;i&gt;Element y of shapeLocation is set to the value of element y plus 25 more using dot notation.&lt;/i&gt;&lt;/li&gt;&lt;li&gt;&lt;b&gt;shapeSize["w"] = shapeSize["w"] - 50&lt;/b&gt;&lt;br /&gt;&lt;i&gt;Element w of shapeSize is lessened by 50 using array subscript syntax&lt;/i&gt;&lt;/li&gt;&lt;li&gt;&lt;b&gt;heightVar = "h"&lt;/b&gt; and &lt;b&gt;shapeSize[heightVar] = shapeSize[heightVar] - 50&lt;/b&gt;&lt;br /&gt;&lt;i&gt;Here we set a new variable, and then use that variable to access the appropriate element of shapeSize.  This is the real power of the array subscript syntax.&lt;/i&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;i&gt;Note: While the same code is executed each iteration of the loop, the values of the variables end up changing each time.  For example, each pass through the loop ends up reducing shapeSize.w by 25, until it eventually ends up at 200, after starting at 400.&lt;/i&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Display More Shapes&lt;/h4&gt;After a few inconsequential prints to the debugger console, the next thing we do within the loop is draw another rectangle using the &lt;b&gt;setLayer()&lt;/b&gt; method.  Here we set the layer to 1+i, so we don't overwrite the item we already drew on layer 0 in the first pass of the loop, and draw each of the subsequently smaller rectangles using the color, size and location we've computed earlier in this pass of the loop.&lt;br /&gt;&lt;br /&gt;Finally, we end the loop with an &lt;b&gt;end loop&lt;/b&gt; statement, and following convention de-indent the code from here on.  That's the end of the new code for this part of the tutorial, and that's plenty if I do say so myself.  Below you can find the complete contents of the new &lt;b&gt;source/main.brs&lt;/b&gt; file, with a few extra spaces and comments thrown in the pretty it up.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;sub main()&lt;br /&gt;        ' Crate canvas component&lt;br /&gt;        canvas = CreateObject("roImageCanvas")&lt;br /&gt;&lt;br /&gt;        ' Set background color (no location data means full screen)&lt;br /&gt;        canvas.setLayer(0, { color: "#884400" })&lt;br /&gt;&lt;br /&gt;        ' Display a shape&lt;br /&gt;        newShapeLocation = { x: 300, y: 200, w: 200, h: 100 }&lt;br /&gt;        canvas.setLayer(10, { color: "#00BB00", targetRect: newShapeLocation })&lt;br /&gt;&lt;br /&gt;        ' Display some text&lt;br /&gt;        newTextAttributes = {&lt;br /&gt;                color: "#0000CC"&lt;br /&gt;                font: "Large"&lt;br /&gt;                Halign: "Hcenter"&lt;br /&gt;                Valign: "Vcenter"&lt;br /&gt;        }&lt;br /&gt;        canvas.setLayer(5, {&lt;br /&gt;                text: "Hello World!",&lt;br /&gt;                textAttrs: newTextAttributes,&lt;br /&gt;                targetRect: {&lt;br /&gt;                        x: 200, y: 200, w: 200, h: 100&lt;br /&gt;                }&lt;br /&gt;        })&lt;br /&gt;&lt;br /&gt;        ' Create iteratively smaller boxes using a loop&lt;br /&gt;        ' Array of colors to use&lt;br /&gt;        colors = [ "#AA0000", "#0000AA", "#A0A0A0", "#F0F0F0" ]&lt;br /&gt;        ' Initial location data&lt;br /&gt;        shapeLocation = { x: 100, y: 100 }&lt;br /&gt;        shapeSize = { w: 400, h: 300 }&lt;br /&gt;        for i=0 to colors.count() -1&lt;br /&gt;                ' Access desired color&lt;br /&gt;                c = colors[i]&lt;br /&gt;                ' Update the location and size data&lt;br /&gt;                shapeLocation.x = shapeLocation.x + 25&lt;br /&gt;                shapeLocation.y = shapeLocation.y + 25&lt;br /&gt;                shapeSize["w"] = shapeSize["w"] - 50&lt;br /&gt;                heightVar = "h"&lt;br /&gt;                shapeSize[heightVar] = shapeSize[heightVar] - 50&lt;br /&gt;                ' Add shape to canvas&lt;br /&gt;                print "Adding shape"&lt;br /&gt;                print shapeLocation&lt;br /&gt;                print shapeSize&lt;br /&gt;                canvas.setLayer(1+i, {&lt;br /&gt;                        color: c,&lt;br /&gt;                        targetRect: shapeSize,&lt;br /&gt;                        targetTranslation: shapeLocation,&lt;br /&gt;                })&lt;br /&gt;        end for&lt;br /&gt;&lt;br /&gt;        ' Show the canvas&lt;br /&gt;        canvas.show()&lt;br /&gt;&lt;br /&gt;        ' Print something to dbugger console&lt;br /&gt;        print "canvas shown"&lt;br /&gt;&lt;br /&gt;        ' Sleep so the channel doesn't end immediately&lt;br /&gt;        sleep(5000)&lt;br /&gt;&lt;br /&gt;end sub&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Go ahead and package and upload the channel now.  You should see what looks like square rings around the original "Hello World" text (or what is visible of it, at least).  This is because each subsequent rectangle was a higher layer then the previous and obscured the previous, yet they were all lower than the text we set before the loop, which stayed in front and thus visible.&lt;br /&gt;&lt;br /&gt;That concludes Part 4 of the tutorial.  Next we'll go into a bit more detail on functions, and how to use them.  I'll try to make it a bit shorter than this tutorial, which ended up going a bit longer than I hoped.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4326142759936870351-9209576341535649259?l=yaketyhack.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://yaketyhack.blogspot.com/feeds/9209576341535649259/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://yaketyhack.blogspot.com/2011/05/start-developing-for-roku-part-4.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4326142759936870351/posts/default/9209576341535649259'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4326142759936870351/posts/default/9209576341535649259'/><link rel='alternate' type='text/html' href='http://yaketyhack.blogspot.com/2011/05/start-developing-for-roku-part-4.html' title='Start developing for the Roku Part 4: Iterate!'/><author><name>kbenson</name><uri>http://www.blogger.com/profile/14874291376019276497</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4326142759936870351.post-8162555541155679907</id><published>2011-05-22T22:20:00.000-07:00</published><updated>2011-05-22T22:24:11.637-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Tutorial'/><category scheme='http://www.blogger.com/atom/ns#' term='Roku'/><category scheme='http://www.blogger.com/atom/ns#' term='BrightScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Revision'/><category scheme='http://www.blogger.com/atom/ns#' term='Update'/><title type='text'>Tutorial part 2 updated</title><content type='html'>&lt;a href='http://yaketyhack.blogspot.com/2011/05/start-developing-for-roku-part-2.html'&gt;Part 2&lt;/a&gt; of the Beginning Roku development tutorials has been updated.  It was horrible before.  Hopefully it's slightly less so now.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4326142759936870351-8162555541155679907?l=yaketyhack.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://yaketyhack.blogspot.com/feeds/8162555541155679907/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://yaketyhack.blogspot.com/2011/05/tutorial-part-2-updated.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4326142759936870351/posts/default/8162555541155679907'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4326142759936870351/posts/default/8162555541155679907'/><link rel='alternate' type='text/html' href='http://yaketyhack.blogspot.com/2011/05/tutorial-part-2-updated.html' title='Tutorial part 2 updated'/><author><name>kbenson</name><uri>http://www.blogger.com/profile/14874291376019276497</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4326142759936870351.post-1167890712802598792</id><published>2011-05-17T21:26:00.000-07:00</published><updated>2011-05-23T23:55:26.555-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Tutorial'/><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Roku'/><category scheme='http://www.blogger.com/atom/ns#' term='BrightScript'/><title type='text'>Start developing for the Roku Part 3: More to the picture (associative arrays)</title><content type='html'>This is the third in a multi-part series.  Please be sure to check out Part 1 and Part 2.&lt;br /&gt;&lt;br /&gt;When we last left off, I had just shown how to side-load, or upload, your channel to the Roku using the developer mode interface.  If you used the simple channel we created in the Part 1, it should have resulted in an orange screen that persisted for 5 seconds.  Now, it's time to add to that.&lt;br /&gt;&lt;br /&gt;Now, we are going to add a colored box, and some text.  To do so, take the main.brs file from Part 1 and add the following lines after the single existing &lt;b&gt;canvas.setLayer&lt;/b&gt; call:&lt;br /&gt;&lt;blockquote&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt; ' Display a shape&lt;br /&gt; newShapeLocation = { x: 300, y: 200, w: 200, h: 100 }&lt;br /&gt; canvas.setLayer(10, { color: "#00BB00", targetRect: newShapeLocation })&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;/blockquote&gt;&lt;br /&gt;There's a few things going on in these new lines, but first I'll explain that together they add a mostly green box that is 200 pixels wide and 100 pixels high starting 300 pixels from the left side of the screen and 200 pixels from the top of the screen.&lt;br /&gt;&lt;br /&gt;The first line is a comment.  Anything after a single-quote character until a newline is considered a comment, and it not evaluated as BrightScript code.&lt;br /&gt;&lt;br /&gt;In the second line, we assign an associative array to the variable &lt;b&gt;newShapeLocation&lt;/b&gt;.  Associative Arrays are created automatically when curly brases are used, and consist of colon separed key-value pairs, themselves separated by commas (or newlines, as we'll see later)&lt;br /&gt;&lt;br /&gt;Finally, we have another setLayer() call, and this time we are specifying the location of the shape we are drawing.  You can see now that setLayer() expects an Associative Array for the second argument, and the placement information is supplied as another associative array under the key &lt;b&gt;targetRect&lt;/b&gt;.  We could just as easily have called setLayer as so:&lt;br /&gt;&lt;blockquote&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt; canvas.setLayer(10, { color: "#00BB00", targetRect: { x: 300, y: 200, w: 200, h: 100 } })&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;/blockquote&gt;&lt;br /&gt;...but that is't quite as easy to read, is it?  Later we'll cover formatting to alleviate this issue somewhat.&lt;br /&gt;&lt;br /&gt;Okay, now that we've added a colored box, lets add some text.  The following should accomplish that:&lt;br /&gt;&lt;blockquote&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt; ' Display some text&lt;br /&gt; newTextAttributes = {&lt;br /&gt;  color: "#0000CC"&lt;br /&gt;  font: "Large"&lt;br /&gt;  Halign: "Hcenter"&lt;br /&gt;  Valign: "Vcenter"&lt;br /&gt; }&lt;br /&gt; canvas.setLayer(5, {&lt;br /&gt;  text: "Hello World!",&lt;br /&gt;  textAttrs: newTextAttributes,&lt;br /&gt;  targetRect: {&lt;br /&gt;   x: 200, y: 200, w: 200, h: 100&lt;br /&gt;  }&lt;br /&gt; })&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;/blockquote&gt;&lt;br /&gt;Here we see another comment, another associative array, and another call to setLayer().&lt;br /&gt;&lt;br /&gt;Notice how the newTextAttributes associative array spans multiple lines?  This is the formatting technique I mentioned before to make the data more reasonable.  Note the missing commas; in multi-line associative arrays they are optional (and as such supplying them won't hurt).&lt;br /&gt;&lt;br /&gt;Finally, note how the setLayer() call is extended over multiple lines with the associative array, and the targetRect is defined directly as another associative array within the first.  We could just as easily have passed a variable containing another associative array in its place as we did before, but with this formatting this is easy to read as is.&lt;br /&gt;&lt;br /&gt;Adding these all into the original main.brs file results in the following:&lt;br /&gt;&lt;blockquote&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;sub main()&lt;br /&gt; canvas = CreateObject("roImageCanvas")&lt;br /&gt; canvas.setLayer(0, { color: "#884400" })&lt;br /&gt; ' Display a shape&lt;br /&gt; newShapeLocation = { x: 300, y: 200, w: 200, h: 100 }&lt;br /&gt; canvas.setLayer(10, { color: "#00BB00", targetRect: newShapeLocation })&lt;br /&gt; ' Display some text&lt;br /&gt; newTextAttributes = {&lt;br /&gt;  color: "#0000CC"&lt;br /&gt;  font: "Large"&lt;br /&gt;  Halign: "Hcenter"&lt;br /&gt;  Valign: "Vcenter"&lt;br /&gt; }&lt;br /&gt; canvas.setLayer(5, {&lt;br /&gt;  text: "Hello World!",&lt;br /&gt;  textAttrs: newTextAttributes,&lt;br /&gt;  targetRect: {&lt;br /&gt;   x: 200, y: 200, w: 200, h: 100&lt;br /&gt;  }&lt;br /&gt; })&lt;br /&gt; canvas.show()&lt;br /&gt; print "canvas shown"&lt;br /&gt; sleep(5000)&lt;br /&gt;end sub&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;/blockquote&gt;&lt;br /&gt;Packaging and uploading the channel now should result in an orange display, some blue text saying "Hello World!", and a green box the partially obscures the text.  The reason the box obscures the text even through we defined the text later has to do with the layers we set for each (the first argument to setLayer()).  The higher the layer, the "closer" the object appears to the viewer, with closer objects obscuring older ones.&lt;br /&gt;&lt;br /&gt;This concludes Part 3.  Next, we'll look at loops, regular arrays, and accessing associative array components.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4326142759936870351-1167890712802598792?l=yaketyhack.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://yaketyhack.blogspot.com/feeds/1167890712802598792/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://yaketyhack.blogspot.com/2011/05/start-developing-for-roku-part-3-more.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4326142759936870351/posts/default/1167890712802598792'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4326142759936870351/posts/default/1167890712802598792'/><link rel='alternate' type='text/html' href='http://yaketyhack.blogspot.com/2011/05/start-developing-for-roku-part-3-more.html' title='Start developing for the Roku Part 3: More to the picture (associative arrays)'/><author><name>kbenson</name><uri>http://www.blogger.com/profile/14874291376019276497</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4326142759936870351.post-5722959382418549663</id><published>2011-05-17T13:47:00.000-07:00</published><updated>2011-05-22T21:48:50.555-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Tutorial'/><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Roku'/><category scheme='http://www.blogger.com/atom/ns#' term='BrightScript'/><title type='text'>Start developing for the Roku Part 2: Packaging and uploading</title><content type='html'>This is part 2 in a series.  You may want to see &lt;a href='http://yaketyhack.blogspot.com/2011/05/start-developing-for-roku-part-1-my.html'&gt;Part 1&lt;/a&gt; to figure out how we got to this point.&lt;br /&gt;&lt;br /&gt;Part 2 of this tutorial will cover how to package and upload your channel to the Roku.  This allows the Roku to compile your code and report any errors it encountered, and run the channel so you can test how it works.&lt;br /&gt;&lt;br /&gt;Now that we have something to upload to the Roku, we need to package it for side-loading.  Side-loading is the process of manually uploading a channel you've created using the Roku's developer mode. It doesn't require any special utilities out of the ordinary, and is very easy, but only one channel can be side-loaded at a time.&lt;br /&gt;&lt;br /&gt;There are three steps to side-loading your channel:&lt;ol&gt;&lt;li&gt;Enable developer mode&lt;/li&gt;&lt;li&gt;Package your channel&lt;/li&gt;&lt;li&gt;Upload your packaged channel&lt;/li&gt;&lt;/ol&gt;These are each extremely easy, and only #2 and #3 need be repeated to side-load after the first time.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Enabling Developer Mode&lt;/h4&gt;The first step to side-loading a channel is to enable developer mode.  To enable developer mode on the Roku, you need to enter the3 following sequence on the remote: Home, Home, Home, Up, Up, Roght, Left, Right, Left, Right.  This should cause a special "Developer Settings" screen to come up, which offers you the option to enable (or disable if it's already enabled) the installer. It will require a restart of the Roku, but after that you should be able to side-load channels without problem.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Packaging Your Channel&lt;/h4&gt;Packaging your channel for side-loading onto the Roku really just means compressing your channel into a ZIP archive.  Most modern operating systems ship with some sort of built in archival utility that can create ZIP archives, but if your operating system doesn't, you can download either WinZIP or WinRAR's free versions for your OS and use that to create the archive.&lt;br /&gt;&lt;blockquote&gt;&lt;i&gt;&lt;br /&gt;Note: While packaging for side-loading requires only creating a ZIP archive, packaging for upload as a Private or Public channel requires the extra step of signing the package.  This is covered in the Roku SDK in the Packaging and Publishing document, and may be covered in a future tutorial.&lt;br /&gt;&lt;/i&gt;&lt;/blockquote&gt;&lt;br /&gt;The important thing to remember when zipping your channel content is that the channel folder itself should not be part of the ZIP file. That is, if you examine the contents of the ZIP file, you should see a manifest file and source folder at the top level (plus any additional files, such as an images folder), NOT a single folder containing those items.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Uploading your channel&lt;/h4&gt;To finish side-loading your channel, you need to upload it to the Roku.  To upload just browse to your Roku's IP address in a browser, which should bring up the channel packager and installer interface.  Simply click the &lt;input type='button' value='Choose File'/&gt; button to select your packaged (zipped) channel, and then click &lt;input type='button' value='Install'/&gt; (or &lt;input type='button' value='replace'/&gt; if you've already got a channel side-loaded) to upload the channel.&lt;br /&gt;&lt;blockquote&gt;&lt;i&gt;&lt;br /&gt;Note: You can find your Roku's IP address by going to the &lt;b&gt;Settings&lt;/b&gt; section and choosing the &lt;b&gt;Player Info&lt;/b&gt; section.&lt;br /&gt;&lt;/i&gt;&lt;/blockquote&gt;&lt;br /&gt;Upon uploading your channel, it will run automatically.  It will also show up as the last channel in the list of channels on the Roku main screen so you can start it again without uploading it.  Currently, it won't have a useful image, but that's because we haven't defined one in the manifest file.&lt;br /&gt;&lt;blockquote&gt;&lt;i&gt;&lt;br /&gt;Note: When a side-loaded channel is running, you can telnet to port 8085 using the Roku's IP address, and you'll have access to a debugger console. This will show the output of any print statements, as well as any errors encountered in compiling or running the code.&lt;br /&gt;&lt;/i&gt;&lt;/blockquote&gt;&lt;br /&gt;When the channel runs, you should see an orange-ish screen for 5 seconds (if you are uploading the package from &lt;a href='http://yaketyhack.blogspot.com/2011/05/start-developing-for-roku-part-1-my.html'&gt;Part 1&lt;/a&gt;) before it returns to the Roku home screen.  If you had a telnet session to the debugger console open, you should also have seen a notice that the channel started running, and the output from the print statement we put in the channel in &lt;a href='http://yaketyhack.blogspot.com/2011/05/start-developing-for-roku-part-1-my.html'&gt;Part 1&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;This concludes Part 2.  In Part 3 we'll draw some more shapes on the screen and examine a few of the BrightScript core data types available (Arrays and Associative Arrays).&lt;br /&gt;&lt;br /&gt;Changelog:&lt;br /&gt;2011-05-22 21:47 Re-wrote and re-formatted.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4326142759936870351-5722959382418549663?l=yaketyhack.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://yaketyhack.blogspot.com/feeds/5722959382418549663/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://yaketyhack.blogspot.com/2011/05/start-developing-for-roku-part-2.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4326142759936870351/posts/default/5722959382418549663'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4326142759936870351/posts/default/5722959382418549663'/><link rel='alternate' type='text/html' href='http://yaketyhack.blogspot.com/2011/05/start-developing-for-roku-part-2.html' title='Start developing for the Roku Part 2: Packaging and uploading'/><author><name>kbenson</name><uri>http://www.blogger.com/profile/14874291376019276497</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4326142759936870351.post-765198195327947235</id><published>2011-05-17T12:49:00.000-07:00</published><updated>2011-09-16T16:39:02.853-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Tutorial'/><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Roku'/><category scheme='http://www.blogger.com/atom/ns#' term='BrightScript'/><title type='text'>Start developing for the Roku Part 1: My first channel</title><content type='html'>There's quite a few posts on the Roku Developer Forum about how to get started developing for the Roku, given that the platform uses a proprietary language called BrightScript.  So much so, in fact, that I decided it's time to write up a few tutorials on how to get your first channel going, starting from scratch.&lt;br /&gt;&lt;br /&gt;This isn't intended for those intending to just take an example channel (of which there are many in the SDK) and alter it, but for those struggling with the core concepts of the language, or that have trouble following what's happening in the examples provided.&lt;br /&gt;&lt;br /&gt;This is Part 1, which covers how to create the content of a very simple channel.  Part 2 will cover how to package and upload this channel to the Roku.&lt;br /&gt;&lt;br /&gt;First, we need to create the directory/folder structure for the channel, which will look like this:&lt;br /&gt;&lt;blockquote&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;-- myfirstchannel&lt;br /&gt;   |-- manifest (a text file)&lt;br /&gt;   |-- source (a folder)&lt;br /&gt;   |   `-- main.brs (text file for BrightScript code)&lt;br /&gt;   `-- images (a folder)&lt;br /&gt;       `-- ... (no contents currently)&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;/blockquote&gt;&lt;br /&gt;As you can see, we have a folder for the channel, containing a manifest file, and two more folders, source and images.  Within the source folder there is a file called main.brs which will contain our starting code.&lt;br /&gt;&lt;blockquote&gt;&lt;i&gt;&lt;br /&gt;Note: The file containing the code can be called anything as long as it resides within the source folder or a sub-folder of it, and ends in .brs.  More specifically, any file ending in .brs in source will be concatenated together at run-time, so the specific file names don't matter at all.&lt;br /&gt;&lt;/i&gt;&lt;/blockquote&gt;&lt;br /&gt;First things first, we need some content for our manifest file.  Let's use the following for now:&lt;br /&gt;&lt;blockquote&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;title=Tutorial Channel&lt;br /&gt;subtitle=The basics of BrightScript programming&lt;br /&gt;mm_icon_focus_hd=pkg:/images/not_here.png&lt;br /&gt;mm_icon_side_hd=pkg:/images/not_here.png&lt;br /&gt;mm_icon_focus_sd=pkg:/images/not_here.png&lt;br /&gt;mm_icon_side_sd=pkg:/images/not_here.png&lt;br /&gt;major_version=1&lt;br /&gt;minor_version=0&lt;br /&gt;build_version=00000&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;/blockquote&gt;&lt;br /&gt;In Windows, you'll want to make sure you can see file extensions, and that there isn't one.  The file contains text, but it doesn't have a .txt extension.  If you double click the file and it automatically opens in a text editor, you need to look into how to rename the file so there is no extension.  Also, please make sure you are saving these files as ASCII (non Word/RTF) text.&lt;br /&gt;&lt;br /&gt;Now we need to add some content to the channel.  Let's open up (create) source/main.brs in a text editor and add some content:&lt;br /&gt;&lt;blockquote&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;sub main()&lt;br /&gt;    canvas = CreateObject("roImageCanvas")&lt;br /&gt;    canvas.setLayer(0, { color: "#884400" })&lt;br /&gt;    canvas.show()&lt;br /&gt;    print "canvas shown"&lt;br /&gt;    sleep(5000)&lt;br /&gt;end sub&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;/blockquote&gt;&lt;br /&gt;Here we have a few things to examine.  Firstly, there's a subroutine (a function without a return value) called main.  This is how the Roku determines where to start executing code.  After concatenating all the source files together, and parsing the code, it looks for a function or subroutine called main to run, and starts there.&lt;br /&gt;&lt;br /&gt;Second, we create an object of type roImageCanvas and assign it to the canvas variable.  The CreateObject() function is how you access built in BrightScript and Roku components that have been provided to enhance the platform.  Almost every complex component of the system will be created with a call similar to this.&lt;br /&gt;&lt;br /&gt;Third, we call the setLayer() method on the canvas object to assign some data.  in this case, we are creating a layer 0 with an orange-ish color (as specified by the hex color #884400).  Don't worry too much about the curly braces, we'll cover those a bit later.  Just remember that we passed in a color attribute when we set the layer.&lt;br /&gt;&lt;br /&gt;Fourth, we call the show() method on the canvas object, which will cause it to actually show on the screen.  Without this command any changes to the canvas object are non-visible.&lt;br /&gt;&lt;br /&gt;Lastly, we print a simple statement that the canvas has been shown, and sleep for 5 seconds (5000 ms).  The print output won't be visible on the screen, it is only output to the debugger console, but we'll see it later.  The sleep statement pauses execution for a time.  If we didn't do this, you wouldn't see much for your channel, as the main subroutine would end, and the channel would exit back to the Roku main screen almost immediately.&lt;br /&gt;&lt;br /&gt;That concludes the first part of the tutorial.  In Part 2 we'll cover how to package the channel you just made and upload it to the Roku for testing.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4326142759936870351-765198195327947235?l=yaketyhack.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://yaketyhack.blogspot.com/feeds/765198195327947235/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://yaketyhack.blogspot.com/2011/05/start-developing-for-roku-part-1-my.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4326142759936870351/posts/default/765198195327947235'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4326142759936870351/posts/default/765198195327947235'/><link rel='alternate' type='text/html' href='http://yaketyhack.blogspot.com/2011/05/start-developing-for-roku-part-1-my.html' title='Start developing for the Roku Part 1: My first channel'/><author><name>kbenson</name><uri>http://www.blogger.com/profile/14874291376019276497</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4326142759936870351.post-6876849935009269921</id><published>2011-05-09T14:08:00.000-07:00</published><updated>2011-05-09T14:53:03.196-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='App::perlbrew'/><category scheme='http://www.blogger.com/atom/ns#' term='Perl'/><category scheme='http://www.blogger.com/atom/ns#' term='enterprise'/><title type='text'>Perlbrew to the rescue</title><content type='html'>Chromatic recently posted about the support lifetime of Perl, and it's &lt;a href="http://www.modernperlbooks.com/mt/2011/05/2018-is-the-year-of-perl-510.html"&gt;extension through enterprise distributions&lt;/a&gt;.  While I don't particularly buy his arguments against enterprise need for back-patching and supporting older versions of Perl (and I suspect neither does he completely.  He always strikes be as somewhat of a provocateur, a noble profession), I do agree that &lt;a href="http://search.cpan.org/%7Egugod/App-perlbrew-0.20/lib/App/perlbrew.pm"&gt;App::perlbrew&lt;/a&gt; is part of the solution.&lt;br /&gt;&lt;br /&gt;While we seem to be in agreement that perlbrew is the solution, he seems to think (it's ambiguous in the post) that perlbrew can't be included in existing enterprise releases, such as RHEL 5 (which I'm most familiar with, and will restrict my examples to).  I don't see any major reason that it can't be made available to existing enterprise distributions (in a supported manner, even).  RHEL has a long history of providing feature enhancements and new packages/programs in their point releases (as opposed to the strictly bug and security fixes between point releases), so including perlbrew would be easily accomplished.  Even if RHEL doesn't want to include it for whatever reason, getting it included in CentOS through their extras would be trivial (well, as trivial as doing anything with the CentOS developers is these days), and provide a real enhancement.&lt;br /&gt;&lt;br /&gt;Of course, the Perl versions installed from perlbrew themselves would not necessarily be supported, but that's an easy point of demarcation to define.  Different support policies could (and should) be defined for applications developed and/or deployed on a platform, as opposed to the platform itself.  This allows for easy updating of subsystems that aren't related to the deployed application, but are required for security reasons.&lt;br /&gt;&lt;br /&gt;I remember the most recent time I was migrating (and updating) RT.  I spent quite a while swimming through dependency hell, making RPMs of all the required CPAN modules that weren't already available through RPMForge, EPEL and the like.  During the final stages of that, visions of making my own RT bundle that auto installed Perl through perlbrew, along with the latest relevant CPAN modules were definitely dancing through my head.  I think the world is a more barren place for my lack of motivation after the migration project was complete.&lt;br /&gt;&lt;br /&gt;Now, for anyone who really just doesn't get why enterprise distributions need to keep the old version of Perl around, consider the following; enterprise distributions need the ability to ensure that during any update, nothing can or will go wrong (at least as much as they can).  This often means limiting an installed program to the original shipped version, and back-porting non-conflicting features.  When you have to upgrade hundreds of servers, this is essential.  This is the continual struggle between system administrators and developers.  Both groups are striving for stability, maintainability, and security, but these concepts mean slightly different things to each group.  The beauty of perlbrew is it allows each group to have their own sandbox that they can correctly apply their goals to.&lt;br /&gt;&lt;br /&gt;Please note than while I'm not sure the support requirements of perlbrew itself, if they aren't as minimal as possible to run on as old a version of Perl as possible, than I see that as a serious design flaw.  I don't believe this to apply to CPAN modules in general though.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4326142759936870351-6876849935009269921?l=yaketyhack.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://yaketyhack.blogspot.com/feeds/6876849935009269921/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://yaketyhack.blogspot.com/2011/05/perlbrew-to-rescue.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4326142759936870351/posts/default/6876849935009269921'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4326142759936870351/posts/default/6876849935009269921'/><link rel='alternate' type='text/html' href='http://yaketyhack.blogspot.com/2011/05/perlbrew-to-rescue.html' title='Perlbrew to the rescue'/><author><name>kbenson</name><uri>http://www.blogger.com/profile/14874291376019276497</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4326142759936870351.post-3641615206529938689</id><published>2010-09-24T14:28:00.000-07:00</published><updated>2010-10-25T14:10:29.159-07:00</updated><title type='text'>Threading: Perl vs Python (revisited)</title><content type='html'>I got a &lt;a href="http://ruralperl.blogspot.com/2010/07/response-to-blog-post-threading-perl-vs.html"&gt;response&lt;/a&gt; to my threading post!  Too bad I was too busy elsewhere to monitor a respond in a timely fashion.&lt;br /&gt;&lt;br /&gt;It's a good post, and it builds on what I started to do in my post, which was to show some differences in threading through benchmarks and highlight &lt;span style="font-style: italic;"&gt;some&lt;span style="font-style: italic;"&gt; &lt;/span&gt;&lt;/span&gt;(ok, one) cases where you need to be aware of what your chosen model gets you.  Juster seems to be looking to do an actual comparison of or the models, and goes as far as finding better matching models within the languages to compare.&lt;br /&gt;&lt;br /&gt;Some of his complaints about my methodology are that the benchmarks are only there to serve a point (apparently to make Perl enthusiasts happy), that the benchmarks are CPU bound, and that my code doesn't make use of threads as it should.  This all makes sense, if you assume I was trying to compare Perl's concurrent processing to Python's, which I was not.  Apparently I wasn't clear, so I'll attempt to rectify that now.&lt;br /&gt;&lt;br /&gt;My post was meant only to illustrate the differences in truly concurrent processing through Os threads and Python's chosen threading model, which is "green" threads.  Please view my assertions and examples in that light.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Pandering to an audience&lt;/span&gt; - Apparently, JAPHs should be applauding me for bashing perennial foe Python in it's thread-hole.  Unfortunately I didn't set out to elevate Perl in any way, nor to disparage Python, just to discuss threading models and benefits and detriments for some programming needs for each.  I would think anyone with even a passing interest would see that as obvious, but apparently I was wrong.  I guess you can't have Perl and Python together in a post with different results without implying that one is better than the other, no matter your original intention or assertions that it's a special case.&lt;br /&gt;&lt;br /&gt;It's unfortunate that I chose to use Perl and Python.  I could just as easily have use Perl with the Coro module, or Python threads and Python's multiple processes, capabilities.  It would have been just as illustrative for my point.  Alas, my mind was stuck in the original context of this, which was Perl and Python, and it seemed natural for me to just use the default threading of each.  It WAS on my mind that Perl's threading isn't really true threading, but I figured that mattered little to my point.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;CPU bound&lt;/span&gt; - Another critique is that my benchmarks are CPU bound.  They are.  By design.  How else should I show that one technique doesn't help with CPU bound problems while the other does?&lt;br /&gt;&lt;br /&gt;And note this was in my post:&lt;br /&gt;&lt;blockquote&gt;Now, it's only fair to note, this isn't always the case.  We are seeing  this behavior because this is a CPU bound problem.  If the workload for  each thread were IO bound, we would probably see much better performance  from python than we are in this case.  Green threads have the ability  to much more closely and easily control the separate threads, which  yields good results when you don't actually NEED multiple CPU cores.&lt;/blockquote&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Flawed code&lt;span style="font-weight: bold;"&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; - As stated, it was specifically designed to take advantage of multiple CPU cores, not exhibit a real world problem in all it's complexity.  That said, it does map to some problems fairly well.  I do share data within the threads, but only after the main processing has been completed.  This is a common worker technique, where you spawn threads, hand them work to compute, and collect it afterwords.  Some sorting techniques may make use of this, as well as Perl 6's auto-threading.  Regardless of all that, it does illustrate some patterns don't lend themselves well to that model.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;All said and done, I think Juster really missed the point of my post.  I think he ran with what he got out of it though, and did a fairly thorough benchmarking of Perl vs. Python in more comparable terms.  I think that should be applauded, so his post isn't without merit.  I just wish he interpreted my post as what I was really trying to say.  Though, from his post I figure he understands it well enough.&lt;br /&gt;&lt;br /&gt;Edit: It &lt;span style="font-style: italic;"&gt;just&lt;/span&gt; occurred to me why Juster interpreted it the way he did.  As a boneheaded move, I left the subject from when I first started the article, and it says Perl vs. Python.  With that coloring the reading of the article, it's only fair to assume I was actually comparing them.  &lt;span style="font-style: italic;"&gt;Mea culpa&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;As a testament to just how oblivious I was to this, I also missed it when copy-pasting the subject into this new article.  Duh.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4326142759936870351-3641615206529938689?l=yaketyhack.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://yaketyhack.blogspot.com/feeds/3641615206529938689/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://yaketyhack.blogspot.com/2010/09/threading-perl-vs-python-revisited.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4326142759936870351/posts/default/3641615206529938689'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4326142759936870351/posts/default/3641615206529938689'/><link rel='alternate' type='text/html' href='http://yaketyhack.blogspot.com/2010/09/threading-perl-vs-python-revisited.html' title='Threading: Perl vs Python (revisited)'/><author><name>kbenson</name><uri>http://www.blogger.com/profile/14874291376019276497</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4326142759936870351.post-2422453617546564960</id><published>2010-07-21T12:05:00.000-07:00</published><updated>2010-07-21T13:33:24.211-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='comparison'/><category scheme='http://www.blogger.com/atom/ns#' term='Perl'/><category scheme='http://www.blogger.com/atom/ns#' term='green threads'/><category scheme='http://www.blogger.com/atom/ns#' term='coroutines'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='threading'/><title type='text'>Threading: Perl vs Python</title><content type='html'>&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Apparently some people don't believe that python's threading, while easier, is necessarily slower than a system that uses OS threads.  This illustrates a fundamental misunderstanding of how OS threads work compared to "green" threads, also called coroutines.  Python's threading model doesn't seem to quite match green threads exactly, but because of the global interpreter lock, they have a lot of the same performance problems, so I'll treat this test as an indication of the coroutine threading model's performance as well.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The problem with green threads is that they don't scale with the hardware.  Sure, they scale beautifully in the number of processes you can spawn and control, but just because it shows two concurrent processes, doesn't mean you are actually utilizing the hardware effectively.  Python's global interpreter lock ensures that two python processes can't be running at the same time.  This resolves any possible variable locking problems.  It also means, only one CPU core is being utilized, which has clear performance implications.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;To illustrate this, I've created two simple test programs, one in python, one in Perl.  They each split into a specifiable number of threads, and each thread performs some repetitive math operations 10,000,000 times and reports back how long it took to a centralized data structure.  Finally, it prints out the sum of the times the threads took to perform the task, and the time the main process actually took spawn and collect them, which is the real processing time.  Here they are:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Perl:&lt;/div&gt;&lt;blockquote&gt;&lt;div&gt;&lt;div&gt;use strict;&lt;/div&gt;&lt;div&gt;use warnings;&lt;/div&gt;&lt;div&gt;use threads;&lt;/div&gt;&lt;div&gt;use threads::shared;&lt;/div&gt;&lt;div&gt;use Time::HiRes qw/time/;&lt;/div&gt;&lt;div&gt;my @thread_times :shared;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;sub workfunc {&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt; &lt;/span&gt;my $start = time();&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt; &lt;/span&gt;print "Thread ".threads::tid()." :: started at ".localtime()."\n";&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt; &lt;/span&gt;my $init = shift;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt; &lt;/span&gt;for (1 .. 10_000_000) {&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;  &lt;/span&gt;$init += (($_%4)*($_%4)) / ($_%10+1);&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt; &lt;/span&gt;}&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt; &lt;/span&gt;my $runtime = time() - $start;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt; &lt;/span&gt;{ lock(@thread_times); push(@thread_times, $runtime); }&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt; &lt;/span&gt;print "Thread ".threads::tid()." :: stopped at ".localtime()."\n";&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt; &lt;/span&gt;return $runtime;&lt;/div&gt;&lt;div&gt;}&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;# Setup&lt;/div&gt;&lt;div&gt;my $num_threads = shift || 2;&lt;/div&gt;&lt;div&gt;print "Running with $num_threads threads\n";&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;# Main code&lt;/div&gt;&lt;div&gt;my $master_start = time();&lt;/div&gt;&lt;div&gt;my @threads = map { threads-&gt;create(\&amp;amp;workfunc, rand(10)); } 1 .. $num_threads;&lt;/div&gt;&lt;div&gt;my $thread_time = 0;&lt;/div&gt;&lt;div&gt;for my $t (@threads) {&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt; &lt;/span&gt;$thread_time += $t-&gt;join();&lt;/div&gt;&lt;div&gt;}&lt;/div&gt;&lt;div&gt;my $master_time = time() - $master_start;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;# Find and print times&lt;/div&gt;&lt;div&gt;my $lock_time = 0;&lt;/div&gt;&lt;div&gt;{ lock(@thread_times); $lock_time += $_ for @thread_times; }&lt;/div&gt;&lt;div&gt;print "Master logged $master_time runtime\n";&lt;/div&gt;&lt;div&gt;print "Threads reported $lock_time combined runtime\n";&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Python:&lt;/div&gt;&lt;blockquote&gt;&lt;div&gt;import math&lt;/div&gt;&lt;div&gt;&lt;div&gt;import random&lt;/div&gt;&lt;div&gt;import time&lt;/div&gt;&lt;div&gt;import sys&lt;/div&gt;&lt;div&gt;import threading&lt;/div&gt;&lt;div&gt;thread_times = []&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;def workfunc(id,init):&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt; &lt;/span&gt;l = threading.local()&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt; &lt;/span&gt;l.start = time.clock()&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt; &lt;/span&gt;print "Thread "+str(id)+" :: started at "+time.asctime()&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt; &lt;/span&gt;for i in xrange (1, 10000000):&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;  &lt;/span&gt;init += ((i%4)*(i%4)) / (i%10+1)&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt; &lt;/span&gt;l.runtime = time.clock() - l.start&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt; &lt;/span&gt;thread_times.append(l.runtime)&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt; &lt;/span&gt;print "Thread "+str(id)+" :: stopped at "+time.asctime()&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt; &lt;/span&gt;return l.runtime&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;# Setup&lt;/div&gt;&lt;div&gt;num_threads = 2&lt;/div&gt;&lt;div&gt;if len(sys.argv)&gt;1:&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt; &lt;/span&gt;num_threads = int(sys.argv[1])&lt;/div&gt;&lt;div&gt;print "Running with "+str(num_threads)+" threads"&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;# Main code&lt;/div&gt;&lt;div&gt;master_start = time.clock()&lt;/div&gt;&lt;div&gt;threads = []&lt;/div&gt;&lt;div&gt;for i in xrange (1, num_threads+1):&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt; &lt;/span&gt;t = threading.Thread(target=workfunc,args=(i,random.random()*10))&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt; &lt;/span&gt;t.start()&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt; &lt;/span&gt;threads.append(t)&lt;/div&gt;&lt;div&gt;for t in threads:&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt; &lt;/span&gt;t.join()&lt;/div&gt;&lt;div&gt;master_time = time.clock() - master_start&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;# Find and print times&lt;/div&gt;&lt;div&gt;thread_time = 0&lt;/div&gt;&lt;div&gt;for t in thread_times:&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt; &lt;/span&gt;thread_time += t&lt;/div&gt;&lt;div&gt;print "Master logged "+str(master_time)+" runtime";&lt;/div&gt;&lt;div&gt;print "Threads reported "+str(thread_time)+" combined runtime";&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;The results are very telling, and here they are:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Perl:&lt;/div&gt;&lt;div&gt;&lt;blockquote&gt;&lt;div&gt;$ perl thread.pl 1&lt;/div&gt;&lt;div&gt;Running with 1 threads&lt;/div&gt;&lt;div&gt;Thread 1 :: started at Wed Jul 21 12:47:26 2010&lt;/div&gt;&lt;div&gt;Thread 1 :: stopped at Wed Jul 21 12:47:31 2010&lt;/div&gt;&lt;div&gt;Master logged 5.21124505996704 runtime&lt;/div&gt;&lt;div&gt;Threads reported 5.20653009414673 combined runtime&lt;/div&gt;&lt;div&gt;$ perl thread.pl 2&lt;/div&gt;&lt;div&gt;Running with 2 threads&lt;/div&gt;&lt;div&gt;Thread 1 :: started at Wed Jul 21 12:47:36 2010&lt;/div&gt;&lt;div&gt;Thread 2 :: started at Wed Jul 21 12:47:36 2010&lt;/div&gt;&lt;div&gt;Thread 2 :: stopped at Wed Jul 21 12:47:42 2010&lt;/div&gt;&lt;div&gt;Thread 1 :: stopped at Wed Jul 21 12:47:42 2010&lt;/div&gt;&lt;div&gt;Master logged 5.42760705947876 runtime&lt;/div&gt;&lt;div&gt;Threads reported 10.6412711143494 combined runtime&lt;/div&gt;&lt;div&gt;$ perl thread.pl 3&lt;/div&gt;&lt;div&gt;Running with 3 threads&lt;/div&gt;&lt;div&gt;Thread 1 :: started at Wed Jul 21 12:47:47 2010&lt;/div&gt;&lt;div&gt;Thread 2 :: started at Wed Jul 21 12:47:47 2010&lt;/div&gt;&lt;div&gt;Thread 3 :: started at Wed Jul 21 12:47:47 2010&lt;/div&gt;&lt;div&gt;Thread 2 :: stopped at Wed Jul 21 12:47:54 2010&lt;/div&gt;&lt;div&gt;Thread 1 :: stopped at Wed Jul 21 12:47:54 2010&lt;/div&gt;&lt;div&gt;Thread 3 :: stopped at Wed Jul 21 12:47:56 2010&lt;/div&gt;&lt;div&gt;Master logged 8.67003512382507 runtime&lt;/div&gt;&lt;div&gt;Threads reported 22.7089991569519 combined runtime&lt;/div&gt;&lt;div&gt;$ perl thread.pl 4&lt;/div&gt;&lt;div&gt;Running with 4 threads&lt;/div&gt;&lt;div&gt;Thread 1 :: started at Wed Jul 21 12:47:59 2010&lt;/div&gt;&lt;div&gt;Thread 2 :: started at Wed Jul 21 12:47:59 2010&lt;/div&gt;&lt;div&gt;Thread 3 :: started at Wed Jul 21 12:47:59 2010&lt;/div&gt;&lt;div&gt;Thread 4 :: started at Wed Jul 21 12:47:59 2010&lt;/div&gt;&lt;div&gt;Thread 1 :: stopped at Wed Jul 21 12:48:09 2010&lt;/div&gt;&lt;div&gt;Thread 3 :: stopped at Wed Jul 21 12:48:09 2010&lt;/div&gt;&lt;div&gt;Thread 2 :: stopped at Wed Jul 21 12:48:10 2010&lt;/div&gt;&lt;div&gt;Thread 4 :: stopped at Wed Jul 21 12:48:10 2010&lt;/div&gt;&lt;div&gt;Master logged 10.9765570163727 runtime&lt;/div&gt;&lt;div&gt;Threads reported 43.3241600990295 combined runtime&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;Note that a single thread takes approximately 5.2 seconds.  Two threads takes only a few fractions of a second more.  That's because each thread ran on it's own core.  The threads report they had a combined run time of 10+ seconds, as each had a 5+ second run time, but it was *in parallel*, so the real processing time was ~5.2 seconds.  My system is a dual core system, so we finally see some slowdown at three threads, where it takes 50% longer.  Four threads takes twice as long as two threads, as expected.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt; Now let's look at Python:&lt;/div&gt;&lt;blockquote&gt;&lt;div&gt;&lt;div&gt;$ python thread.py 1&lt;/div&gt;&lt;div&gt;Running with 1 threads&lt;/div&gt;&lt;div&gt;Thread 1 :: started at Wed Jul 21 12:53:03 2010&lt;/div&gt;&lt;div&gt;Thread 1 :: stopped at Wed Jul 21 12:53:08 2010&lt;/div&gt;&lt;div&gt;Master logged 5.25 runtime&lt;/div&gt;&lt;div&gt;Threads reported 5.25 combined runtime&lt;/div&gt;&lt;div&gt;$ python thread.py 2&lt;/div&gt;&lt;div&gt;Running with 2 threads&lt;/div&gt;&lt;div&gt;Thread 1 :: started at Wed Jul 21 12:53:10 2010&lt;/div&gt;&lt;div&gt;Thread 2 :: started at Wed Jul 21 12:53:10 2010&lt;/div&gt;&lt;div&gt;Thread 2 :: stopped at Wed Jul 21 12:53:22 2010&lt;/div&gt;&lt;div&gt;Thread 1 :: stopped at Wed Jul 21 12:53:22 2010&lt;/div&gt;&lt;div&gt;Master logged 14.66 runtime&lt;/div&gt;&lt;div&gt;Threads reported 29.09 combined runtime&lt;/div&gt;&lt;div&gt;$ python thread.py 3&lt;/div&gt;&lt;div&gt;Running with 3 threads&lt;/div&gt;&lt;div&gt;Thread 1 :: started at Wed Jul 21 12:53:25 2010&lt;/div&gt;&lt;div&gt;Thread 2 :: started at Wed Jul 21 12:53:25 2010&lt;/div&gt;&lt;div&gt;Thread 3 :: started at Wed Jul 21 12:53:25 2010&lt;/div&gt;&lt;div&gt;Thread 3 :: stopped at Wed Jul 21 12:53:43 2010&lt;/div&gt;&lt;div&gt;Thread 1 :: stopped at Wed Jul 21 12:53:44 2010&lt;/div&gt;&lt;div&gt;Thread 2 :: stopped at Wed Jul 21 12:53:44 2010&lt;/div&gt;&lt;div&gt;Master logged 22.48 runtime&lt;/div&gt;&lt;div&gt;Threads reported 66.26 combined runtime&lt;/div&gt;&lt;div&gt;$ python thread.py 4&lt;/div&gt;&lt;div&gt;Running with 4 threads&lt;/div&gt;&lt;div&gt;Thread 1 :: started at Wed Jul 21 12:53:46 2010&lt;/div&gt;&lt;div&gt;Thread 2 :: started at Wed Jul 21 12:53:46 2010&lt;/div&gt;&lt;div&gt;Thread 3 :: started at Wed Jul 21 12:53:46 2010&lt;/div&gt;&lt;div&gt;Thread 4 :: started at Wed Jul 21 12:53:46 2010&lt;/div&gt;&lt;div&gt;Thread 2 :: stopped at Wed Jul 21 12:54:12 2010&lt;/div&gt;&lt;div&gt;Thread 1 :: stopped at Wed Jul 21 12:54:12 2010&lt;/div&gt;&lt;div&gt;Thread 3 :: stopped at Wed Jul 21 12:54:13 2010&lt;/div&gt;&lt;div&gt;Thread 4 :: stopped at Wed Jul 21 12:54:13 2010&lt;/div&gt;&lt;div&gt;Master logged 30.16 runtime&lt;/div&gt;&lt;div&gt;Threads reported 120.4 combined runtime&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;Here we see where the global interpreter lock causing us problems.  Each additional thread causes it to take the same amount more time.  That's because only a single thread can run at any time, yielding no time savings for running four threads.  It's equivalent to running them in sequence, in this case.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Now, it's only fair to note, this isn't always the case.  We are seeing this behavior because this is a CPU bound problem.  If the workload for each thread were IO bound, we would probably see much better performance from python than we are in this case.  Green threads have the ability to much more closely and easily control the separate threads, which yields good results when you don't actually NEED multiple CPU cores.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In summary, Green threads, coroutines, and python's implementation of threads have clear disadvantages under certain workloads, to the point of having negligible to no performance benefits in some cases.  If you are using a language that only offers one of these methods of threading and you have a process model that lends itself well to threading, I feel for you.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Update: I figured I should post the python and perl versions before I'm asked/accused.&lt;/div&gt;&lt;div&gt;&lt;div&gt;$ perl -v&lt;/div&gt;&lt;div&gt;This is perl, v5.10.1 (*) built for x86_64-linux-thread-multi&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;$ python -V&lt;/div&gt;&lt;div&gt;Python 2.6.2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Both are the standard RPMs included with the RHEL6 beta 2 install.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;$ rpm -q perl&lt;/div&gt;&lt;div&gt;perl-5.10.1-109.el6.x86_64&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;$ rpm -q python&lt;/div&gt;&lt;div&gt;python-2.6.2-7.el6.x86_64&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4326142759936870351-2422453617546564960?l=yaketyhack.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://yaketyhack.blogspot.com/feeds/2422453617546564960/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://yaketyhack.blogspot.com/2010/07/threading-perl-vs-python.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4326142759936870351/posts/default/2422453617546564960'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4326142759936870351/posts/default/2422453617546564960'/><link rel='alternate' type='text/html' href='http://yaketyhack.blogspot.com/2010/07/threading-perl-vs-python.html' title='Threading: Perl vs Python'/><author><name>kbenson</name><uri>http://www.blogger.com/profile/14874291376019276497</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4326142759936870351.post-1916781849880898677</id><published>2010-07-19T17:37:00.000-07:00</published><updated>2010-07-19T17:44:03.196-07:00</updated><title type='text'>Perl promotion</title><content type='html'>Why is making money from a community a bad thing?&lt;br /&gt;&lt;br /&gt;Gabor's been taking a lot of &lt;a href="http://blogs.perl.org/users/gabor_szabo/2010/07/promoting-perl-is-fun.html"&gt;criticism&lt;/a&gt; for attempting to promote Perl, and proposing it so he gets paid for it.  Apparently, many people seem to be of the opinion that getting paid to do community work is trying to pull a fast one over on the community.&lt;br /&gt;&lt;br /&gt;It's interesting to see the backlash from people when  they think someone is going to somehow profit from a community they are  part of.  I see nothing fundamentally opposing someone making a profit  from the Perl community (not that I think that's Gabor's goal).  The  only metric that matters here should be whether the gains outweigh the  costs. &lt;br /&gt;&lt;br /&gt;Personally, I don't care if Gabor is paid $1, $10,000, or $1,000,000  dollars.  If the gains match or exceed the cost, it was worth it.   Quantification can be hard to determine, but I find it hard to believe  we can't agree that the attempt is worthwhile, and we can determine  whether it was successful in any part at a later date.&lt;br /&gt;&lt;br /&gt;As a thought exercise, let's examine this from the opposite direction.   Assume the community or a part thereof has the option to pay $1,000,000 (which let's  assume it has) to an individual which guarantees Perl to become the #1  most popular programming language within the next year.  Is it worth it?   Is it even desirable?  If the answer to those is yes, you  should be in fundamental agreement with Gabor's proposal, and only the  implementation details are to be worked out.  And if that's the case, if  not Gabor, then who else?&lt;div class="comment-content"&gt;&lt;br /&gt;        &lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4326142759936870351-1916781849880898677?l=yaketyhack.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://yaketyhack.blogspot.com/feeds/1916781849880898677/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://yaketyhack.blogspot.com/2010/07/perl-promotion.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4326142759936870351/posts/default/1916781849880898677'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4326142759936870351/posts/default/1916781849880898677'/><link rel='alternate' type='text/html' href='http://yaketyhack.blogspot.com/2010/07/perl-promotion.html' title='Perl promotion'/><author><name>kbenson</name><uri>http://www.blogger.com/profile/14874291376019276497</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
