HN user

eckzow

74 karma
Posts0
Comments28
View on HN
No posts found.

Zipline | South San Francisco, US | Remote ok (with travel) | Full Time | Embedded Systems | Robot Perception

Zipline uses drones to deliver critical and lifesaving medicine to thousands of hospitals serving millions of people in multiple countries. Our mission is to provide every human on Earth with instant access to vital medical supplies. Join Zipline and help us make this a reality for billions of people.

We're hiring an embedded systems specialist to help us build perception systems for our aircraft. You'll build high-performance, real-time, safety-critical embedded software systems from kernel drivers to neural networks. We write Rust and C++ on embedded Linux, and we work with hardware from i2c to GPUs. We train networks and analyze data with industry-standard Python tools.

More information and apply @ https://flyzipline.com/careers/job/4633974003/?gh_jid=463397...

We're hiring for many other positions, too! https://flyzipline.com/careers/

The first footnote of the article says:

While that could mean just about anything, nearly everyone who writes a half-decent hand-written parser, whether they know it or not, is writing a recursive descent parser [1].

[1] Parsing Expression Grammars (PEG)s and “parser combinators” in some functional languages are just recursive descent parsers in disguise.

Which, while I haven't thought about it deeply, intuitively makes some sense: parser combinators encourage an approach mostly equivalent to little functions that do something like "parse a $thing, or parse a $different_thing" (etc.) which very much translates down like a recursive descent parser.

I'll second my sibling comment in saying that C (and C++!) will be around for a long time, and is epically easier to get hired with if you're trying to break into the industry. The C ecosystem just has too many advantages at this point (i.e. not just existing code but also number of supported platforms, etc.).

Also, a significant fraction of embedded code is going to be "unsafe" Rust by definition: drivers performing volatile load/stores on memory mapped hardware. In those scenarios, defect mitigation techniques are a matter of system architecture (MPUs, pre-empting deadline-based task schedulers) rather than being language-specific. Arguably even Rust provides you with no protection against the most devious bugs (memory barrier usage, cache-DMA interactions).

I agree with your sentiment, but I think the situation is a bit more nuanced.

The usual argument is that headers don't actually provide good encapsulation: implementation details internal to the class (i.e. private members) end up getting leaked into the public header. There are workarounds (pimpl, opaque "handle" types, etc.) but they all have their own disadvantages (mostly an extra layer of indirection).

Complicating the matter is the issue of static polymorphism--CRTP and its friends cause the template-ization forcing lots of code into headers. Smarter compilers help a bit (with de-virtualization and constexpr), but if you want to guarantee that something is resolved at compile time, turning it into a template is the only real option.

Lastly there's the inlining specter; the separate compilation model means that without link-time optimization (which has admittedly made great strides in the last few years), code must be moved into headers to be eligible for inlining. Premature optimization and all that, but this is a death-by-a-thousand-cuts situation where the language gets in the way of doing the performant thing (and you're probably only using C++ if you care about performance in one aspect or another).

None of these things is an insurmountable problem, to be sure, but they represent little inefficiencies (either for the programmer or the program) which are not generally present in more modern language implementations. I am thinking primarily of Rust, for which the static polymorphism and link time optimization stories are strong--although perhaps that is in direct reaction to some of these C++ shortcomings, and we'll discover Rust has warts of its own after a few decades of wear and tear.

By my reading that implementation was assembled by humans, not the optimizer.

Although, there's certainly nothing magical that prevents the optimizer from generating such code. Here's something[1] I just threw together that (ab)uses constexpr to do just that.

On my machine, building with:

  $ clang++ -std=c++14 fizzbuzz.cpp -O2 -S
