Thursday, December 23, 2010

Signing Your Work

I recently had occasion to go digging around in the installers for MacOS System 7.0.1 and 7.6, extracting its excellent beep sounds to use on my phone. While schlepping around I found wonderful little gems where the developers signed their work. The 7.0.1 installer binary contains a plea for help from the Blue Meanies, shown here. The 7.6 installation tome contains a series of images, reproduced further down this page. As the best laid plans of mice and developers often go astray, the largest image is corrupted in the CD golden image, with a blue cast over the bottom third of the image. I'm sure that was disappointing.

00000000  4d 61 63 69 6e 74 6f 73  68 20 53 79 73 74 65 6d  |Macintosh System|
00000010  20 76 65 72 73 69 6f 6e  20 37 2e 30 2e 31 0d 0d  | version 7.0.1..|
00000020  0d a9 20 41 70 70 6c 65  20 43 6f 6d 70 75 74 65  |.. Apple Compute|
00000030  72 2c 20 49 6e 63 2e 20  31 39 38 33 2d 31 39 39  |r, Inc. 1983-199|
00000040  31 0d 41 6c 6c 20 72 69  67 68 74 73 20 72 65 73  |1.All rights res|
00000050  65 72 76 65 64 2e 20 20  20 20 20 20 20 20 20 20  |erved.          |
00000060  20 20 20 20 20 20 20 20  20 20 20 20 20 20 20 20  |                |
*
00000200  60 04 4e fa 05 22 59 4f  2f 3c 62 6f 6f 74 3f 3c  |`.N.."YO/<boot?<|
00000210  00 01 a9 a0 22 1f 67 54  4f ef ff 86 20 4f 42 a8  |....".gTO... OB.|
00000220  00 12 42 68 00 1c 42 68  00 16 a2 07 66 34 31 68  |..Bh..Bh....f41h|
00000230  00 42 00 16 67 36 31 68  00 44 00 18 22 41 22 51  |.B..g61h.D.."A"Q|
00000240  21 49 00 20 21 7c 00 00  04 00 00 24 31 7c 00 01  |!I. !|.....$1|..|
00000250  00 2c 42 a8 00 2e a0 03  66 08 20 78 02 ae 4e e8  |.,B.....f. x..N.|
00000260  00 0a 0c 40 ff d4 66 04  70 68 a9 c9 70 63 a9 c9  |...@..f.ph..pc..|
00000270  a9 20 31 39 38 33 2c 20  31 39 38 34 2c 20 31 39  |. 1983, 1984, 19|
00000280  38 35 2c 20 31 39 38 36  2c 20 31 39 38 37 2c 20  |85, 1986, 1987, |
00000290  31 39 38 38 2c 20 31 39  38 39 2c 20 31 39 39 30  |1988, 1989, 1990|
000002a0  2c 20 31 39 39 31 20 41  70 70 6c 65 20 43 6f 6d  |, 1991 Apple Com|
000002b0  70 75 74 65 72 20 49 6e  63 2e 0d 41 6c 6c 20 52  |puter Inc..All R|
000002c0  69 67 68 74 73 20 52 65  73 65 72 76 65 64 2e 0d  |ights Reserved..|
000002d0  0d 48 65 6c 70 21 20 48  65 6c 70 21 20 57 65 d5  |.Help! Help! We.|
000002e0  72 65 20 62 65 69 6e 67  20 68 65 6c 64 20 70 72  |re being held pr|
000002f0  69 73 6f 6e 65 72 20 69  6e 20 61 20 73 79 73 74  |isoner in a syst|
00000300  65 6d 20 73 6f 66 74 77  61 72 65 20 66 61 63 74  |em software fact|
00000310  6f 72 79 21 0d 0d 54 68  65 20 42 6c 75 65 20 4d  |ory!..The Blue M|
00000320  65 61 6e 69 65 73 0d 0d  44 61 72 69 6e 20 41 64  |eanies..Darin Ad|
00000330  6c 65 72 0d 53 63 6f 74  74 20 42 6f 79 64 0d 43  |ler.Scott Boyd.C|
00000340  68 72 69 73 20 44 65 72  6f 73 73 69 0d 43 79 6e  |hris Derossi.Cyn|
00000350  74 68 69 61 20 4a 61 73  70 65 72 0d 42 72 69 61  |thia Jasper.Bria|
00000360  6e 20 4d 63 47 68 69 65  0d 47 72 65 67 20 4d 61  |n McGhie.Greg Ma|
00000370  72 72 69 6f 74 74 0d 42  65 61 74 72 69 63 65 20  |rriott.Beatrice |
00000380  53 6f 63 68 6f 72 0d 44  65 61 6e 20 59 75 0d 00  |Sochor.Dean Yu..|
Help! Help! We're being held prisoner in a system software factory! The Blue Meanies: Darin Adler, Scott Boyd, Chris Derossi, Cynthia Jasper, Brian McGhie, Greg Marriott, Beatrice Sochor, Dean Yu.
MacOS 7.6 installer images

 
Do You Sign Your Work?

Every ASIC I worked on has an undocumented register hardwired to read out my initials, and many ASIC designers follow a similar practice. Some take it further by changing the actual operation of the device, for example I've heard of a mode to insert a phrase like "We are the Knights who say Ni!" into the datastream (actual phrase omitted to protect the guilty). Functional modification like this always seemed too risky to me, a bug or manufacturing defect could conceivably enable it unexpectedly.

In ASIC design these tidbits serve a real business purpose: it is not unknown for departing employees to take a copy of a netlist or verilog source with them, and the company can find itself competing with its own designs. The existence of these telltale registers can serve as legal proof that the design was stolen, not reverse engineered.

Amongst software developers the practice of signing one's work is far from universal. GUI applications sometimes put developers names in the About box, though even this practice seems to be less common that it used to be. Developers of infrastructure devices without direct display to a user typically don't include any way of crediting the developers, in my experience. I think that is a shame. Signing ones work represents pride in craftsmanship, a desire to broadcast that "I made this."


 
Can You Sign Your Work?

Sometimes a company will ban the practice of listing developers names. A common reason I've heard is they don't want to enable recruiters to target their developers, but that is an astonishingly bad reason. Not only is it completely ineffective in this age of LinkedIn and hyper-connectedness, its also insulting that denying credit is considered to be a retention policy.

A second, more acceptable reason for banning names is that given the size and loose connections amongst teams working on a product its easy to mistakenly omit people who've made valuable contributions, engendering resentment. This is plausible, but I suspect that means the teams should find a way to maintain their own list of contributors and combine them as needed in the final product.


 
You Should Sign Your Work

I encourage developers to sign their work in an accessible (though not gratuitously intrusive) way. Encouraging pride in one's craft is a net positive for the product, and for the profession. Civil Engineers and architects on large projects sign their work, in the form of a plaque or cornerstone. Artists and craftspeople sign their work. Software engineers should, too.


 

Monday, December 20, 2010

Atomic Weight Adjustments

element old new
Hydrogen (H) 1.00794 [1.00784; 1.00811]
Lithium (Li) 6.941 [6.938; 6.997]
Boron (B) 10.811 [10.806; 10.821]
Carbon (C) 12.0107 [12.0096; 12.0116]
Nitrogen (N) 14.0067 [14.00643; 14.00728]
Oxygen (O) 15.9994 [15.99903; 15.99977]
Silicon (Si) 28.0855 [28.084; 28.086]
Sulfur (S) 32.065 [32.059; 32.076]
Chlorine (Cl) 35.453 [35.446; 35.457]
Thallium (Tl) 204.3833 [204.382; 204.385]
Germanium (Ge) 72.64 72.63

2011 is the International Year of Chemistry, reflecting the crucial importance of the physical sciences. To drive this point home and to demonstrate their power, the International Union of Pure and Applied Chemistry has decided to update the atomic weights of 11 elements, as described in this Ars Technica article and directly on the IUPAC site. The changes are relatively small, and elements with several common isotopes are now expressed as a range reflecting common ratios found in nature. For your convenience I've reproduced the updated atomic weights here.

Note that both Hydrogen and Oxygen have been updated. As water (H2O) is the largest component of the human body by mass, you may notice a difference on the scale in the morning. The good news is that in both Hydrogen and Oxygen the lower end of the new range is lighter than the old value, so on some days you may find that you weigh less than before. Unfortunately it is more likely to find yourself made up of heavier variants of the molecule. If this happens, try switching to a different brand of bottled water or moving to an area with a different source of municipal water.

A more significant adjustment is in the atomic properties of Silicon, which forms the basis of nearly all electronic technology. It is impossible to predict the full ramifications of this move, however it is hoped that they will be relatively minor. GPU results are likely to be slightly red-shifted, and you may need to adjust your monitor to compensate. There is absolutely no truth to the rumor that the change in Silicon will make it impossible for hackers to attack systems, please continue with your current vigilance and don't slack off on anti-virus and firewall software. Note that Germanium is also being adjusted, so a quick switch to a different semiconductor wouldn't help.

The most important take-away message is: Don't Panic. Sure, the Sun is mostly Hydrogen and we're blithely mucking with it, but there has been absolutely no credible evidence published in peer reviewed scientific journals that this will lead to a supernova, nor is there time to make it through the peer review process before the alleged supernova would happen. Don't believe anything you may read on the Internet making claims to the contrary.


 
 
 
 

(Yes, this is a joke)

Thursday, December 16, 2010

Code Snippet: libarchive

Paper Tapelibarchive is a library to handle tar, zip, cpio, pax, and many other archive formats. It uses a "walk through the archive" programming model, generally eschewing random access. Diving straight into it, we'll open a tar archive and list the files therein.

#include <archive.h>
#include <archive_entry.h>

archive.h contains the APIs for working with archives, archive_entry.h deals with files within the archive.

struct archive* archive = archive_read_new();
assert(archive != NULL);

archive_read_new() allocates the data structure to read an archive. It is only allocated in memory, and does not open a file on disk or tape. Later we'll open the file and associate it with the data structure.

if ((archive_read_support_compression_all(archive) != ARCHIVE_OK) ||
    (archive_read_support_format_all(archive) != ARCHIVE_OK))) {
  archive_read_finish(archive);
  // Error handling
}

There are a series of APIs like archive_read_support_compression_bzip2() or archive_read_support_format_tar() which can restrict the set of allowed formats, but here we set both the compression filter and format to anything libarchive supports. libarchive relies on external libraries for some things, such as libz for gzip, so the choices when building libarchive will restrict the formats it can support.

if (archive_read_open_filename(archive, "foobar.tgz", 8192) != ARCHIVE_OK) {
  archive_read_finish(archive);
  // Error handling
}

Here we've asked libarchive to open a file by name. There are also archive_read_open_FILE() and archive_read_open_fd() APIs to pass in a FILE* or file descriptor, respectively.

"8192" is the block size, which is used for a few archive formats like tar. Nonetheless libarchive does a good job of determining the real block size if it is incorrect. There is mention of removing the block size parameter in a future version of the library and relying solely on inferring it from the file.

struct archive_entry *entry;
while (archive_read_next_header(archive, &entry) == ARCHIVE_OK) {
  printf("file = %s\n", archive_entry_pathname(entry));
}

This is the main point of the routine: iterate through the entries in the file printing filenames, skipping over the data in between. Many archive formats lack a complete table of contents, instead allowing appends to extend the archive ad hoc. archive_read_next_header() will often have to seek through the file to find the next entry. If the file is located on a remote filesystem, this can be slow.

  archive_read_finish();

When we're done, archive_read_finish() frees the resources allocated by archive_read_new().


 
Reading File Contents

To extract a file from the archive you first iterate through archive_read_next_header() until you find one with the filename you want. I'll skip the code which does this as it is identical to that shown above, and start from the point where *entry points to the file we want.

size_t total = archive_entry_size(entry);
char buf[MY_BUF_SIZE];
size_t len_to_read = (total < sizeof(buf)) ? total : sizeof(buf);
ssize_t size = archive_read_data(archive, buf, len_to_read);
if (size <= 0) {
  // Error handling
}

archive_read_data() reads the content of *entry into a buffer. There are several variations such as archive_read_data_block() which additionally takes an offset, and archive_read_extract() which reads data and writes it to a file on disk.


 
Writing Files

Writing to an archive uses a similar set of APIs as reading.

  struct archive* archive = archive_write_new();
  assert(archive != NULL);

archive_write_new() allocates the data structure to track an archive. It does not create anything on disk

if ((archive_write_set_compression_gzip(archive) != ARCHIVE_OK) ||
    (archive_write_set_format_ustar(archive) != ARCHIVE_OK) ||
    (archive_write_open_filename(archive, "foobar.tgz") != ARCHIVE_OK)) {
  // Error handling
}

Where the read APIs allow "all" as a choice, writing an entry requires you to pick a format. Here I've chosen a tar.gz, and written it to foobar.tgz.

struct archive_entry* entry = archive_entry_new();
assert(entry != NULL);

struct timespec ts;
assert(clock_gettime(CLOCK_REALTIME, &ts) == 0);

archive_entry_set_pathname(entry, filename);
archive_entry_set_size(entry, contents_len);
archive_entry_set_filetype(entry, AE_IFREG);
archive_entry_set_perm(entry, 0444);
archive_entry_set_atime(entry, ts.tv_sec, ts.tv_nsec);
archive_entry_set_birthtime(entry, ts.tv_sec, ts.tv_nsec);
archive_entry_set_ctime(entry, ts.tv_sec, ts.tv_nsec);
archive_entry_set_mtime(entry, ts.tv_sec, ts.tv_nsec);

Here we create the metadata for a file in the archive, populating it with permissions and timestamps. Not all archive formats support all of these timestamps, but it seems a good idea to populate them in case a different format is chosen later.

int rc = archive_write_header(archive, entry);
archive_entry_free(entry);
entry = NULL;
if (ARCHIVE_OK != rc) {
  // Error handling
}

Once the metadata has been written to the archive, the archive_entry is no longer needed.

size_t written = archive_write_data(archive, contents, contents_len);
if (written != contents_len) {
  // Error handling
}

archive_write_finish(archive);

Finally, we write the data. contents is a pointer to a buffer in memory, contents_len is its length in bytes. archive_write_data() can be called multiple times, each will append its contents at the end of the last. There is no random access API with an offset parameter.


 
Closing Thoughts

libarchive APIs are designed to allow use with either disk or tape. There are no APIs to overwrite bytes in the middle of a file, because tape drives cannot do that without corrupting adjacent data. There is an alternate set of APIs designed for disk in archive_read_disk and archive_write_disk, though I see relatively little difference in them other than accessing the uid/gid of the archive itself.

I hope you find this useful.


Monday, December 13, 2010

Game Mechanics All the Way Down

There has been much discussion about the impact of Facebook Places on geo-location services, and what FourSquare should do to differentiate itself. I humbly present some suggestions on new areas to move into, and how they might benefit from the game mechanics FourSquare uses for location checkins.

Badge with bowling ball FourSpare: Badges for Bowling

Example mechanics:

  • STRIKE! Ten points!
  • Gutter ball. No points this time, but try again!
Badge with a pair of pants FourWear: Game Mechanics for Clothes Shopping

Example mechanics:

  • Blue. Definitely blue. 10 points!
  • No points for khakis. Perhaps you should try for the FourSpare badge instead.
Badge with a silhouette of a horse FourMare: Internet Betting is Illegal, Try This Instead

Example mechanics:

  • Always check the teeth. 5 points.
  • Sure you lost all your money, but more importantly you lost 200 points.
Badge with a stylized alligator FourDare: Points for Insanely Dangerous Things

Example mechanics:

  • Scary looking animals don't automatically score higher, it has to be truly dangerous.
  • A broken arm earns 20 points. Break your clavicle for more points!
Badge with a fluffy white cloud FourAir: The Gamification of Breathing

Example mechanics:

  • (inhale) Thats 2 points!
  • (exhale) Another 2 points!

Sunday, December 12, 2010

Sand Mandala

Last week over the course of five days, a group of Buddhist monks constructed a Sand Mandala in a lobby of the Googleplex, using brightly colored sand they had ground and prepared by hand. Quoting from the description provided by the monks, "This work of art is meant to depict a picture of the world in its divine form, and represents a map by which the ordinary human mind is transformed into the enlightened mind."

Sand Mandala

Sudhakar 'Thaths' Chandra posted a time lapse video of its construction over the entire five day span.

The Mandala also represents the impermanence of all things. On friday in a dissolution ceremony, the Mandala was swept away. The sand used in its construction, and all of the energy it contains, was cast into a body of water as an offering to the Naga. I took home a small bag of sand from this Mandala, which we cast into a lake near our house.

Sand Mandala

While they were here I got the opportunity to hear a talk and sit in meditation with one of the monks. I've tried meditation several times before, and it turns out that his mere presence didn't make it easier to maintain focus. Who knew? I guess I'll keep practicing.

Monday, December 6, 2010

Orgchart Enlightenment

orgchart as rearranged caffeine molecule

It turns out that arranging your orgchart as an unfolded caffeine molecule works better in theory than in practice.

Thursday, December 2, 2010

Engineering in a Small World

I currently work in a relatively large development team. As is the case with every team of that size, we are organized as one enormous group where everybody works with everybody else, every day. I've graphed out our team interactions. I'm sure it looks a lot like your team, right?

fully connected graph of 20 people

loosely connected group of 20 people Wait: does that sound weird, based on your experience? You're right, I made it all up. We're not organized as one enormous group, we're grouped into smaller teams like everybody else. Yet to a degree, the larger group has to be able to coordinate between every single person, every day. How is this accomplished?

Even in a relatively small group of people, a certain pattern emerges. Most individuals in the group interact with a small number of others, but a few are far more highly connected and routinely interact with dramatically more. These connectors result in enormous groups, loosely coupled. This is the phenomena which leads to the six degrees of separation theory, that on average any two people on the planet can be connected by six friends of friends. This pattern is also the basis of the six degrees of Kevin Bacon, who is one of those "highly connected" nodes in the graph of film actors.


 
The Small World Pattern

This phenomena is called the Small World pattern. I first read about it in Here Comes Everybody.

Cover of Here Comes Everybody

Here Comes Everybody, chapter 9.
... the chance that you know [a highly connected person] is high. And the "knowing someone in common" link - the thing that makes you exclaim "Small World!" with your seat mate - is specifically about that kind of connection.


The Small World Pattern seems obvious, in hindsight. Of course some people are simply more social and outgoing that others. They make an effort to meet people. They form connections. They are far more connected to other people than most.

The rest of this musing will concern the Small World Pattern in engineering organizations.


 
The Small World Scoffs at Your Orgchart

Connections can be forced, organizationally: a regular meeting between tech leads from related projects, for example. Connections can also happen by happenstance, as when members of different teams work at adjacent desks. However, the strongest connections happen because some percentage of the engineering population wants to be connected. They are outgoing, and enjoy talking to people outside their immediate coworkers. These connections are far more persistent, and likely to survive past the end of any particular project or recurring meeting.


 
No Group is an Island, but Some are Peninsulas

Something which can happen in a large company: you work on an infrastructure project which should be applicable in a number of different areas, yet never seems to get the attention you think it deserves. Other groups which could leverage your work instead do their own thing, and later only grudgingly evaluate your system before pronouncing it unfit. Is it because you've misunderstood their requirements? Is it because they think your implementation is poor?

More likely, its because you lack connections from your group to others. It takes just one person in the right place at the right time to say "we should go talk to John on Project Foo." When these suggestions are made organically and at the right time, they are far more likely to be acted upon. When such a suggestion comes as an edict way after the decision point, such as via some recurring meeting, it is far less likely to be received favorably.


 
To the Connector Go the Spoils

Being highly connected within an engineering organization reaps many rewards. People associate them with the good outcomes of serendipitous introductions.

Being highly connected within an engineering organization also suffers some downsides. I wish I understood the psychological reason why, but nonetheless it happens: Technical competence as an individual contributor will be questioned more often if you spend significant time interacting with other groups. Its weird.


 
Closing Thoughts

Engineers are human, though in your daily work it might not always seem so. Understanding human behavior is as important in our field as in any other. I highly recommend Shirky's Here Comes Everybody, and his subsequent Cognitive Surplus. Both are excellent.

Tuesday, November 23, 2010

Code Snippet: ctemplate

Content management systems like Django typically do not embed HTML strings directly in code. They separate the presentation of the data out from the code which assembles the data by using templates. Here is an example Django template taken from a small App Engine project of mine:

<div class="resultsSectionItems">
{% for comment in friend.comments|slice:":29" %}
  <div><a href="http://friendfeed.com/e/{{ comment.entryObj.entryId }}" ... etc
  <span class="commentText">{{ comment.commentText }}</span>
  </div>

Each "{%" block is a template command. This template iterates through comments, creating links.


Templates maintain a separation of responsibilities. The code prepares data structures populated with the data to display. The template iterates over those structures, generating and formatting output. Templates are widespread within content management systems, but they can also be useful in embedded systems work. Some examples:

  • Presenting common system data to CLI, embedded web server, and SNMP backends.
  • Allowing an OEM to customize the output to include their logos and branding, without having to change code.
  • Easier support for multiple languages, as most text should be in templates not code. Templates also tend to compress well, lowering the footprint of internationalization.

Most CMSes are written in Python/Ruby/Java/Perl or other high level languages. There is an opensource C++ templating package by Craig Silverstein at Google called ctemplate. Here is an example which produces a portion of the Apache httpd.conf file based on internal configuration data:

# This file is autogenerated from configuration. Changes will be lost
# after the next config change.

{{#DIR}}<Directory {{PATH}}>
{{#OPTIONS}}  Options {{#OPT}}{{VAL}} {{/OPT}}{{/OPTIONS}}
  Order {{ORDER}}
</Directory>
{{/DIR}}

Code dealing with populating variables in dictionaries is bolded in the example below, as this is the key point of using ctemplate.

#include <assert.h>
#include <ctemplate/template.h>
#include <iostream>
#include <list>

void apache_example() {
  // Apache <Directory> blocks to create
  struct ApacheDir {
    const char* path;
    std::list<const char*> options;
    bool deny;
  } apache_dirs[] = {
    {"/var/www", {"FollowSymLinks"}, false},
    {"/SecretFeature", {"ExecCGI", "-Indexes"}, true},
    {"/Tetris", {}, false}
  };

  ctemplate::TemplateDictionary dict("APACHE_EXAMPLE");
  int num_dirs = sizeof(apache_dirs) / sizeof(apache_dirs[0]);
  for (int i = 0; i < num_dirs; ++i) {
    struct ApacheDir* entry = &apache_dirs[i];
    ctemplate::TemplateDictionary* sub_dict = dict.AddSectionDictionary("DIR");

    assert(entry->path != NULL);
    sub_dict->SetValue("PATH", entry->path);

    std::list<const char*>::const_iterator li;
    for (li = entry->options.begin(); li != entry->options.end(); ++li) {
      sub_dict->SetValueAndShowSection("OPT", *li, "OPTIONS");
    }
    sub_dict->SetValue("ORDER", (entry->deny ? "deny,allow" : "allow,deny"));
  }

  std::string output;
  ctemplate::ExpandTemplate("apache.tpl", ctemplate::DO_NOT_STRIP,
                            &dict, &output);
  std::cout << output << std::endl;
}

The example shows some interesting features beyond simple variable substitution. The OPTIONS section is only displayed if there are options present, by using SetValueAndShowSection() in the code. The output of running this code is:

# This file is autogenerated from configuration. Changes will be lost
# after the next config change.

<Directory /var/www>
  Options FollowSymLinks 
  Order allow,deny
</Directory>

<Directory /SecretFeature>
  Options ExecCGI -Indexes 
  Order deny,allow
</Directory>

<Directory /Tetris>

  Order allow,deny
</Directory>

Like many other templating systems, ctemplate can apply modifiers to expanded variables. The builtin modifiers mostly concern escaping of HTML, XML, or JSON to avoid common security issues like cross site scripting. It is possible to supply additional variable modifiers by subclassing ctemplate::TemplateModifier. The App Engine example at the top of this article pipes variables through a slice statement to truncate strings to a specific length. We can create equivalent functionality for ctemplate by subclassing the TemplateModifier. The Modify() method is shown in bold, as this is the key part of the implementation.

class MaxlenModifier : public ctemplate::TemplateModifier {
  virtual void Modify(const char* in, size_t inlen,
                      const ctemplate::PerExpandData* per_expand_data,
                      ctemplate::ExpandEmitter* outbuf,
                      const std::string& arg) const {
    unsigned int maxlen;
    if ((sscanf(arg.c_str(), "=%u", &maxlen) == 1) && (maxlen <= inlen)) {
      outbuf->Emit(std::string(in, maxlen));
    } else {
      outbuf->Emit(in);
    }
  }
};

void modifier_example() {
  MaxlenModifier* maxlen = new MaxlenModifier();
  if (!(ctemplate::AddModifier("x-maxlen=", maxlen))) {
    printf("AddModifier failed\n");
    exit(1);
  }

  ctemplate::TemplateDictionary dict("MAXLEN_TEST");
  dict.SetValue("LONGSTRING", "0123456789abcdefghijklmnopqrstuvwxyz0123456789");
  std::string output;
  ctemplate::ExpandTemplate("maxlen.tpl", ctemplate::DO_NOT_STRIP,
                            &dict, &output);
  std::cout << output << std::endl;
}

Our custom modifier is instantiated in the template using x-maxlen=N. Prefixing customer modifiers with "x-" is very strongly encouraged in the ctemplate documentation.

The original string: {{LONGSTRING}}
A maxlen=10  string: {{LONGSTRING:x-maxlen=10}}
A maxlen=20  string: {{LONGSTRING:x-maxlen=20}}
A maxlen=80  string: {{LONGSTRING:x-maxlen=80}}

Here is the output, with the long string truncated to various lengths:

The original string: 0123456789abcdefghijklmnopqrstuvwxyz
A maxlen=10  string: 0123456789
A maxlen=20  string: 0123456789abcdefghij
A maxlen=80  string: 0123456789abcdefghijklmnopqrstuvwxyz

I've found ctemplate to be quite useful, and I hope others do as well.

Monday, November 22, 2010

Cross Control Confusion

My first thought when starting Photoshop this morning: "Why does Photoshop need to know my location? I should turn that off."

Adobe Photoshop splash screen icon looks like the Twitter location toggle

Saturday, November 20, 2010

Happy Birthday Microsoft Windows

Windows 1.0 logo

Windows 1.0 shipped on November 20, 1985, making today the 25th birthday of Windows. Happy Birthday, Windows. What a long, strange trip it has been.

Windows 1.0 logo birthday cake with candle

Thursday, November 18, 2010

Code Snippet: getifaddrs

A few months ago I posted a description of how to use SIOCGIFCONF to retrieve information about interfaces. SIOCGIFCONF is somewhat clunky in that you use an ioctl to find out how many interfaces are present, allocate enough memory to retrieve them all, and then issue another ioctl to actually get the information. To handle the vanishingly small chance that more interfaces will be added during the time you spend allocating memory, a fudge factor of 2x is added to the memory allocation. Because, you know, its not likely the number of interfaces would double.

That was all very silly, and as it turns out in Linux there is a much better API for retrieving information about interfaces: getifaddr(). The call handles memory allocation so you don't have to pass in a buffer of sufficient size, though you do have to call freeifaddrs() afterwards to release the memory. getifaddrs allows each protocol family in the kernel to export information about an interface. The caller has to check the address family of each returned interface to know how to interpret it. For example, AF_INET/AF_INET6 contain the interface address, while AF_PACKET has statistics. Example code for these three families is shown here.

#include <arpa/inet.h>
#include <sys/socket.h>
#include <netdb.h>
#include <ifaddrs.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/if_link.h>

int main(int argc, char *argv[]) {
  struct ifaddrs *ifaddr;
  int family, s;

  if (getifaddrs(&ifaddr) == -1) {
    perror("getifaddrs");
    exit(1);
  }

  struct ifaddrs *ifa = ifaddr;
  for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
    if (ifa->ifa_addr != NULL) {
      int family = ifa->ifa_addr->sa_family;
      if (family == AF_INET || family == AF_INET6) {
        char ip_addr[NI_MAXHOST];
        int s = getnameinfo(ifa->ifa_addr,
                            ((family == AF_INET) ? sizeof(struct sockaddr_in) :
                                                   sizeof(struct sockaddr_in6)),
                            ip_addr, sizeof(ip_addr), NULL, 0, NI_NUMERICHOST);
        if (s != 0) {
          printf("getnameinfo() failed: %s\n", gai_strerror(s));
          exit(1);
        } else {
          printf("%-7s: %s\n", ifa->ifa_name, ip_addr);
        }
      } else if (family == AF_PACKET) {
        struct rtnl_link_stats *stats = ifa->ifa_data;
        printf("%-7s:\n"
               "\ttx_packets = %12u, rx_packets = %12u\n"
               "\ttx_bytes   = %12u, rx_bytes   = %12u\n",
               ifa->ifa_name,
               stats->tx_packets, stats->rx_packets,
               stats->tx_bytes, stats->rx_bytes);
      } else {
        printf("%-7s: family=%d\n", ifa->ifa_name, family);
      }
    }
  }

  freeifaddrs(ifaddr);
  exit(0);
}

On my system the output is as follows (though I've obscured the addresses):

lo     :
        tx_packets =     16714641, rx_packets =     16714641
        tx_bytes   =   1943837629, rx_bytes   =   1943837629
eth0   :
        tx_packets =    102862634, rx_packets =    118537985
        tx_bytes   =   3472339330, rx_bytes   =    698859563
gre0   :
        tx_packets =            0, rx_packets =            0
        tx_bytes   =            0, rx_bytes   =            0
lo     : 127.0.0.1
eth0   : 10.0.0.1
lo     : ::1
eth0   : 1111:1111:1111:1111:a800:1ff:fe00:1111
eth0   : fe80::a800:1ff:fe00:1111%eth0

Tuesday, November 16, 2010

More on Increasing the Speed of Light

A modest suggestion to increase the speed of light resulted in interesting discussion which I would like to highlight by Kit Dotson at SiliconANGLE and by Howard Marks at Network Computing. Howard Marks wrote about details of the chemistry of fiber optic cables, in particular that the index of refraction is related to the density of the material. A fiber with a 10% lower index would be less dense than water, which makes it unlikely to be practical. C'est la vie.




Speed Limit 222,970 km/secAlso I'll state again: the speed of light in fiber only matters for wide area links. The propagation delay in 100 meters of fiber is dwarfed by queueing and software delays, to the point of insignificance. In fact if we could reduce the cost or power consumption of short range lasers by making the speed of light even slower in the fiber they drive, that would be a good tradeoff.

For long range links things become more interesting. Internet lore says that Amazon found each 100 msec of page load time resulted in a 1% increase in abandoned transactions, though I cannot find a hard reference for this data. E-commerce is heavily studied as there is money involved, but general satisfaction with a website increases when it has "teh snappy." This isn't just a function of bandwidth: most web pages require multiple round trips to fully render, owing to the pervasive use of JavaScript to trigger the loading of additional page elements. The round trip time matters.

For long reach fiber the useable spectral capacity is probably the most important factor, as this determines the numer of wavelengths it can carry and is the primary economic justification. Long reach fibers also have to trade off clarity (i.e. loss of signal) because that determines how far apart the amplifiers/regenerators have to be. This is where I'd throw the index of refraction into the mix, as another factor to be weighed and optimized for.

Monday, November 1, 2010

Intel and Achronix Get Engaged

Fake Intel x86 with FPGAsIn January JP Morgan predicted that Intel would acquire an FPGA vendor in 2010. Speculation immediately focussed on Altera and Xilinx, which are large enough to have a material impact on Intel's sales. I wrote about it then, speculating that Intel would use the technology to get into various embedded market segments without needing a zillion SoC variants. Choose a die with appropriate I/O pins, load the logic into FPGA blocks alongside the CPU, and voila!

Yesterday the Wall Street Journal reported that Intel is opening their fabs to Achronix Semiconductor, a startup with interesting FPGA technology. The Achronix home page highlights what is presumably the immediate benefit to Intel, in unlocking additional sales to US military and intelligence agencies.

"The Achronix Speedster22i FPGA Platform uniquely enables applications that require an end-to-end supply chain within the United States. Being built at an onshore location offers significant advantages to programmable logic users who demand the highest level of security."

Presumably the agencies interested in using these parts want to embed optimized hardware to offload algorithms from software. This can be necessary for some applications, if the customer has the resources to implement it. The desire for an on-shore supply chain which can be audited is in reaction to the inadvertent use of counterfeit chips in previous military systems.

Achronix is using branding for the product line which looks remarkably like Intel's, and it seems certain the deal has provisions for cancellation or modification upon change of control to another party. This announcement also amounts to Intel marking their territory for an acquisition.


I/Os Considered Important

DoD requirements notwithstanding, there are relatively few applications where embedding algorithms in FPGAs makes sense. The drawback has never been a technological one, in requiring closer cooperation between CPU and FPGA. It is a business issue: once you commit to a specialized hardware design, the clock starts ticking. There will come a day when a software implementation could meet the requirements, and at that point the FPGA becomes an expensive liability in the BOM cost. You have to make enough profit from the hardware offload product to pay for its own design, plus a redesign in software, or the whole exercise turns out to be a waste of money.

There is another quote on the Achronix technology page which is quite relevant:

"Speedster FPGAs include four embedded DDR1/2/3 controllers, each offering up to 72 bits of data at 1066 Mbps. ... The DDR controllers are fully by-passable so the pins can be used as general I/O if the DDR controllers are not needed." (emphasis added)

Being able to select various I/O drivers for a pin in an FPGA is relatively common, but generally quite limited. Very high speed SERDES pins often cannot be reassigned or are restricted in what else they can be used for, because the high speed interface is sensitive to layout and loading. If Achronix has developed robust I/O muxing with more flexibility, this would be very interesting to Intel. It gets them closer to having a small selection of silicon dies, with different IP loads to target specific markets.

Using FPGAs as a way to tailor chips for specific markets makes a lot more sense than algorithm offload, IMHO. This provides products which could not otherwise exist, as it would be difficult to justify the incremental cost of each different chip. Amortizing the cost of silicon development over a much larger number of different applications makes more sense.