Monday, March 3, 2014

Ongoing Goto Wars

Recently, Apple announced a security fix for their SSL implementation.  I would have taken little note here, except WIRED's summary indicated the cause being the use of goto.  Ah, goto, how useful you are.  How abused you are.

In practice, I would also have used goto, if I was writing a series of conditionals where I only have a single error block.  The code could be restructured to link all of the conditionals into a single clause, yet it also tries to retain the specific error code for the check that failed.  I would really dislike to see the following:
if ((e = foo) != 0 && (e = bar) != 0 && ...) goto fail;
So what then?  Do we call upon the software engineering folk to better analyze programs and find these mistakes?  Or write style guidelines that require braces for every if, and ban every goto?  And then the employee's manager is required to sign off on every exception.  These are some of the many proposals floated for addressing this, and often made by programmers touting the superiority of their proposal and how this would never happen to them.

Leave me to shrug.  I'll use my goto, sparingly.  And I'll continue to try to impress on students that they and all of us programmers will make mistakes so write elegant code to help find these bugs.  Until then, if anyone has a good construction for the error code that doesn't require goto, let me know.

Friday, February 21, 2014

Being Nice on the Internet

I am sitting in Catherine Grevet's thesis proposal on supporting diverse opinions online (e.g., NBC News).  The intent is not to achieve some undescribed utopia, but rather establish mechanisms that encourage civil discourse.  The goal is not consensus, but rather engagement.

Looking at links and replies, links are more commonly made to similar opinions as the linker.  Replies do not have this distribution, but the replies are often incivil and not constructive.

