<?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-7867050353982208310</id><updated>2012-02-16T16:37:49.331-08:00</updated><category term='logging'/><category term='objc'/><category term='introductions'/><category term='iphone'/><category term='xcode'/><category term='timeit'/><category term='welcome'/><category term='documentation'/><category term='python'/><category term='ucsb'/><category term='cogi'/><category term='trace'/><category term='publicapi'/><category term='performance'/><category term='algorithms'/><category term='django'/><title type='text'>Cogi Engineering</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://cogidev.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7867050353982208310/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://cogidev.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>admin</name><uri>http://www.blogger.com/profile/02691217519757101106</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>5</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-7867050353982208310.post-3898588783014500020</id><published>2010-02-16T09:59:00.000-08:00</published><updated>2010-02-16T10:02:32.011-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='documentation'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='publicapi'/><title type='text'>Building better API documentation</title><content type='html'>A while back, over on the &lt;a href="http://blog.cogi.com/"&gt;Cogi blog&lt;/a&gt;, James announced the  &lt;a href="http://blog.cogi.com/2009/12/01/cogi-announces-the-public-beta-of-its-new-api/"&gt;public beta of our new public remote API&lt;/a&gt;. Designing and implementing this API has been a major focus for our team over the last several months. We've taken our several private interfaces, and refactored and reimplemented them into a single interface that we want to expose to our customers and partners to allow them to develop applications that take advantage of the rich backend systems we've built over the past couple of years.&lt;br /&gt;&lt;br /&gt;Now, one of the things with releasing a remote API with consumers beyond your own applications is that the API itself becomes a kind of contract between your service and the new consumers -- a contract that you must both &lt;i&gt;present&lt;/i&gt; and &lt;i&gt;maintain&lt;/i&gt;. In this post, I'd like to talk about the primary mechanism through which we present our API -- the API documentation.&lt;br /&gt;&lt;br /&gt;There is a whole laundry list of things that can go wrong with any API documentation -- for starters, let's consider just this small (and incomplete) list of problems:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Stale documentation&lt;/li&gt;&lt;li&gt;Missing documentation&lt;/li&gt;&lt;li&gt;Documentation that's difficult to interpret&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;An interesting take-away from this is that both of the first two points are related to the documentation not matching the interface that's actually deployed on the server -- that is, if we had a way of connecting that documentation to the service itself, those problems would be less likely to become a problem. So, we decided to introduce the concept of an API "demo" &lt;span style="font-style: italic;"&gt;as our documentation&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;So, rather than merely describing the interface we provide and expect in some separately maintained documentation, our API documentation actually shows you what to do -- how the call is formed, and what the response looks like. What's more, in the process, it actually demonstrates that the API is actually working as described, because the demo actually makes a call into the API itself! In fact, the API documentation is essentially an automatically generated &lt;i&gt;client &lt;/i&gt;of the API itself!&lt;br /&gt;&lt;br /&gt;Whats more, since the API documentation is dynamically generated at runtime, we never have to worry about it being out of date, since what is displayed in the documentation exactly matches the code the API server is running from -- that includes the method interface, method documentation, and response API.&lt;br /&gt;&lt;br /&gt;Want to try it out? Contact us at &lt;a href="mailto:api@cogi.com"&gt;api@cogi.com&lt;/a&gt; to get an API key and start developing against the Cogi API! Have other thoughts? Leave them in the comments below!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7867050353982208310-3898588783014500020?l=cogidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cogidev.blogspot.com/feeds/3898588783014500020/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cogidev.blogspot.com/2010/02/building-better-api-documentation.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7867050353982208310/posts/default/3898588783014500020'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7867050353982208310/posts/default/3898588783014500020'/><link rel='alternate' type='text/html' href='http://cogidev.blogspot.com/2010/02/building-better-api-documentation.html' title='Building better API documentation'/><author><name>Chris</name><uri>http://www.blogger.com/profile/05755072772135183636</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-7867050353982208310.post-8778282587156206146</id><published>2010-02-04T14:43:00.000-08:00</published><updated>2010-02-04T14:49:39.795-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='algorithms'/><category scheme='http://www.blogger.com/atom/ns#' term='trace'/><category scheme='http://www.blogger.com/atom/ns#' term='ucsb'/><category scheme='http://www.blogger.com/atom/ns#' term='logging'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='timeit'/><title type='text'>Python Tracing Performance</title><content type='html'>Looking back at &lt;a href="http://cogidev.blogspot.com/2009/11/introducing-me.html"&gt;my introductory post&lt;/a&gt;, I realize I didn't mention that I am currently working on my &lt;a href="http://www.cs.ucsb.edu/"&gt;M.S. degree in Computer Science at UCSB&lt;/a&gt;. Occasionally, my academic work leads me to discover something&amp;nbsp;immensely&amp;nbsp;applicable to my industrial work -- I recently encountered one such case that was fairly game changing in terms of how I intend to use Python for large processing tasks.&lt;br /&gt;&lt;br /&gt;The details of the particular assignment I was working on aren't&amp;nbsp;pertinent&amp;nbsp;to this discussion. Suffice it to say that I needed to perform some fairly complex processing on a fairly large graph of nodes. For the purposes of this discussion, I was particularly interested in some of the relationships between many of the nodes.&lt;br /&gt;&lt;br /&gt;Once the majority of the code was written and tested, I attempted to run it on the large dataset, and found that while progress was being made, it was being made very slowly, taking upwards of 10 minutes to complete one particular portion of the processing. Naturally, I dug back into the relevant code, searching for where I had allowed some &lt;a href="http://en.wikipedia.org/wiki/Big_O_notation"&gt;O(N^2)&lt;/a&gt; code to be introduced into what was supposed to be a O(N log N) algorithm. Much to my dismay, I couldn't find any area where I had allowed such an obvious mistake to enter my code&amp;nbsp;(well, partial dismay -- I was actually glad I didn't screw it up).&lt;br /&gt;&lt;br /&gt;Once I was satisfied that there were no major algorithmic mistakes, I took steps to begin&amp;nbsp;optimizing&amp;nbsp;out any of the constant-time operations that could possibly be removed. I pushed many of the operations into other areas of the code where I was already manipulating the object in question, improved some of the layout to avoid re-examining the same object, etc. However, none of these optimizations resulted in any worthwhile performance gains.&lt;br /&gt;&lt;br /&gt;Up until now, I hadn't given my trace routines much thought, as I was simply using them to help me debug the problems in question, but even with the trace disabled I was still finding the code extremely slow to run... then it hit me -- even when my trace function was "disabled", I still had to call the function in question. So on a whim I commented out all of the calls to my trace function, and ran the algorithm again... and before I could lean back in my chair to wait, the results popped up on my screen. I was shocked -- from 10 minutes to less than a second, just by removing my "disabled" trace lines.&lt;br /&gt;&lt;br /&gt;Now, I've been writing Python code for a while -- I'm fairly consistent in sticking to the &lt;a href="http://wiki.python.org/moin/PythonSpeed/PerformanceTips"&gt;performance patterns mentioned in the Python wiki&lt;/a&gt;&amp;nbsp;and &lt;a href="http://www.skymind.com/%7Eocrow/python_string/"&gt;working with strings efficiently&lt;/a&gt;&amp;nbsp;but I was amazed by how much this one little change affected my program.&lt;br /&gt;&lt;br /&gt;As I thought about the problem in particular, I realized it made sense: function calls in Python are fairly expensive, so even if you're only doing O(N) of them, if N is large enough, that still adds up to a lot of function calls. So, I decided to develop a test module to demonstrate the various simple tracing routines I could quickly write down. Here's the code for the testing module:&lt;br /&gt;&lt;br /&gt;&lt;style type="text/css"&gt;pre.code {    font-style: Lucida,"Courier New";    padding-left: 20px;}.number {    color: #0080C0;}.operator {    color: #000000;}.string {    color: #008000;}.comment {    color: #808080;}.name {    color: #000000;}.error {    color: #FF8080;    border: solid 1.5pt #FF0000;}.keyword {    color: #0000FF;    font-weight: bold;}.text {    color: #000000;}&lt;/style&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span class="string"&gt;'''&lt;br /&gt;Demonstrate the performance impacts of various log/tracing techniques.&lt;br /&gt;&lt;br /&gt;The tests in this module do not test the performance of various logging&lt;br /&gt;methods while logging is enabled, as we likely don't mind the overhead &lt;br /&gt;of checking to see whether the statement is enabled or not when we're &lt;br /&gt;actually writing to sys.stdout or a file (the I/O overhead is ususally &lt;br /&gt;going to be much larger than even a few function calls).&lt;br /&gt;&lt;br /&gt;Instead, these tests focus on the running code while it contains &lt;br /&gt;*disabled* log statements to see how that unused code is ultimately&lt;br /&gt;affecting program performance.&lt;br /&gt;'''&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;from&lt;/span&gt; &lt;span class="name"&gt;__future__&lt;/span&gt; &lt;span class="keyword"&gt;import&lt;/span&gt; &lt;span class="name"&gt;print_function&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;from&lt;/span&gt; &lt;span class="name"&gt;logging&lt;/span&gt; &lt;span class="keyword"&gt;import&lt;/span&gt; &lt;span class="name"&gt;getLogger&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;import&lt;/span&gt; &lt;span class="name"&gt;timeit&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="name"&gt;TRACE_MESSAGE_ARGS&lt;/span&gt; &lt;span class="operator"&gt;=&lt;/span&gt; &lt;span class="name"&gt;tuple&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;range&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="number"&gt;5&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class="name"&gt;TRACE_MESSAGE_FORMAT&lt;/span&gt; &lt;span class="operator"&gt;=&lt;/span&gt; &lt;span class="string"&gt;'Traced message: %s'&lt;/span&gt; &lt;span class="operator"&gt;%&lt;/span&gt; &lt;span class="string"&gt;', '&lt;/span&gt;&lt;span class="operator"&gt;.&lt;/span&gt;&lt;span class="name"&gt;join&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="string"&gt;'%s'&lt;/span&gt; &lt;span class="keyword"&gt;for&lt;/span&gt; &lt;span class="name"&gt;_i&lt;/span&gt; &lt;span class="keyword"&gt;in&lt;/span&gt; &lt;span class="name"&gt;TRACE_MESSAGE_ARGS&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="name"&gt;method_0&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class="string"&gt;'''Provide a baseline that simply comments out unused trace.'''&lt;/span&gt;&lt;br /&gt;&lt;span class="comment"&gt;#    print TRACE_MESSAGE_FORMAT % TRACE_MESSAGE_ARGS&lt;br /&gt;&lt;/span&gt;    &lt;span class="keyword"&gt;pass&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="name"&gt;METHOD_1_ENABLED_LEVELS&lt;/span&gt; &lt;span class="operator"&gt;=&lt;/span&gt; &lt;span class="name"&gt;set&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="operator"&gt;[&lt;/span&gt;&lt;br /&gt;    &lt;span class="string"&gt;'FOO'&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class="string"&gt;'BAR'&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class="operator"&gt;]&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="name"&gt;method_1a&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class="string"&gt;'''Use a trace method that checks for an enabled level.&lt;br /&gt;    &lt;br /&gt;    This is the typical simple trace mechanism. Using a set() for the&lt;br /&gt;    level definition helps avoid unnecessary list scans, but two things&lt;br /&gt;    are wrong with this approach:&lt;br /&gt;        &lt;br /&gt;        1. The string interpolation must be performed every iteration.&lt;br /&gt;        2. We have to call the method_1_trace function every iteration. &lt;br /&gt;    '''&lt;/span&gt;&lt;br /&gt;    &lt;span class="name"&gt;method_1a_trace&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="string"&gt;'NOT_ENABLED'&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt; &lt;span class="name"&gt;TRACE_MESSAGE_FORMAT&lt;/span&gt; &lt;span class="operator"&gt;%&lt;/span&gt; &lt;span class="name"&gt;TRACE_MESSAGE_ARGS&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="name"&gt;method_1a_trace&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;level&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt; &lt;span class="name"&gt;message&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="name"&gt;level&lt;/span&gt; &lt;span class="keyword"&gt;in&lt;/span&gt; &lt;span class="name"&gt;METHOD_1_ENABLED_LEVELS&lt;/span&gt;&lt;span class="operator"&gt;:&lt;/span&gt;&lt;br /&gt;        &lt;span class="keyword"&gt;print&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;message&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="name"&gt;method_1b&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class="string"&gt;'''Similar to method 1a, but don't force the string interpolation every iteration.'''&lt;/span&gt;&lt;br /&gt;    &lt;span class="name"&gt;method_1b_trace&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="string"&gt;'NOT_ENABLED'&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt; &lt;span class="name"&gt;TRACE_MESSAGE_FORMAT&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt; &lt;span class="operator"&gt;*&lt;/span&gt;&lt;span class="name"&gt;TRACE_MESSAGE_ARGS&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="name"&gt;method_1b_trace&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;level&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt; &lt;span class="name"&gt;message&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt; &lt;span class="operator"&gt;*&lt;/span&gt;&lt;span class="name"&gt;formatArgs&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="name"&gt;level&lt;/span&gt; &lt;span class="keyword"&gt;in&lt;/span&gt; &lt;span class="name"&gt;METHOD_1_ENABLED_LEVELS&lt;/span&gt;&lt;span class="operator"&gt;:&lt;/span&gt;&lt;br /&gt;        &lt;span class="keyword"&gt;print&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;message&lt;/span&gt; &lt;span class="operator"&gt;%&lt;/span&gt; &lt;span class="name"&gt;formatArgs&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="name"&gt;method_1c&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class="string"&gt;'''Similar to method 1a, but don't force the function call every iteration.'''&lt;/span&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="string"&gt;'NOT_ENABLED'&lt;/span&gt; &lt;span class="keyword"&gt;in&lt;/span&gt; &lt;span class="name"&gt;METHOD_1_ENABLED_LEVELS&lt;/span&gt;&lt;span class="operator"&gt;:&lt;/span&gt;&lt;br /&gt;        &lt;span class="keyword"&gt;print&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;TRACE_MESSAGE_FORMAT&lt;/span&gt; &lt;span class="operator"&gt;%&lt;/span&gt; &lt;span class="name"&gt;TRACE_MESSAGE_ARGS&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="name"&gt;METHOD_2_LEVEL&lt;/span&gt; &lt;span class="operator"&gt;=&lt;/span&gt; &lt;span class="name"&gt;False&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="name"&gt;method_2&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class="string"&gt;'''Use the trace level as a boolean guard.&lt;br /&gt;    &lt;br /&gt;    The concept here is to avoid executing a function call, or a string &lt;br /&gt;    interpolation step at all in the case where the level is disabled.&lt;br /&gt;    &lt;br /&gt;    This appears to be the fastest mechanism available short of actually&lt;br /&gt;    removing the trace code from the program entirely (via a comment or &lt;br /&gt;    otherwise).&lt;br /&gt;    '''&lt;/span&gt;&lt;br /&gt;    &lt;span class="name"&gt;METHOD_2_LEVEL&lt;/span&gt; &lt;span class="keyword"&gt;and&lt;/span&gt; &lt;span class="keyword"&gt;print&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;TRACE_MESSAGE_FORMAT&lt;/span&gt; &lt;span class="operator"&gt;%&lt;/span&gt; &lt;span class="name"&gt;TRACE_MESSAGE_ARGS&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="name"&gt;METHOD_2_LEVEL_FOO&lt;/span&gt; &lt;span class="operator"&gt;=&lt;/span&gt; &lt;span class="string"&gt;'foo'&lt;/span&gt;&lt;br /&gt;&lt;span class="name"&gt;METHOD_2_LEVEL_BAR&lt;/span&gt; &lt;span class="operator"&gt;=&lt;/span&gt; &lt;span class="string"&gt;'bar'&lt;/span&gt;&lt;br /&gt;&lt;span class="name"&gt;METHOD_2_LEVEL_DISABLED&lt;/span&gt; &lt;span class="operator"&gt;=&lt;/span&gt; &lt;span class="string"&gt;'disabled'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="name"&gt;METHOD_2_ENABLED_LEVELS&lt;/span&gt; &lt;span class="operator"&gt;=&lt;/span&gt; &lt;span class="name"&gt;set&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="operator"&gt;[&lt;/span&gt;&lt;br /&gt;    &lt;span class="name"&gt;METHOD_2_LEVEL_FOO&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class="name"&gt;METHOD_2_LEVEL_BAR&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class="operator"&gt;]&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="name"&gt;method_2b&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class="string"&gt;'''Mix of method_2 with enabled levels.'''&lt;/span&gt;&lt;br /&gt;    &lt;span class="name"&gt;METHOD_2_LEVEL_DISABLED&lt;/span&gt; &lt;span class="keyword"&gt;in&lt;/span&gt; &lt;span class="name"&gt;METHOD_2_ENABLED_LEVELS&lt;/span&gt; &lt;span class="keyword"&gt;and&lt;/span&gt; &lt;span class="keyword"&gt;print&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;TRACE_MESSAGE_FORMAT&lt;/span&gt; &lt;span class="operator"&gt;%&lt;/span&gt; &lt;span class="name"&gt;TRACE_MESSAGE_ARGS&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="name"&gt;method_3_logger&lt;/span&gt; &lt;span class="operator"&gt;=&lt;/span&gt; &lt;span class="name"&gt;getLogger&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;__name__&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class="name"&gt;method_3_trace&lt;/span&gt; &lt;span class="operator"&gt;=&lt;/span&gt; &lt;span class="name"&gt;method_3_logger&lt;/span&gt;&lt;span class="operator"&gt;.&lt;/span&gt;&lt;span class="name"&gt;debug&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="name"&gt;method_3&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class="string"&gt;'''Use the built-in logging mechanism.&lt;br /&gt;    &lt;br /&gt;    This was included somewhat as an afterthought, since we've already&lt;br /&gt;    shown that function calls are the major overhead to avoid. &lt;br /&gt;    &lt;br /&gt;    It turns out this is by far the worst performing option available!&lt;br /&gt;    '''&lt;/span&gt;&lt;br /&gt;    &lt;span class="name"&gt;method_3_trace&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;TRACE_MESSAGE_FORMAT&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt; &lt;span class="operator"&gt;*&lt;/span&gt;&lt;span class="name"&gt;TRACE_MESSAGE_ARGS&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="name"&gt;main&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;print&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="string"&gt;'Method 0: '&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt; &lt;span class="name"&gt;min&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;timeit&lt;/span&gt;&lt;span class="operator"&gt;.&lt;/span&gt;&lt;span class="name"&gt;Timer&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;method_0&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;.&lt;/span&gt;&lt;span class="name"&gt;repeat&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;print&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="string"&gt;'Method 1a: '&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt; &lt;span class="name"&gt;min&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;timeit&lt;/span&gt;&lt;span class="operator"&gt;.&lt;/span&gt;&lt;span class="name"&gt;Timer&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;method_1a&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;.&lt;/span&gt;&lt;span class="name"&gt;repeat&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;print&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="string"&gt;'Method 1b: '&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt; &lt;span class="name"&gt;min&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;timeit&lt;/span&gt;&lt;span class="operator"&gt;.&lt;/span&gt;&lt;span class="name"&gt;Timer&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;method_1b&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;.&lt;/span&gt;&lt;span class="name"&gt;repeat&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;print&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="string"&gt;'Method 1c: '&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt; &lt;span class="name"&gt;min&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;timeit&lt;/span&gt;&lt;span class="operator"&gt;.&lt;/span&gt;&lt;span class="name"&gt;Timer&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;method_1c&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;.&lt;/span&gt;&lt;span class="name"&gt;repeat&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;print&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="string"&gt;'Method 2: '&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt; &lt;span class="name"&gt;min&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;timeit&lt;/span&gt;&lt;span class="operator"&gt;.&lt;/span&gt;&lt;span class="name"&gt;Timer&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;method_2&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;.&lt;/span&gt;&lt;span class="name"&gt;repeat&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;print&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="string"&gt;'Method 2b: '&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt; &lt;span class="name"&gt;min&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;timeit&lt;/span&gt;&lt;span class="operator"&gt;.&lt;/span&gt;&lt;span class="name"&gt;Timer&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;method_2b&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;.&lt;/span&gt;&lt;span class="name"&gt;repeat&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class="keyword"&gt;print&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="string"&gt;'Method 3: '&lt;/span&gt;&lt;span class="operator"&gt;,&lt;/span&gt; &lt;span class="name"&gt;min&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;timeit&lt;/span&gt;&lt;span class="operator"&gt;.&lt;/span&gt;&lt;span class="name"&gt;Timer&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="name"&gt;method_3&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;.&lt;/span&gt;&lt;span class="name"&gt;repeat&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="name"&gt;__name__&lt;/span&gt; &lt;span class="operator"&gt;==&lt;/span&gt; &lt;span class="string"&gt;'__main__'&lt;/span&gt;&lt;span class="operator"&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class="name"&gt;main&lt;/span&gt;&lt;span class="operator"&gt;(&lt;/span&gt;&lt;span class="operator"&gt;)&lt;/span&gt;&lt;span class="text"&gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;On my machine here at work, the output of running that module looks like this:&lt;br /&gt;&lt;pre class="code"&gt;Method 0:  0.203303173335&lt;br /&gt;Method 1a:  1.00159193476&lt;br /&gt;Method 1b:  0.559357103269&lt;br /&gt;Method 1c:  0.222934412164&lt;br /&gt;Method 2:  0.193546388001&lt;br /&gt;Method 2b:  0.244648784973&lt;br /&gt;Method 3:  5.09330805922&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Method 0 provides a simple baseline for the test -- the fastest run of the test with no code takes .2 seconds. Given that we're running the test method a million times (the default for the timeit.Timer.repeat method), that seems reasonable for Python.&lt;br /&gt;&lt;br /&gt;The Method 1{a,b,c} results represent variations on a simple trace(level, message) metaphor that I've frequently used to do simple tracing. As you can see, dropping the string interpolation from 1a in test 1b cuts the time nearly in half, and dropping the function call in test 1c drops it nearly in half again. The point to take away from this is this: a trace function really can't afford to do &lt;b&gt;any&lt;/b&gt;&amp;nbsp;work while it's disabled!&lt;br /&gt;&lt;br /&gt;Method 2 turns out to be my preferred choice for simple tracing now -- just guard a print statement with a boolean condition that's either enabled or disabled. Note, it's important that the object be simple to evaluate in a boolean context (e.g, True, False, 0, 1, etc) -- it wouldn't do any good if we had to check a complicated logic function to see if we were supposed to get past the guard. Method 2b is a simple variation on this theme that tries to combine the efficiency of method 2 with the convenience of method 1*. However, the slight degradation in performance doesn't seem worth it to me.&lt;br /&gt;&lt;br /&gt;Astoundingly, using &lt;a href="http://docs.python.org/library/logging.html"&gt;Python's provided logging module&lt;/a&gt; provided the worst performance by &lt;b&gt;far&lt;/b&gt;! But, when you think about it, the logging module in concept is similar to the 1a method, but has to do a good deal more work to check it's guard condition. And in fact, judging from these results, it looks like it's about 5 times as much work!&lt;br /&gt;&lt;br /&gt;There seem to be a couple of lessons we could take away from this brief analysis:&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;If possible, be sure not to call any functions when checking a guard condition for a trace method -- function calls in Python are too expensive to be executing just to prevent printing. (Note that this includes any dot-operator accesses, as expressions like "a.b" are essentially just method calls like "a.__getattr__(b)", with some additional magic applied.&lt;/li&gt;&lt;li&gt;If you must use something like the logging module or a level-checking function for trace, only include trace where absolutely necessary, and never inside frequently executed loops. If you need the trace there for debugging, be sure to comment it out when you've finished your investigation.&lt;/li&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7867050353982208310-8778282587156206146?l=cogidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cogidev.blogspot.com/feeds/8778282587156206146/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cogidev.blogspot.com/2010/02/python-tracing-performance.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7867050353982208310/posts/default/8778282587156206146'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7867050353982208310/posts/default/8778282587156206146'/><link rel='alternate' type='text/html' href='http://cogidev.blogspot.com/2010/02/python-tracing-performance.html' title='Python Tracing Performance'/><author><name>Chris</name><uri>http://www.blogger.com/profile/05755072772135183636</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-7867050353982208310.post-5225154533794552394</id><published>2010-01-26T09:46:00.000-08:00</published><updated>2010-01-26T09:46:07.886-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='objc'/><category scheme='http://www.blogger.com/atom/ns#' term='xcode'/><category scheme='http://www.blogger.com/atom/ns#' term='iphone'/><title type='text'>A developer's perspective on iPhone applications</title><content type='html'>I'm proud to announce that Cogi has joined the rest of the world and &lt;a href="http://www.cogi.com/cogi-recorder-iphone"&gt;now has an iPhone application&lt;/a&gt;! We believe that we are uniquely suited to provide the best available recording application, for a variety of reasons. For more information on the app itself, check out &lt;a href="http://www.cogi.com/cogi-recorder-iphone-faq"&gt;its FAQ page&lt;/a&gt;. But convincing you of the merits of that app (of which I believe there are many), isn't the reason for this post. What I want to discuss in this post is the iPhone application development &lt;span style="font-style: italic;"&gt;process&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;To set the stage, let me note that I've worked on several applications, across many programming languages, with quite a few different development environments. However, after several weeks of development, I have come to the conclusion that iPhone development is quite possibly the &lt;span style="font-weight: bold;"&gt;worst&lt;/span&gt; environment for development of an application I have come across yet.&lt;br /&gt;&lt;br /&gt;Okay, you're hooked now, right? What am I going to say that's bad about XCode, Interface Builder and Objective C? Well, let's set that aside for the moment and discuss what's good about the environment first:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;XCode's refactoring tool may be the best I have ever seen -- just right click on a symbol, provide a name, click preview and apply. It even integrates with Subversion nicely, so I don't have to worry about renaming the files in Subversion first. The only thing that I haven't been able to get XCode to refactor properly was a category I applied to a built in class, which is understandable.&lt;/li&gt;&lt;li&gt;The provided iPhone simulator is great! It's a great way to build and test without an actual iPhone connected to your computer.&lt;/li&gt;&lt;li&gt;There is an amazing amount of detailed documentation. (If you can find it, more on that below.)&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;Unfortunately, that's about all I see in this environment that stands out from the crowd -- anything else is either merely par for the course, or much worse than everything else.&lt;br /&gt;&lt;br /&gt;So, what's so bad about it? Here are the highlights:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;i&gt;Objective C&lt;/i&gt;. In order to build an application of any complexity, Apple has forced developers into learning and developing with Objective C. Now, I know there's a good deal of history there, but this is a language that has roots in the 60's, and it shows. There is so much &lt;span style="font-style: italic;"&gt;cruft&lt;/span&gt; just to get a simple class in place that I cringe, and often want to avoid proper OO design, and avoid good testing practices, just because of the nasty overhead involved. The square bracket message passing syntax is a nightmare to type out and read, and difficult-at-best to indent properly for multi-line messages. Finally the ridiculously long names and function calls with arguments leave you with no choice but to have either strange linebreaks in your code or to have lines that are 200+ characters long.&lt;/li&gt;&lt;li&gt;&lt;i&gt;XCode&lt;/i&gt;. I see things online all the time about how &lt;b&gt;good&lt;/b&gt; XCode is, so maybe there's something wrong that we're doing here at Cogi, but we all hate XCode. In particular, it doesn't help you with navigating around many open files, and wants to impose it's idea of "groups" on top of your particular file system layout. You actually have to jump through hoops to create a hierarchy of folders that makes sense.&lt;tbd&gt;&lt;/tbd&gt;&lt;/li&gt;&lt;li&gt;&lt;i&gt;Interface Builder &amp;amp; UIFramework&lt;/i&gt;. Why can't you set anything on an object in Interface Builder that you can do programatically? Why aren't UITextField and UITextArea related objects?&lt;a href="http://www.blogger.com/post-edit.g?blogID=7867050353982208310&amp;amp;postID=5225154533794552394#UITextField"&gt;&lt;sup&gt;1&lt;/sup&gt;&lt;/a&gt; This area in particular is particularly difficult to get to work at all until you've read all of the hundreds of pages of documentation that Apple has made available.&lt;sup&gt;&lt;a href="http://www.blogger.com/post-edit.g?blogID=7867050353982208310&amp;amp;postID=5225154533794552394#complexity"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;tbd&gt;&lt;/tbd&gt;&lt;/li&gt;&lt;li&gt;&lt;i&gt;No way to simulate phone calls&lt;/i&gt;. As far as we've been able to find, when running your code in the iPhone simulator, there is no way to simulate either incoming or outgoing phone calls. In the real world, applications have to deal with this -- with no way to test this in a simulator, this requires a full build, deploy, test, and place a phone call cycle, just to discover that your change may or may not work as you expected.&lt;/li&gt;&lt;/ol&gt;And this is all assuming you've already jumped through all the hoops Apple requires you to go through &lt;span style="font-style: italic;"&gt;just to start &lt;/span&gt;developing on a phone. And that you have a pretty good understanding of the hundreds of pages of documentation required to "get" how Apple wants you to develop applications for the iPhone. And we're assuming you already have a Mac available to do the development (really, there's no good reason to force development in XCode on a Mac -- just put out an SDK that can be used from Windows already!).&lt;br /&gt;&lt;br /&gt;So, all that said, where do we go from here? Well, at Cogi, we are continuing to develop an outstanding iPhone application that our customers will love -- us developers will just have to continue to suffer through some amount of pain and frustration along the way. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.blogger.com/post-edit.g?blogID=7867050353982208310&amp;amp;postID=5225154533794552394" name="UITextField"&gt;1.&lt;/a&gt; This issue is particularly frustrating. So much so, that I will likely be writing more about it in the coming weeks.&lt;br /&gt;&lt;a href="http://www.blogger.com/post-edit.g?blogID=7867050353982208310&amp;amp;postID=5225154533794552394" name="complexity"&gt;2.&lt;/a&gt; Apple could learn a lot from the Pythonism: "Simple is better than complex." (from the &lt;a href="http://www.python.org/dev/peps/pep-0020/"&gt;Zen of Python&lt;/a&gt;).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7867050353982208310-5225154533794552394?l=cogidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cogidev.blogspot.com/feeds/5225154533794552394/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cogidev.blogspot.com/2010/01/developers-perspective-on-iphone.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7867050353982208310/posts/default/5225154533794552394'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7867050353982208310/posts/default/5225154533794552394'/><link rel='alternate' type='text/html' href='http://cogidev.blogspot.com/2010/01/developers-perspective-on-iphone.html' title='A developer&apos;s perspective on iPhone applications'/><author><name>Chris</name><uri>http://www.blogger.com/profile/05755072772135183636</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-7867050353982208310.post-1357001106087602675</id><published>2009-12-04T09:30:00.000-08:00</published><updated>2009-12-04T09:34:15.857-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='introductions'/><title type='text'>Introducing me!</title><content type='html'>Hi, I'm Chris.&lt;br /&gt;&lt;br /&gt;I'm a software developer and for the past year or so I've been working at &lt;a href="http://www.cogi.com/"&gt;Cogi&lt;/a&gt;. Prior to joining Cogi, I had worked with a couple others on the team, and was really excited to be working with them again in a fast paced, dynamic environment focused on developing high-quality software written in Python.&lt;br /&gt;&lt;br /&gt;Most of my development background is in C++ and Python, though I've been known to dabble in Flex, Java, Lisp, and lately Objective C, plus quite a few others. I tend to enjoy writing supporting libraries (like managing threads or state machines, etc), but also write a good deal of client- and server-based code.&lt;br /&gt;&lt;br /&gt;My language of choice is, without a doubt, &lt;a href="http://python.org/"&gt;Python&lt;/a&gt;. I've worked with several other languages, and looking back I now see how much time I wasted fighting with them. I love how Python just gets out of my way and helps me get the job done. (Of course there was the initial shock of "what, no curly braces and indentation matters?", but I quickly got over that.) I've been using Python since around 2.3, and I'm loving the continually improving language, right on up to Python 3.x.&lt;br /&gt;&lt;br /&gt;I'll be writing entries that mostly focus on working with Python, or some of the various tools and libraries that we use with Python, but I will likely also write entries for other projects and processes that we're involved with here at Cogi.&lt;br /&gt;&lt;br /&gt;If you have thoughts or questions, please feel free to ask -- I like to think that I'm pretty good about responding to emails or comments, so fire away!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7867050353982208310-1357001106087602675?l=cogidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cogidev.blogspot.com/feeds/1357001106087602675/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cogidev.blogspot.com/2009/11/introducing-me.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7867050353982208310/posts/default/1357001106087602675'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7867050353982208310/posts/default/1357001106087602675'/><link rel='alternate' type='text/html' href='http://cogidev.blogspot.com/2009/11/introducing-me.html' title='Introducing me!'/><author><name>Chris</name><uri>http://www.blogger.com/profile/05755072772135183636</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-7867050353982208310.post-2270700872504207128</id><published>2009-11-17T11:50:00.001-08:00</published><updated>2009-11-26T11:43:34.221-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='introductions'/><category scheme='http://www.blogger.com/atom/ns#' term='cogi'/><category scheme='http://www.blogger.com/atom/ns#' term='welcome'/><title type='text'>Introducing the Cogi Engineering Blog</title><content type='html'>Welcome to the new &lt;a href="http://www.cogi.com/"&gt;Cogi &lt;/a&gt;engineering blog!&lt;br /&gt;&lt;br /&gt;As you may have guessed from the title, this blog is maintained by the software engineering department at Cogi. We are a bunch of developers with a wide range of experience that have come together to build what we believe are the "best of breed" recording and transcription solutions for your phone, computer, and mobile devices.&lt;br /&gt;&lt;br /&gt;Over the next couple of weeks, several of us (the developers at Cogi) will likely write short introductions about ourselves, explaining what we do at Cogi, what our primary development areas are, and what kinds of topics we are likely to write about. You can expect to see engineering expertise ranging from embedded signal processing to web development, to desktop and mobile client development.  Figuring out how to make our solutions be scalable, reliable, correct and robust is one of our many core strengths.&lt;br /&gt;&lt;br /&gt;Here at Cogi, we do a wide variety of development work using the &lt;a href="http://en.wikipedia.org/wiki/Scrum_%28development%29"&gt;Scrum&lt;/a&gt; development process. We build client applications, web and telephony servers, and transcription engines in Python, Objective C, C and C++. We also have experience working with SQL databases and C# .NET applications.&lt;br /&gt;&lt;br /&gt;We are extremely excited to enter the blogging world, to share our experiences and learn from yours. Please feel free to contact us either via the comment system, directly through our profiles, or via email at &lt;a href="mailto:devblog@cogi.com"&gt;devblog@cogi.com&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7867050353982208310-2270700872504207128?l=cogidev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://cogidev.blogspot.com/feeds/2270700872504207128/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://cogidev.blogspot.com/2009/11/introducing-cogi-engineering-blog.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7867050353982208310/posts/default/2270700872504207128'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7867050353982208310/posts/default/2270700872504207128'/><link rel='alternate' type='text/html' href='http://cogidev.blogspot.com/2009/11/introducing-cogi-engineering-blog.html' title='Introducing the Cogi Engineering Blog'/><author><name>Chris</name><uri>http://www.blogger.com/profile/05755072772135183636</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>