Gives code similar to:
  main:                                   # @main
          .cfi_startproc
  # BB#0:
          pushq   %rax
  .Ltmp0:
          .cfi_def_cfa_offset 16
          movl    $1, %edi
          movl    $_ZL6output, %esi
          movl    $413, %edx              # imm = 0x19D
          callq   write
          xorl    %eax, %eax
          popq    %rdx
          retq
  .Ltmp1:
          .size   main, .Ltmp1-main
          .cfi_endproc
  
          .type   _ZL6output,@object      # @_ZL6output
          .section        .rodata,"a",@progbits
  _ZL6output:
          .ascii  "1\n2\nFizz\n4\nBuzz\nFizz\n7\n8\nFizz\nBuzz\n11\nFizz\n13\n14\nFizzBuzz\n16\n17\nFizz\n19\nBuzz\nFizz\n22\n23\nFizz\nBuzz\n26\nFizz\n28\n29\nFizzBuzz\n31\n32\nFizz\n34\nBuzz\nFizz\n37\n38\nFizz\nBuzz\n41\nFizz\n43\n44\nFizzBuzz\n46\n47\nFizz\n49\nBuzz\nFizz\n52\n53\nFizz\nBuzz\n56\nFizz\n58\n59\nFizzBuzz\n61\n62\nFizz\n64\nBuzz\nFizz\n67\n68\nFizz\nBuzz\n71\nFizz\n73\n74\nFizzBuzz\n76\n77\nFizz\n79\nBuzz\nFizz\n82\n83\nFizz\nBuzz\n86\nFizz\n88\n89\nFizzBuzz\n91\n92\nFizz\n94\nBuzz\nFizz\n97\n98\nFizz\nBuzz\n"
Of course, having now written this I feel like I should retroactively fail my last interview.

[1] https://gist.github.com/anonymous/7818f902a374a953b274

A good interview exercise (not necessarily vouching for fizzbuzz here) allows depth of discussion past the initial answer. It is in this discussion stage where I spend the majority of my time with a good candidate.

In such a discussion we might talk about whether it makes sense to make the /15 case "fall out" naturally. Using your example, there's an implicit assumption that the requirements would change to {4: fizz, 5: buzz, 20: fizzbuzz}, but in real life I've found things have the annoying tendency to change to, e.g. {4: fizz, 5: buzz, 15: fizzbuzz}.

Protothreads 12 years ago

The worst one--which to be fair, is specifically warned against on the site--is that you can't really use local variables.

The technique is useful to a point. I used a similar technique for a project and the inability to use locals threw off new hires (who usually created a bug before understanding why).

The bigger problem than educating new users though was that as the number of threads scales up (or grows in hierarchy) you burn more and more CPU time stacking/unstacking your calls just to get to the check that "whoops, this thread should still have been blocked." Between that and implementing preemption, at some point you just have to buckle down and pay for stacks.

Glib response:

:s/t:50/t:60/g

More serious response: I think after many years of training ones brain to use the features available to it some of those features become second nature and subconscious. This is great for editing and it's the reason I immediately saw that you left me a really easy way to do it, but it can make seeing the value of new features more difficult.

The descent to C 12 years ago

Fair enough. The Cortex-M's that I've dealt with have generally been homed inside some larger chip, and so the "reset pin" notion was a bit more vague, and in such an environment ARM's stance makes a bit more sense as you really want the reset to reset the "subsystem" (including whatever other random hardware you glued to yourself today).

It's also true that ARM has heard some of these complaints, which is why there were steps toward standardizing some things--like, the SYSTICK interface--in v7-M. It's really been a step up since the ARM7TDMI, and I hope that it will continue with v8-M or whatever the next revision ends up being.

Personally, when I'm dealing with a discrete chip and I need to reset it I've found that the most reliable methods that don't set off the hack-o-meter too badly are to wait for or directly invoke watchdog hardware... but yes, that's essentially always device specific.

The descent to C 12 years ago

I don't mean to be a party pooper since I've done my fair share of hacky workarounds in v7-M processors and it is always exhilarating when it works...

But since I'm a Cortex M fanboy I have to defend its reset capabilities :)

For your specific case, try something like

  volatile uint32_t *AIRCR = (volatile uint32_t*)0xE000ED0C;
  const uint32_t VECTKEY = 0x05FA << 16;
  const uint32_t SYSRESETREQ = 1 << 2;

  void take_reset()
  {
    *AIRCR = VECTKEY | SYSRESETREQ;
    __dsb();
    while(1);
  }
It's technically dependent on external hardware in your processor subsystem, but it should work if your implementer has half a brain (or, at least cares enough to read the integration manual). If it doesn't work, please flog your implementer publicly so that I can know to avoid them in the future...

Incidentally, even that tiny code snippet is uses a C extension (the __dsb intrinsic) which is either a great example of how C can be wielded to great power (I can generate raw instructions!) or how C is terribly handicapped (I need a special compiler extension or all my system code is horribly broken!). All depends on point of view, I guess...

Anyway, more info @ page 498, http://web.eecs.umich.edu/~prabal/teaching/eecs373-f10/readi...