Under what conditions do different opinions coexist in social media? If these links are being maintained, then it is possible for the opinions to coexist and suggest possibility that conditions could be identified.  Through twitter, politically active (based on sharing white house petitions) social users were contacted about how they manage this issue.  Four techniques were identified:
tuning out, long fruitless conversations, weak ties were brittle (don't want to unfriend, but...), and changing perceptions of friends.  These behaviors reinforce homophily.

Research questions:
  • How does discouraging incivility impact the discourse between friends of different opinions in social media?
  • How does encouraging civility impact the discourse between friends of different opinions in social media?
Using a politeness classifier, facebook plugins will be developed to adjust the design based on the classifications received.  Perhaps hiding impolite comments, or even hiding the entire post, are techniques for addressing the above question about reducing / discouraging incivility.  Toward the final question, two probes could be applied: feedback on impoliteness to encourage more civil posts and highlighting polite friends.

So keep an eye out for a new plugin!

Tuesday, February 4, 2014

Book Review: ARM Assembly Language: Fundamentals and Techniques

Besides reading an average of 5 research papers every week, I also read an average of one book each week.  Occasionally those books relate to computers and usually then I'll write about them here.  I had realized a couple months ago that I didn't really know anything about ARM processors, besides that they are low power.  It seemed remiss to be studying computer architecture and not know one of the modern architectures.  Thus I visited my school library and checked out a book.

This is the story of that book - ARM Assembly Language: Fundamentals and Techniques.  An interesting book that covered the basic of what I wanted to learn, but the short coming was that it had an expected environment that was different from mine.  ARM processors can be found in a greater diversity of devices than say, x86.  Yet, I am still thinking about the ARM processor as a drop-in replacement.  I look more to devices like Microsoft's Surface or a smartphone, and think about the presence of an OS, etc.

I learned particularly that the ARM instructions have bits to make them predicated.  And I realized then that conditional branches are really just predicated instructions.  If the predicate(s) are true, then take the branch.  Just another perspective on instruction sets.  Anyway, I look forward to getting a Raspberry Pi, so I can try out some of what I've learned and get a chance to also work through the assembly generated by compilers.

Monday, September 23, 2013

Book Review: Turing's Cathedral

Recently, I read the work of history, Turing's Cathedral: The Origins of the Digital Universe (Vintage), which is an interesting book that tells of the development of some of the first computers in the United States.  It's particular focus is on the founding of the Institute for Advanced Study (IAS), and then John von Neumann's time there.  Now, the von Neumann architecture is something I regularly conceptualize and use in teaching.  And it was interesting to read of how this architectural model was developed, and why.

In contrast, a significant portion of the book was instead written as a history of the Institute.  Given that the Institute provided access to the records used as a significant part of the source material, it is understandable that the author's focus would be so directed.  However, it adds to the misleading focus that this work follows.

Of perhaps greater slight is that a work titled "Turing's Cathedral" only features Alan Turing for a small part of the writing.  Instead we find greater focus placed on his work and how it fit into the research of that time.  Eventually leading von Neumann to explore the usage of electronic digital computers to solve the US military's problems.  He, like many European scientists, had left his homeland ahead of Hitler, and these scientists supported work leading to Germany's defeat.

The grand development that von Neumann introduced was making a computer, programmable. Beyond just reconfigurable, the project he lead at the IAS was programmable, the electronic device could store both data as well as codes that were instructions for what the device was to do.  Consider that for the next 50 years, programs would be constrained by having to store the instructions in memory, which was often a very limited resource.

So John von Neumann stars in a book titled for Turing, and a book that devotes a third of its pages to references.  A good, interesting work that could probably have been improved by an editor's scissors.  To trim the writing down to the core bits about computers, and set aside so much of the well researched chapters to attain a focus that is lacking.

Wednesday, July 24, 2013

Repost: There's nothing like a tidy codebase

When I grade student assignments, around 90% of the grade is for what the code does.  The other 10% is for how it looks, because There's nothing like a tidy codebase.  So take some additional time and help ensure that the code you write is readable.

Monday, July 22, 2013

Review: Patterns for Cache Optimizations on Multi-processor Machines (ParaPLoP '10)

In order to optimize any code, know that the optimizations "depend on the exact architecture of the machine (processor, memory, etc), the exact version of the compiler, the exact version of the operating system and the particular configuration of the program that [you] are trying to optimize."  At minimum, you should establish what your workload is and how its performance is measured, and then have some gauge of what its current performance is (including the distribution of measurements).  All of which is covered in Patterns for cache optimizations on multi-processor machines before they are touch on any optimization.  And I applaud them for doing so.

In this work, they explore three patterns of cache (mis-)use on modern processors.  The first pattern is termed "loop interchange", named for its solution.  In this pattern, the program does not access data with spatial locality.  Instead of accessing every element in a cache line, the program has a different ordering and only touches a subset of the cache line, while later (after the line has been evicted) it accesses other subsets.  In the example below, assume that N and M are both quite large (say 1+ million), so this code will likely have significant cache misses (at minimum L1 misses), while switching the "i" and "j" for loops (i.e. interchange) will considerably reduce the number of cache misses.

int X[N][M];
for (j = 0; j < M; j++)
  for (i = 0; i < N; i ++)
    f(X[i][j]); // Any set of operations applied to this element.

The next pattern is false sharing.  Threads in a program intentionally and unintentionally share data.  Data structures are written by programmers to logically group data; however, the grouping and structuring of the data is often made by the programmer and not for algorithmic need.  The hardware is expecting locality from arrays and data structures.  When multithreaded, the cache line is the unit by which the hardware tracks sharing of data.  So if different threads write to different data in the same cache line, then hardware treats the writes as being made to the same thing, which precludes it from caching.  The usual recommendation for solving this problem is to pad the data, so that the software notion (int) and hardware notion (cache line) are the same size.

int X[N];
void* thread_work(int tid)
{
  for (int i = 0; i < N; i++)
    if (i % num_threads == tid)
      X[i] = do_work(X[i]);
}

This second example goes beyond the paper's scope for false sharing.  Common data structures may also have different sharing patterns for each element.  For example in this data structure, the following fields are written to: encoding, sum, weight_left, and weight_right.  The rest are read-only. Currently the data structure uses two cache lines (as all fields are 8-bytes in size).   If the structure was rearranged so that the written fields were in one cache line and the read-only fields in the second line, then updates by any thread would only result in one cache miss rather than two.  Padding may be required, but the key insight here is arranging data by sharing pattern, which is a generalization of the previous paragraph.

typedef struct _node {
    graph_value_t value, encoding;
    unsigned long long sum;
    struct _edge* fwd;
    struct _edge* back;
   
    // tree sorted by value
    struct _node* left;
    struct _node* right;
   
    // tree sorted by encoding

    struct _node* weight_left;
    struct _node* weight_right;
} node, *pnode;


The final pattern explored in the paper is data alignment.  Ignoring the issue of misaligned accesses, let's look at misaligned allocations.  Suppose we allocate an array of 48-byte data structures in a multithreaded program.  Sometimes accessing an element is one cache miss, but sometimes it is two.  The runtime system has packed the data structures together, with 4 fitting in 3 cache lines.  In general, when you allocate data structures, they come with the same alignment as in the array, made to a 16-byte boundary, but this boundary is not guaranteed to be the start of a cache line.  The primary solution is to use support calls that change the allocation alignment.  This may waste space, but now the allocation comes using our expected number of cache lines.  And by using the lines we expect, we can tailor the program to the architecture and observe the expected performance characteristics.

The patterns are three simple ones that architects and performance minded programmers have known for years.  I am pleased to see them being reiterated, but the response may be like that from the developer after I changed his code per these patterns years ago, "Why can't the compiler just do that for me?!"

Thursday, July 11, 2013

Book Review: Exceptional C++ Style 40 New Engineering Puzzles, ...

Generally, I do not write code in C++; however, on occasion (like when writing LLVM compiler passes), I am forced into using this language.  I also more regularly find myself grading student assignments that have used C++.  Particularly reading these assignments, I will be thankful to have read this book and better be able to express how students have violated the standards, as they have done in the past.

Were I forced to read a book directly on the C++ standards, let's just say I can think of lots of things I'd rather be doing.  But while Exceptional C++ Style: 40 New Engineering Puzzles, Programming Problems, and Solutions exposed me to more of the standards, I never felt like I was reading quotes from the standard.  Instead, I was listening to an interesting conversation about some real programming questions that just may require invoking the standards to answer.

I enjoyed chapters 20 and 21, as I appreciate the effort toward explaining how memory is allocated and structures laid out.  Help dispel the ignorance that new / malloc are how the OS provides memory.  And I then learned that new will throw an exception instead of returning NULL.  Perhaps time to rewrite some code.  Furthermore, I understand now why most C++ code uses preincrement on iterators.

It is not strictly a book on style, but instead this tome covers the style I care most about: good programming practices.  I don't care which style convention you use, so long as you use one consistently.  But for whatever style your code has, it had better be good code.

I recommend reading this book even if you do not regularly use C++.  I will note that it is dated; however, unless you are now using C++11, the text is still timely and even if you are using C++11 I doubt that everything has changed (though I did notice the discussion of the auto keyword was out of date).