Clearing an arbitrary bit actually is supported (as mentioned in the article: "you can set, clear, or toggle any bit with one instruction").

The specific details require you to dig a bit past explaining just the immediate encoding, but in the clearing case there's a dedicated instruction for clearing the bit specified by the immediate:

  BIC - Bit Clear (immediate) performs a bitwise AND
  of a register value and the complement of an immediate
  value, and writes the result to the destination register.
As I mentioned in my other post, zero-rotation encodings are gamed out as well (to allow byte repetition).

Thumb-2 immediate encoding is even more gleeful--in addition to allowing rotation, it also allows for spaced repetition of any 8-bit pattern (common in low level hack patterns, like from [1]) to be encoded in single instructions.

For those interested, check out page 122 of the ARMv7-M architecture reference manual[2]:

  // ThumbExpandImm_C()
  // ==================
  (bits(32), bit) ThumbExpandImm_C(bits(12) imm12, bit carry_in)
    if imm12<11:10> == ’00’ then
      case imm12<9:8> of
        when ’00’
          imm32 = ZeroExtend(imm12<7:0>, 32);
        when ’01’
          if imm12<7:0> == ’00000000’ then UNPREDICTABLE;
          imm32 = ’00000000’ : imm12<7:0> : ’00000000’ : imm12<7:0>;
        when ’10’
          if imm12<7:0> == ’00000000’ then UNPREDICTABLE;
          imm32 = imm12<7:0> : ’00000000’ : imm12<7:0> : ’00000000’;
        when ’11’
          if imm12<7:0> == ’00000000’ then UNPREDICTABLE;
          imm32 = imm12<7:0> : imm12<7:0> : imm12<7:0> : imm12<7:0>;
      carry_out = carry_in;
  else
    unrotated_value = ZeroExtend(’1’:imm12<6:0>, 32);
    (imm32, carry_out) = ROR_C(unrotated_value, UInt(imm12<11:7>));
  return (imm32, carry_out)
[1] http://graphics.stanford.edu/~seander/bithacks.html (worth a read on its own if you're into this kind of thing)

[2] http://web.eecs.umich.edu/~prabal/teaching/eecs373-f10/readi... (no-registration link)

Snake Vim Trainer 13 years ago

I don't know about "a ton" -- there are far better ways to move around a file than mashing hjkl, even with numbered versions (vim-easymotion, vim-seek). Once you can get to the right place in the file I almost never use hjkl with an action, but rather text objects or motions like ci" or dt;

Learn C 13 years ago

It's been a while since I used Gentoo, but most of the serious users consider build-from-source as a feature and don't penalize portage for that. I believe your parent is referring to the sometimes annoyingly long dependency computation time of something like emerge -uDN world.

The example definitely leaves a bit to be desired.

One great story for why this way might be better is that with a small tweak (to allow dynamic [de]registration of command functions) it is much more open to extension.

To continue using the somewhat creaky "text game" example, if possessing certain items or being in a particular room affects the available command set, this can be easily included: without a magic feather, "fly" returns "You don't know how to do that" but when you pick up said feather a new action is registered, and now "fly" works properly.

Now, I'm not sure I'd implement this particular game that way, but such an interface tends to be useful for building plugin systems, etc.

It's mainly for this reason that large switch statements, particularly those not doing "math" of some sort, tend to be a code smell.[1]

[1] http://en.wikipedia.org/wiki/Open/closed_principle

Why Use Make 13 years ago

For Unix-only and smaller projects another great option is fabricate.py

That's funny--as a python guy I was squirming for a set comprehension immediately:

  >>> a = {'a':1,'b':2}
  >>> b = {'b':3,'c':4}
  >>> def keylist(*maps):
  ...     return ', '.join(sorted({str(k) for m in maps for k in m.keys()})) or '<none>'
  ...
  >>> keylist(a, b)
  'a, b, c'
  >>> keylist()
  '<none>'
I'm sure people will have differing opinions on the readability of a nested set comprehension, but from the bullets at the top of the article to one-liner implementation in ~30 seconds. (For an arbitrary number of maps, no less.)

(edit: added '<none>' on empty, fixed formatting, whoops now I'm over 30 seconds :))

The Blink Protocol 14 years ago

In re your first bullet: If such things are important to you, it looks like you can produce fixed size packets through a variety of methods. In particular, they support overlength encodings.