Protocol Buffers by Google - <AUTHOR> http://code.google.com/p/protobuf/

<comment><USER2> Someone's bound to say "But it doesn't support RPC!"

No, it doesn't, and that's a good thing. Use ZeroMQ as your transport with a protobuff payload and do remote message passing* instead of RPC - it'll scale more efficiently than straight RPC and you won't end up having to pool connections or queue requests in your client.

*Give each message request an identifier and have each response identify what message it's answering.

<comment><USER3> A research project with which I am involved recently moved from google protocol buffers + TCP to thrift RPC, because they found that thrift was much faster. Google Protocol buffers encode the keys and field types with varints, which are basically series of bytes of arbitrary length to represent ints. If the 8th bit is set, move on to the next byte. It's pretty straightforward

      int parse(uchar *msg) {
        int x = 0;
        do {
          x <<= 7;
          x |= *msg;
        } while(*msg & 0x80);
        return x;
      }

Thrift, on the other hand, uses vanilla 4-byte ints for the same purpose. This has the effect of limited the number of fields to 2^29 (I think they use the bottom 3 bits to specify field type, but I don't remember off the top of my head). On the flip side, it's pretty instant to parse, as opposed to the looping thing.

Further, thrift makes it pretty trivial to write a thrift server/client in any language.

In short, thrift > protocol buffers.

Edit: One caveat -- Async calls WITH return values are more or less a no-go in thrift.

<comment><USER4> I suspect that this is heavily dependent upon the relative speeds of network vs. CPU, as well as the usage that these encoded messages will get afterwards. Protocol buffers are making a speed vs. space tradeoff, minimizing the size of a message at the cost of some processing power. That probably made a lot more sense when Google was young and 100BaseT was new than in the era with ubiquitous gigabit Ethernet that we've got now.

Also remember that for most production services, you'll want to log requests & responses. As soon as you start hitting the disk, increased space usage gets expensive pretty fast. You'd probably want to convert your 32-bit ints to varints then anyway, and then you've lost all the CPU that you saved in the first place.

In short, YMMV. Consider your particular use-case carefully before making any blanket judgments of thrift > protobufs or protobufs > thrift.

<comment><USER3> You're talking about saving 3 bytes per int. I'd imagine it's pretty hard to find a workload where you can achieve 75% compression by using exclusively varints that are less than 128 in magnitude. In fact, I'd imagine that most of the data shipped in most workloads is mostly strings/binary, which have very little if any savings.

So let's talk about the ideal situation where we have exclusively fields that are varints that are limited to 128. This means that each will be 2 bytes (1 for field data + 1 for the varint itself, both of which are under 128 in magnitude). How many of these can we fit? Well, if we use the bottom 3 bits for specifying the type (a conservative estimate) that leaves only 4 bits (remember, subtract 1 for varint or not) to ID them, which means 16. So if a message has 16 fields and we save 6 bytes per request, we're talking a savings of 96 bytes. For the next 2048 entries, we're saving, 5 bytes per (5kb vs 15kb), then beyond that, we're saving 4 bytes per (and eventually none). This is also an incredibly arbitrary situation where you expect ALL of your fields to be small ints.

Same with disk, it's unlikely that your workloads will allow you to save a lot by using varints.

So, you're right that, for some workloads, this may be better, but if someone put a gun to my head and said "protocol buffers or thrift?" I'd have to go with thrift.

<comment><USER5> Going to reply to all your comments at this rate. ;-)

Even with single-byte tags, the overhead can be quite significant; thus the packed = true option. Unfortunately I don't have any public protocols or data to show you, but by switching to packed = true (combined with some extra tricks) I achieved ~40-60% savings on some of our datasets. We use protobufs for our stable storage, and this change certainly saved us more than my yearly compensation package.

> For the next 2048 entries

Yikes! I hope you never have to work with a message type with anywhere near 2048 fields! :-)

In practice, more than half message types have less than 16 fields, which is why the "incredibly arbitrary situation" is justified, and really does end up saving a ton of storage. But, for simplicity, nothing can beat just ntohl and a cast.

</comment></comment></comment></comment></comment><comment><USER6> Protocol buffers are great, but the Python implementation provided by Google is written in native Python and very slow for serializing/deserializing protobufs (see <http://news.ycombinator.com/item?id=767882>).

I've heard you can get around this by SWIG-wrapping the C++ implementation, but I haven't gotten around to trying it yet.

</comment><comment><USER7> A great use case for Protocol Buffers or Thrift is adding schema to other technologies which just use raw byte arrays, like Hadoop files, Cassandra columns, and Gearman parameters

</comment><comment><USER8> I've recently moved to protocol buffers and am quite happy with the results. Before that, we had a few tomcat apps with ad-hoc urls that return xml responses. Since we already have a good http request/response channel, I was able to create a java bridge for the message objects, as well as a simple python library that can communicate with all our servers.

While there was a little overhead in getting it integrated into the build system, it works great, and gives a much better interface, and much better control for messaging individual servers.

The project itself is also very well put together. Hell, even the autoconf generated makefile has targets like "uninstall", which seems to be a rarity when building packages from source.

</comment><comment><USER9> Another good alternative is msgpack at <http://msgpack.org/>

It has it's own rpc mechanism as well that competes with thrift, although it is not IDL-based (however, their IDL is coming soon).

</comment><comment><USER10> See also Apache Thrift (from Facebook) and Caucho Hessian

<comment><USER11> There's a (pretty) good comparison of Thrift to Protobuf on <http://wiki.github.com/eishay/jvm-serializers/>

I think Thrift supports a lot more languages, but based on benchmarks, I believe it does perform (sometimes significantly) slower

I've never used Hessian though, will have to check it out at some point.

</comment></comment><comment><USER12> What sort of versioning capability does this give? That's the main reason to bother using something like this.

<comment><USER5> Both protocol buffers and thrift are primarily designed to handle older or newer messages (than your program was compiled with) gracefully. Speed and compactness appear to be secondary goals. (Especially for Thrift, which does not seem concerned with compactness.)

Rather than version your protocol, the generated code is able to parse newer messages. If you modify a field on that message and serialize it, even the parts of the message your binary is unaware of will be retained. (As opposed to dropping them.) This means you don't need to push all changes at once, and you can have binaries talk with older and new binaries without problems.

The basic wire format is [(tag, payload bytes)*]. The tag identifies both the field "number", and the wire-type of the field. Wire types include fixed 32 and 64, length-delimited ranges, and, for protos, variable length integers. The parser knows how to skip fields of all wire types, so any unknown tags are skipped (and retained in memory) during parsing.

Some caveats apply -- you can modify your protocol in several backwards- incompatible ways. For example: add a new required field. Your new binary will reject all serialized messages from your older binaries. Alternatively, change the type of a field. These are pretty easy to avoid, but given enough people on your team, it is bound to happen once or twice.

</comment></comment><comment><USER13> I use rabbitmq for my android app. It's just a matter of sending a byte array between the client and the server. Then it's just a matter of reading the bytes into the appropriate fields. (both server and client) This may be a bit of work but it's really fast and efficient.

<comment><USER5> After several releases of your app, you'll have several wire formats in the wild. One of the main features of libraries like Protocol Buffers is that they can parse old messages, so you don't need to maintain an increasingly hairier parsing routine.

<comment><USER13> Well, I deny access to the old clients. I consider this an advantage because I force users to keep up to date. If you're careful you can just add items to the end of the stream and the old clients will work just fine. I don't support this though, but I do it in practice.

But yeah, protobuf wins in that domain. Specially if it avoids a high serialization/deserialization overhead penalty. It'd be cool to use rabbitmq to route the protobuf objects around with a cleaner interface.

Shameless plug for my dev blog: <http://developingthedream.blogspot.com/></comment></comment></comment>


SeaMicro builds box with 512 Atom CPUs - <AUTHOR> http://gigaom.com/2010/01/06/seamicros-secret-server-changes-computing-economics/

<comment><USER2> I'm not really sure what real need they're trying to fill here. A single Atom core has a TDP of around 4W, performance-wise it's at about 10% of a mid range Core2 Duo, which has a TDP of around 65W, although the mobile versions are much more efficient than that (35W or so). To get 10 Atoms (or 5 if they're N300 series dual-cores) running must take much more infrastructure than a single Core2, which consumes power as well, so I doubt they're getting more FLOPS/Watt or integer ops/Watt than a Xeon or Opteron cluster.

So the intention must be maximising I/O. What sort of workloads are so shared- nothing that they can parallelise to this many non-shared-memory CPUs efficiently? Content Delivery Networks? Seems incredibly niche; niche enough that the CDNs probably have already built their own.

And what exactly is the I/O bottleneck on a Xeon system that a bunch of Atom systems can do better? FSB/Memory throughput maybe? The Nehalems already have a 192-bit, 1333MHz DDR3 memory interface per CPU and gigantic caches, along with I/O that doesn't share data paths with memory accesses.

<comment><USER3> Frankly with the Niagra out there, this seems partially redundant. Then again, making I/O tasks, essentially a chain of closures, into reasonable threads is a pretty bad waste by itself...

Yup these atoms are essentially I/O processors, just running enough buffer cache management, filesystem, and driver code required to keep other components (network and disk) at high utilization.

The benefit for atoms here is the short latency. They have short pipelines (better for the little actual computation required for I/O driving), and there are presumably more cores available here than the equivalent Xeon system (reducing queuing delays).

<comment><USER4> I think SeaMicro is supposed to be 1/Nth the price of Niagara, and it's x86 so you don't have to recompile.

</comment></comment></comment><comment><USER5> I'm having trouble understanding what the market for this is. It's not going to win on instructions-per-die-area-per-second (NVIDIA and ATI are already ahead of this mark now, with hardware rather cheaper than $100k). And with 512 distinct CPU packages, there's no way the interconnect is going to be faster than the high speed serial stuff we're already using for stuff like SATA, 10G ethernet, Infiniband, etc...

So it's basically a physically smaller supercomputer running low-power CPUs. It probably wins on real estate and power metrics, and probably loses on cost vs. racks of consumer stuff. Is there a market for that? Note that the investment came not from a VC fund, but from the DoE...

<comment><USER4> I think SeaMicro is targeting commercial workloads, not HPC; that immediately excludes GPUs as competitors. It could be somewhat cheaper and lower power than a Nehalem cluster, but I also don't see anything revolutionary.

Their patent applications have some technical details: <http://www.pat2pdf.org/pat2pdf/foo.pl?number=20080320181> <http://www.pat2pdf.org/pat2pdf/foo.pl?number=20090216920> It looks kinda like a cheap x86 Blue Gene...

</comment></comment><comment><USER6> Wouldn't this be similar to what Google does, large amounts of average CPU's?

<comment><USER7> "But both of these firms are going against what is currently the biggest trend in corporate data centers: commodity servers. Such boxes aren't simply a collection of low-power chips — they have to be networked from inside in order to deliver optimal performance for the lowest power consumption"

</comment></comment><comment><USER8> 512 CPUs with no ECC? Ugh.

</comment><comment><USER9> I dunno, you've got to have pretty serious scale to have a 512-CPU outage not hurt at least a little bit.</comment>


An adventure in trying to optimize math.Atan2 with Go assembly - <AUTHOR> http://agniva.me/go/2017/08/27/fun-with-go-assembly.html <comment><USER2> Although this post is interesting, the best way to optimize trig functions is generally via rational approximations[1] if you can live with the error (and the clamping). I remember finding a rational function to approximate a tanh- like soft clipper via a Padé approximator on (-1, 1) a few years ago[2]. See an equivalent rational approximation for atan2 here[3].

[1] [http://www.embedded.com/design/other/4216719/Performing- effi...](http://www.embedded.com/design/other/4216719/Performing-efficient- arctangent-approximation)

[2] [https://stackoverflow.com/questions/6118028/fast- hyperbolic-...](https://stackoverflow.com/questions/6118028/fast-hyperbolic- tangent-approximation-in-javascript/6118100#6118100)

[3] [https://www.dsprelated.com/showarticle/1052.php](https://www.dsprelated.com/showarticle/1052.php)

<comment><USER3> A bit OT but my favorite way to approximate tanh is to do g(f(x)) where f(x) is an odd polynomial and g(x) is 1/sqrt(1 + x^2). The recip sqrt can be computed very quickly using Newton approximation, and many SIMD instructions have an instruction for it. Highly recommended for audio applications.

<comment><USER4> Something is definitely off about your recollection here. _g_ ( _x_ ) = 1/√(1+ _x_ ²) is always positive, whereas tanh( _x_ ) is negative for negative values of _x_. So regardless of your _f_ , _g_ ( _f_ ( _x_ )) can't ever be a correct approximation for negative _x_.

<comment><USER5> Looks like grandparent probably meant g(x) = x/√(1+x²)

Looks like f(x)=x+x^3/5 gets you two decimal digits everywhere [1], and you could improve that with more terms in the polynomial.

Edit: you can derive f(x) by doing a series expansion of tanh(x)/√(1-tanh(x)²) about x=0, and Mathematica tells me the first few terms are

f(x) = x + x^3/6 + x^5/120 + x^7/5040 + O(x^9)

[1] [https://www.desmos.com/calculator/zbyll5oeqk](https://www.desmos.com/calculator/zbyll5oeqk)

<comment><USER3> Oops, yes, you're correct, I was typing from memory and didn't double-check.

And yes, the Taylor series is pretty good but you can usually squeeze a bit more accuracy-over-range by, eg, using an optimizer for the coefficients. Also, note that in your notebook you have x^3/5 but x^3/6 is probably what you wanted.

</comment></comment></comment></comment></comment><comment><USER6> It should be noted that if you are computing sin/cos after atan2, then it's likely that what you are doing could be done with some regular vector manipulation (normalisation, possibly dot product, cross product).

<comment><USER7> Not really. My algorithm just needs atan2.

Its a flight tracking algorithm which calculates velocity of the aircraft from the messages it broadcasts.

EDIT: > then it's likely that what you are doing could be done with some regular vector manipulation (normalisation, possibly dot product, cross product).

Yes, totally. Like I said, I have zero knowledge in assembly. And what you are talking about "vector manipulation" is totally beyond me ;) Just doing this part took a lot from me. :D

Maybe someone knowledgeable enough will be kind enough to write an improved implementation.

<comment><USER8> If it just needs atan2, it is not in the category described by the comment that you're replying to.

<comment><USER7> Sorry, I might have misunderstood something. But OP said - " if you are computing sin/cos after atan2,", and I don't need any sin/cos computation after atan2. Just the atan2.

<comment><USER9> Right, so your situation doesn't fit the condition being described.

</comment></comment></comment></comment></comment><comment><USER10> The assembly version is using the packed version of the FMA instruction (that's what the "P" in the mnemonic stands for), but as far as I can tell, it's only using one of the packed values, whereas the instruction can calculate two (AVX) or four (AVX2) FMA operations at once. It might be possible to get some speedup by rearranging the calculation so it can use the full width of the vector registers - at first glance, at least the two sides of the division should be possible to calculate in parallel with half as many FMA instructions.

</comment><comment><USER11> If anyone was curious why the instruction address didn't match up, it's because he read the instruction identifier wrong. It's also totally counterintuitive and you have to essentially read the full documentation to get to any real information about it. Anyway here we go. VEX is a prefix notation with specialised encodings:

C4 - For a three-byte instruction (which vfmadd is) this is C4, if a two-byte instruction this would be C5.

E2 - This one is more involved but basically for this instruction the first three bits are 1 for setting some modes on it (R, X, and B). Then, because this is an 0f38H instruction, the next 5 bits are 00010. Altogether you get 11100010 (E2)

E9 - this is also involved, but the first bit is a W mode. This is 1 for the instruction he used but it's pretty much ignorable. for bits 2-5 it has to do with the first source register the instruction uses. This is 1101 because it uses XMM2/YMM2 (it's by lookup in a table) first. bit 6 is set to 0 if it is a 128 bit vector (it is). bit 7-8 are set to 01 based on a lookup table for the "pp" value which is 66 in the instruction identifier. Altogether that's 11101001 (E9).

A8 - this one is easy, it's the opcode.

C3 - this is the ModR/M byte which is actually used in this case for reporting the register/memory operands. first 2 bits are 11 to indicate the first operand is the register itself (not displaced, or truncated). The next 6 bytes are the register codes. 000 is actually not used since the destination is overriden for float maths), and the 011 is EBX as the base cpu register to source the data.

Altogether it works out to be C4E2E9A8C3. Not intuitive at all really.

Edit: Please someone correct me if I missed something. I hate this kind of stuff and I'm sure I made a mistake.

</comment><comment><USER12> > "The reason for optimizing the math.Atan2 function is because my current > work involves performing some math calculations. And the math.Atan2 call was > in the hot path. "

He should take a look at:

a) the range of x which will be used for atan2(x)

b) the level of precision he really needs

There is a chance he could get away with precomputing an atan2(x) table in memory and then just reading the values off the table.

Quick as lightning, and an old trick, probably invented in the 1950s!!

EDIT: I see i am potentially _wrong_ , due to slower DRAM access compared to some built-in FP instructions...

<comment> Const-me > i am potentially wrong

Not just potentially. See this: [https://github.com/Const- me/LookupTables](https://github.com/Const-me/LookupTables)

<comment><USER12> Great link, you should submit it to HN.

<comment> Const-me Did that already, everyone ignored that.

[https://news.ycombinator.com/item?id=12724732](https://news.ycombinator.com/item?id=12724732)

</comment></comment></comment></comment><comment><USER13> Not to be mean to the author, but this is an example of why it's generally useless to try to resort to assembly to beat a compiler unless you're an expert at assembly. The sort of case that's being optimized here--a straight- line piece of assembly--is the case where compilers are going to do better than hand-coded except in specific cases.

The first critical failure is using the source code to guide assembly optimization, rather than the original generated assembly. It means that you're going to miss what optimizations the compiler is doing to improve this, and missing those optimizations is going to turn out worse than the original.

There are two main optimizations that come to mind. The first is instruction scheduling. On newer CPUs (Haswell or later), you have two separate instruction ports for vector ALU instructions, so you can execute two instructions in parallel if there are no conflicts. If you interleave the instructions for computing the denominator and the numerator, you can take advantage of the twin execution ports and execute two instructions at once instead of two (you use more registers though). This is the main optimization that I'm sure the Go library is getting.

The other possible optimization is SLP vectorization: the numerators and denominators are both polynomial interpolation, so they're doing the same operations on data vectors. This means that you can stick both of them in the vector and use one instruction to compute the operation for numerator and denominator simultaneously. On the other hand, it's not immediately clear if the extra cost in vector formation is going to be worth the win in speed, particularly given that you can already simultaneously schedule the two instructions in the first operation. I don't know if Go is applying this optimization.

So if you're wondering why it's slower, it's almost certainly that the hand- written assembly is actually poorly scheduled.

<comment><USER2> > So if you're wondering why it's slower, it's almost certainly that the hand- > written assembly is actually poorly scheduled.

The assembly is slower because Go doesn't inline it. The overhead is obvious when calling one-op assembly blocks[1]. The proposal to inline assembly was rejected last year[2] by Russ Cox.

[1] [https://lemire.me/blog/2016/12/21/performance-overhead- when-...](https://lemire.me/blog/2016/12/21/performance-overhead-when-calling- assembly-from-go/)

[2] [https://github.com/golang/go/issues/17373](https://github.com/golang/go/issues/17373)

<comment><USER14> Thanks for the link to the issue. I was wondering why the post was talking about function call overhead instead of being able to inline it.

</comment></comment></comment><comment><USER15> I dabbled a little in gaming with Unity. I discovered that atan2 is called a LOT. At least... I used it a lot.

Shouldn't something like this be implemented as a hardware level operation? Or is it? Or why can't it be? Or maybe it's not used nearly as much as I think?

<comment><USER16> I think in the case that you're willing to trade some accuracy for speed, you'd just use a table lookup instead.

<comment><USER17> Not necessarily. That's OK if you can guarantee that the table is in cache. But if it isn't, that's probably the slowest way.

DRAM read takes ~210 cycles (assuming 60ns access time and 3.5 GHz CPU clock rate).

FPATAN takes ~50 to 200 cycles, depending on the operand value and CPU model (see [http://www.agner.org/optimize/instruction_tables.pdf](http://www.agner.org/optimize/instruction_tables.pdf))

I suspect a polynomial approximation takes less than either of these, since that's what most C standard library implementations use on the PC (I just check Visual Studio 2017).

<comment><USER18> > But if it isn't, that's probably the slowest way.

It's probably the slowest way the first time, but if you call atan2 in a loop, won't it be much faster?

<comment><USER17> It isn't just "the first time" that is slow for the LUT solution.

Each time the table access hits a new cache line, there will be a stall. Say your LUT has 2048 64-bit entries, that's 256 cache lines. So the total stall time waiting for cache lines to be filled could be as bad as 210 cycles * 256 = 53760 cycles. In that amount of time, the std library function could have returned about 1000 results (assuming the std library takes 50 cycles).

And, as well as waiting for the table reads, the LUT based function has other work to do. If we are comparing performance to the std library function, it handles input from the entire range of IEEE 64-bit floats. It isn't practical to do that directly with a lookup table. You would need to clamp the range down to something manageable. Once that's done, if you want anything like decent accuracy you need to read two values from the table and do a linear interpolation between them. Or you could have a HUGE table instead of the lerp but it would have to be far too big to fit in L1 cache, and thus also quite slow.

My guess is a clamp and lerp'd lookup table will take ~10 cycles once the table is entirely in cache. So, I guess I'm claiming that the LUT would then be 5x faster than the std library.

I don't have a lot of confidence in that prediction though. There are lots of other things to worry about if we were to do this analysis properly, including: a) how the input pattern alters the effectiveness of the branch predictor, b) how exactly you are finding some input data to operate on without polluting the cache, c) whether we are measuring latency or throughput.

</comment></comment></comment></comment></comment><comment><USER19> Wow, this brings back vivid memories of late 80's, when I was assigned to write atan2 by hand on Multiflow's 28/14/7-wide VLIW machines.

(Everyone in the company who could hand-code (even us OS guys and gals ;-) was assigned a math routine--it was an all-hands-on-deck kind of performance situation in our competition with Alliant and Convex.)

Imagine trying to hand-schedule up to 28 operations at once, including all memory loads with 12 or 14 (forget now) clocks to memory, all bus contending had to be done by hand, etc, etc. What fun! That's why VLIW compilers were so challenging to write: all resources software-managed.

But, alas, in the end the mini-super market died. We all lost to Sun eventually, as everyone wanted his own machine, even if much less powerful-- never bet against having the computer power closer to the user (starting with mainframes): minicomputer / workstation / PC / mobile / etc, right down the line to today.

(What's going to displace mobile, IoT? Doesn't seem likely. Perhaps when you reach the limit of what you can hold in your hand, that's the last form factor? I don't believe wearable computing will ever really catch on, but I'm old fustian.)

<comment><USER20> > I don't believe wearable computing will ever really catch on, but I'm old > fustian.)

I used to think the same, but it seems to me that phones are the first step. A huge number of people have a mini computer on their persons all day. It's only a matter of time before it becomes even more integrated.

Will be interesting to see, but I doubt the current form factors will win. Smart watches seem so... not it.

<comment><USER21> And also, with wearables it's not so much bringing _a_ device closer, but a _personal network of devices_. The phone may still have it's place as the larges person-network computing device, but peripherals with their own small compute power and additional I/O get added to the network. Smart watches (biometric inputs, small always ready screen output), glasses (AR overlay output, line-of-sight and image recognition input), etc.

</comment></comment></comment><comment><USER22> I always find it interesting to read stories of experiments that didn't end up working.

<comment><USER23> IMO they are more important, because the OP learns more, and you acquire part of the experience without investing as much time.

</comment></comment><comment><USER24> Dang. Props to the blogger for posting even though success wasn't had. Hours on end doing assembly is a feat unto itself. I did not see the twist at the end, I was thinking for sure there'd be a huge speed up. But knowledge is knowledge.

<comment><USER7> Haha. I know. My heart was pounding when I started the final benchmark .. hoping against hope that there will be some gains.

But yea, knowledge is knowledge.

</comment></comment><comment><USER25> I would love to see more posts like this. Seeing your thought process when approaching a problem, even if you don't end up solving the problem, or don't do it well, is valuable insight into how other people work, and how I can possibly integrate some of your methods to my own workflow.

Thanks for posting this.

<comment><USER7> You're welcome ! Glad it was helpful. :)

</comment></comment><comment><USER26> I'm surprised it doesn't eliminate the BenchmarkAtan2 entirely:

* math.Atan2 is in the standard library, so it should know it has no side effects and can be replaced with an empty statement. Unless Go has a way to mark a function as not having side effects, no user-defined function would have that property.

* The only observable impact of the loop if math.Atan2 is removed is that b.N could be a larger size type than n, so it could loop forever depending on the values. If they're known to be the same type, it could eliminate the loop entirely.

Are you sure you're compiling with optimizations enabled?

I also wonder if that DIVSD instruction in yours might cause a floating point exception, which would be a different behavior compared to atan2.

<comment><USER7> Author here.

> I'm surprised it doesn't eliminate the BenchmarkAtan2 entirely:

I'm sorry, could you clarify what you mean by "eliminate" here. Are you saying I shouldn't benchmark atan2 but something else ?

> Are you sure you're compiling with optimizations enabled?

I just did - `go test -bench=. -benchmem -cpu=1`. Did I miss something ?

> I also wonder if that DIVSD instruction in yours might cause a floating > point exception, which would be a different behavior compared to atan2.

I checked with the assembly output of math.Atan2. It also uses DIVSD.

<comment><USER27> > I'm sorry, could you clarify what you mean by "eliminate" here.

Most compilers will eliminate code if they can tell the results of a calculation are never used. Benchmarks typically call some function and discard the result. If you aren't careful, the compiler can often optimize away your entire benchmark down to nothing.

This is why you often see benchmarks that look like:

        int average(a, b) {
          return (a + b) / 2;
        }

        main() {
          int sum;
          for (int i = 0; i < 100000; i++) {
            for (int j = 0; j < 100000; j++) {
              sum += average(i, j);
            }
          }

          if (sum != 123) printf("!");
        }

Instead of:

        int average(a, b) {
          return (a + b) / 2;
        }

        main() {
          for (int i = 0; i < 100000; i++) {
            for (int j = 0; j < 100000; j++) {
              average(i, j);
            }
          }
        }

<comment><USER7> Ah I see ! Gotcha :)

Yes, it seems Go is smart enough not to eliminate those values entirely.

I think this might be a better way to be sure that the function will be computed entirely.

`_ = myatan2(-479, 123)`

The result is same though. I will update the post.

<comment><USER28> Even better is to make a global var sink interface{}, and assign to it. [https://github.com/golang/go/issues/14813](https://github.com/golang/go/issues/14813)

</comment></comment></comment></comment></comment><comment><USER29> I'm no optimisation expert, but I'm wondering if the FMAs are slow because the result of each one is dependent on the previous one? The dependency on the result may mean that the processor can't pipeline the operations. Could it be faster if the two chains of FMAs on either side of the division are interleaved and use different registers?

z := x * x

z = z * fma(fma(fma(fma(P0, z, P1), z, P2), z, P3), z, P4) / fma(fma(fma(fma((z+Q0), z, Q1), z, Q2), z, Q3), z, Q4)

z = fma(x,z,x)

This article is quite the nerd-snipe.

<comment><USER30> It will definitely be faster on the newest intel processors, which have dedicated FMA units. In fact, to max out floating point on the things, you'd likely have to intermix FMA into the normal FP stream (IE FMA by 1 or FMA of something plus 0)

</comment></comment><comment><USER31> In C/C++ it would be much easier to call the intrinsic functions which represent the fma instructions. The compiler would then be able to inline and optimize the whole.

<comment><USER32> In C or C++ you should be able to use the stdlib `fma( )` function and have the compiler lower the call to the instruction, if it isn't completely out to lunch. No intrinsics should be necessary.

Alternatively, set `#pragma STDC FP_CONTRACT ON` and just write `x*y + z`.

<comment><USER31> I didn't know they added that. The intrinsic could be useful to parallelize the computation using SSE/AVX.

</comment></comment></comment><comment><USER7> Author here. Glad to answer any questions you guys have :)

</comment><comment><USER33> I think the primary thing you are missing to get the speed improvement is actually doing the packed computations (e.g. computing two values at once; e.g. the numerator and denominator).</comment>


The iRing - <AUTHOR> http://design-milk.com/iring/

<comment><USER2> Leaving aside how technically feasible this is (and I suspect you'll have a hard time miniturising the electronics enough to fit the form factor of the ring), it's not a bad idea.

The only issue I'd have with it would be that it's not quite natural to touch the back of your knuckle - to me, anyway, it's a slightly awkward movement.</comment>


Facebook continues on... - <AUTHOR> http://plentyoffish.wordpress.com/2007/03/27/facebook-continues-on/ <comment><USER2> It's not hard to imagine facebook as the:

#1 social network #1 dating site #1 photo sharing site #1 news aggregator #1 job search site #1 classified ad site #1 blog site

With that much (voluntarily provided) user data, there are a lot of markets they could dominate. If you could figure out what each of those people was buying, you'd be pretty close to the holy grail of market research.

<comment><AUTHOR> Sounds like what Yahoo and other portals tried to do and didn't really succeed. Facebook is like the same play coming from a different angle, except this time they own all your friends :-).

</comment></comment><comment><AUTHOR> Always amusing how Markus looks at everything in terms of dating. Facebook doesn't seem optimal for dating, I have been thinking of how you could use the Facebook API to do dating properly but didn't come up with an amazing approach.

It has some interesting facts on fb growth too.

</comment><comment><AUTHOR> Also I can't wait till Facebook is actually beating Myspace. I just dont get the motivation/mind of myspace users properly, facebook is a lot easier to understand (this is purely on an academic/entrepreneurial level)</comment>


Infographic – how to turn your idea into a prototype in 5 steps now - <AUTHOR> http://blog.venturepact.com/5-ways-to-turn-your-idea-into-a-prototype-infographic/ <comment><USER2> I don't see how these are supposed to be steps.

These are just "here are some things that you can use in your webservice product that you don't have to make yourself."

And, ok, that could be useful I guess.

But it doesn't seem like a list of steps.</comment>


The Mind Bleeds into the World: A Conversation with David Chalmers - <AUTHOR> https://www.edge.org/conversation/david_chalmers-the-mind-bleeds-into-the-world <comment><USER2> For anyone interested in this, I would highly recommend "The Rise of Human Consciousness" panel discussion hosted by the New York Academy of Sciences with David Chalmers, Michael Graziano, Hod Lipson, and Max Tegmark. They discuss consciousness from the perspectives of philosophy, neuroscience, and robotics, it's 1h30m and an extremely good discussion.

[https://livestream.com/newyorkacademyofsciences/physics4](https://livestream.com/newyorkacademyofsciences/physics4)

</comment><comment><USER3> With regard to Chalmers emphasis on simulation: I think simulation is really a central point in AI and future human development. Not only that we crave the web and the VR, but AIs also need VR for virtual training. It is sometimes costly and other times impossible to encounter certain specific situations in real life, but agents need to be prepared. Human pilots train in simulators as well - for the same reason.

So simulation for AI agents is like school, or a lab where they can experiment. Simulation, in my opinion, is what can turn simple DL systems into agents capable of deep reasoning and creativity. AI will learn mostly from analysing and discriminating in the results of multiple simulation runs.

Gaming, VR and and DL run on the same kind of hardware - GPUs. In fact DL has benefited from the availability of gaming GPUs in the last five years. The same hardware, the same emphasis on simulation for both fields. Other simulation examples: AlphaGo did MCTS on top of neural nets (a simulation of Go playing), and self driving cars reconstruct a virtual scene of the world, where they do path planning and risk avoidance.

In my opinion, reasoning is also a simulation task - once we map into a graph all the relevant entities and the relations they hold between them, it is a matter of doing signal processing on graph (kind of like electronic circuits) to determine the answer for a number of reasoning tasks. So graph based, object relation representations could be used as cognitive simulators. DeepMind has a paper recently on graph based NNs.

Simulation might look different from games to weather, proteins and cognitive reasoning, but in all these cases we input a complex problem state and run the simulation to observe the outcome. We can do research and learning by this process.

</comment><comment><USER4> I like Chalmers because he's not a dualist in the traditional sense. I tend to disagree with the ideas, but take that with a grain of sand because I'm not a philosopher. For instance, I find the zombie argument kind of contrived. That's me though.

Personally, I'm a strict physicalist. I don't even think my own consciousness is magic, much less other matter in the universe.

</comment><comment><USER5> The idea that we may be living in a virtual reality someone created is a disturbing one. It doesn't matter if objects around us are fundamentally real or simulated - that's true. What matters is our minds may be simulated too. What if that simulation has some deep flaws in it? What if there are things we could never understand no matter how we try because of that?

<comment><USER6> Refer to Yoga Vashishta:

The idea that we might be living in someone else's dream or meditation was expressed in there. And the worlds are all dreams stacked within dreams. [0] [1]

Also Jung, that we might be living in someone's else's meditation and when the meditator wakes up all is gone.

"I had dreamed once before of the problem of the self and the ego. In that earlier dream I was on a hiking trip. I was walking along a little road through a hilly landscape; the sun was shining and I had awide view in all directions. Then I came to a small wayside chapel. The door was ajar, and I went in. To my surprise there was no image of the Virgin on the altar, and no crucifix either, but only a wonderful flower arrangement. But then I saw that on the floor in front of the altar, facing me, sat a yogi in lotus posture, in deep meditation. When I looked at him more closely, I realized that he had my face. I started in profound fright, and awoke with the thought: "Aha, so he is the one who is meditating me. He has a dream, and I am it." I knew that when he awakened, I would no longer be" [2]

An interesting take on the Yoga Vasishtha and the Philosophy of the film Inception.[3] Note: I do not have any affiliation with the organization or teachings etc., It's just a random link I googled, but thought can give a quick summary of the book without having to read through the whole book.

[0] [https://en.wikipedia.org/wiki/Yoga_Vasistha](https://en.wikipedia.org/wiki/Yoga_Vasistha)

[1] [http://www.venkatesaya.com/241_vasistha01_months_tags/index....](http://www.venkatesaya.com/241_vasistha01_months_tags/index.vasistha01_months_tags.php?d=9&m=3)

[2] Memories, Dreams, Reflections - Carl G Jung

[3] [http://www.that- first.com/show/article/yoga_vasishtha_and_th...](http://www.that- first.com/show/article/yoga_vasishtha_and_the_philosophy_of_the_film_inception/)

Edit: Wikipedia link

<comment><USER7> Also the famous 'butterfly' passage in the Chuang Tzu[1]

1: [https://books.google.com/books?id=_BqCCgAAQBAJ&lpg=PP1&pg=PA...](https://books.google.com/books?id=_BqCCgAAQBAJ&lpg=PP1&pg=PA31#v=onepage&q=butterfly&f=false)</comment></comment></comment>


AngularJS 1.3: a new release approaches - <AUTHOR> http://blog.angularjs.org/2013/12/angularjs-13-new-release-approaches.html <comment><USER2> The worst part of 1.3 (well, anything > 1.2.2) is they're removing comments from their documentation. The docs are already abysmal; now they're removing the only part of the docs that was decent (since people would post solutions to common problems).

Although, to be fair, half the Disqus comments were just complaining about how bad the docs were.

[http://blog.angularjs.org/2013/11/farewell- disqus.html](http://blog.angularjs.org/2013/11/farewell-disqus.html)

<comment><USER3> Angular's Disqus comments were a perfect example of the hate and entitlement in open source that makes people quit contributing. While they're not the greatest docs, they're quite passable, especially with the rest of the info on the web (videos explaining concepts, egghead.io, etc). On top of that, it is really easy to contribute. I saw a few small doc issues, fixed them, and they were merged within 6 hours. If people could be more civil and constructive, comments would be fine, but I'm glad they're gone.

<comment><USER4> > If people could be more civil and constructive, comments would be fine, but > I'm glad they're gone.

So it's better to just ignore the problems with the documentation? I barely have time to learn the framework—the lack of good documentation means I delete it from my hard drive. No matter how good a framework is, if it's not documented well, it's a buggy black box.

Even criticism that doesn't offer a solution is better than a lack of feedback. Besides, it's not like Google will get all self conscious and stop contributing because people don't think the documentation is good.

<comment><USER5> > I barely have time to learn the framework.

If you can't be bothered to deeply study and understand your tools then you probably don't deserve to use them anyway.

<comment><USER6> Yeah totally. And if you can't be bothered to read your car's owner's manual from cover to cover you probably don't deserve to be driving it anyway.

<comment><USER7> Actually, you should probably read the Chilton (or equivalent) manual cover to cover.

</comment></comment></comment></comment></comment></comment><comment><USER8> If you work with the K-12 system in the US, you'll discover that a lot of your customers are still happily chugging along with IE8.

<comment><USER2> And they'll have no reason to change if websites continue to cater to them.

<comment><USER9> I don't think the K-12 system is going to upgrade because AngularJS is dropping support for IE8. I mean, I don't think it'll be a major factor in their decision. Probably funding will be a big factor. Then AngularJS.

<comment><USER10> But they will change soon enough. It is just that IE8 is the ceiling of Windows XP, which is end-of-life in 4 months. If that does not convince, the bad press of all the vulnerabilities that follows, will.

Another driver: ipads and chromebooks are taking over.

</comment></comment></comment></comment><comment><USER11> For the lazy, Angular 1.3 drops IE8 support. This is a potentially big problem for my company, since we have a big contract that stipulates IE8 support. Maybe this is the final straw that gets the client to drop IE8 support (which would be nice), but I suspect this will stymie us from upgrading further for these particular projects.

<comment><USER12> More particularly, they are stopping automated testing of IE8 in their CI system. However, they specifically note that they are happy for the community to continue IE8 support, and expect those with significant dollars that still need IE8 support to join together and keep up the IE8 support. Maybe your client that needs IE8 support is willing to pay extra to keep it, and you can be a part of the solution!

</comment></comment><comment><USER13> Dropping IE 8 support is tricky. For those who produce web apps for use in the enterprise arena, IE 8 can be a significant part of your user base. In the last month, 15% of our users accessed our site via IE 8. As such, it may be another year, or so, until we can take a serious look at Angular. That, of course, assumes that twelve months hence they don't drop support for IE 9.

</comment><comment><USER14> I guess this was inevitable with Microsoft dropping support for Windows XP.

<comment><USER15> Inevitable and good. I'd much rather the Angular team spend time on features and bugfixes for mainstream browsers than supporting a 54-month-old browser version.

</comment></comment><comment><USER16> Too soon. There are third party components still struggling to update to 1.2.

<comment><USER17> I just had to update a bunch of directives for 1.2, so I see where you're coming from, but Angular is still young enough that I'll welcome some more major changes if it makes it a better framework in the long-term. It's frustrating to be an early adopter, but I'm loving it enough to bite the bullet.

I believe I saw some slides a while back about their long-term roadmap which made these changes seem relatively minor. If they get around to everything they want to, Angular will be very different across the board in a few years.</comment></comment>


As the World Melts, an Artist Finds Beauty in Ancient Ice (2018) - <AUTHOR> https://www.newyorker.com/tech/annals-of-technology/as-the-world-melts-an-artist-finds-beauty-in-ancient-ice <comment><USER2> Super interesting. Makes me wonder how much history we'll miss from areas that never freeze. Layers of sediment seem so much more fragile than ice.

On the off chance the author sees this, I enjoyed the article but the overuse of commas did make it a little hard to read.</comment>


Ask HN: effects of surveillance - <AUTHOR>

Have you noticed any differences in how you think / speak / act over the past week? Have you caught yourself thinking about surveillance before you said / posted something? <comment><USER2> No - I assumed that I was under surveillance based on the number of security cams I see walking around downtown Denver, and things I'd already read about ECHELON and "Stellar Wind". Also, the visible security state seemed all too willing to do stupid, stupid authoritarian things (anything the TSA does at airports), so why not a dragnet surveillance?

I bet the NSA gets a lot of good porn, phone sex and celebrity gossip that way.</comment>


The Dutch government has taken over operational management from DigiNotar. - <AUTHOR> http://www.govcert.nl/english/service-provision/knowledge-and-publications/factsheets/factsheet-fraudulently-issued-security-certificate-discovered.html <comment><USER2> More specifically, they have taken over operational management of the DigiNotar _servers_ :

"On 3 September, the Dutch government has taken over operational management of the DigiNotar systems that are used for certificates."

[http://www.govcert.nl/binaries/live/govcert/hst%3Acontent/en...](http://www.govcert.nl/binaries/live/govcert/hst%3Acontent/english/service- provision/knowledge-and-publications/factsheets/factsheet-fraudulently-issued- security-certificate-discovered/factsheet-fraudulently-issued-security- certificate-discovered/govcert%3AdocumentResource/govcert%3Aresource)

</comment><comment><USER3> Well, that's about as competent a response as you can realistically expect in a situation like this. Kudos to the Dutch Government?

<comment><USER4> Let's not start cheering just yet. Initially, the government failed to acknowledge the seriousness of the issue, even though browser manufactures revoked trust in the CA. In fact, they successfully pressured Mozilla into accepting Dutch government certificates as trustworthy anyway.

It also kept all public services depending on Diginotar certificates operational, basically telling the public "well, you can't trust them, but feel free to use them anyway".

<comment><USER5> They started with this, but ended differently: Friday September 2nd they had a news conference and basically said "for now, it's insecure". DigiD, which is what most consumers will see, had a message saying it couldn't be trusted, till the certificates were replaced sometime saturday or sunday. The timing is a little suspicious though: can imagine they didn't want to do that till the weekend, when not as many business cared, or journalists were around to cover all the details.

</comment></comment></comment><comment><USER6> And they denounced trust in all certificates that are issued by DigiNotar and currently migrating to new CAs (Getronics). The digital identity portal DigiD for government services is already migrated to Getronics CA.

</comment><comment><USER7> Does the Dutch government have the expertise necessary to run a CA?

<comment><USER8> Does it particularly matter? All the browsers have de-trusted them already.

Not like they could really do any worse. Under previous management they literally allowed the _one thing_ no CA must ever allow to happen.

<comment><USER9> You're conflating the Dutch Government and VASCO/Diginotar.

</comment></comment></comment><comment><USER10> <http://www.net-security.org/secworld.php?id=11565>

It looks like many other bad SSL certs were issued.

</comment><comment><USER11> Have there been any stories on VASCO yet, or the people involved in either company?

<comment><USER5> There's now an article here [http://www.nu.nl/internet/2607758/diginotar- negeerde-misbrui...](http://www.nu.nl/internet/2607758/diginotar-negeerde- misbruik-en-was-slecht-beveiligd.html) which details the FoxIT (a Dutch security firm) report on the situation. Looks like sysadmin passwords were brute forced to obtain elevated access, and that keys were stored in a seperate database (going against the written procedures and rules)</comment></comment>


Fast HSV to RGB Conversion - <AUTHOR> http://www.vagrearg.org/content/hsvrgb <comment> seph-reed If you're ever working with colors and have the cpu time available, I highly recommend working with luminosity. The gist is, full-bright yellow is brighter than full-bright purple, they have different luminosities. Anything with a luminosity of 100% is white.

CIELAB is worth looking into for the color space which best defines how humans see ([https://en.wikipedia.org/wiki/CIELAB_color_space#RGB_and_CMY...](https://en.wikipedia.org/wiki/CIELAB_color_space#RGB_and_CMYK_conversions)).

Here's some of the relevant code, copied from the private repo I have for this (legally).

[https://gist.github.com/SephReed/8c762f3434d683c66339f63342e...](https://gist.github.com/SephReed/8c762f3434d683c66339f63342e8ae4b)

<comment> mark-r That's not universal advice, it depends on the problem domain. Since people are used to yellow being brighter than purple, if you try to maintain the same luminosity you can make things totally messed up. If you're trying to recolor a purple object to yellow, you're unlikely to get a satisfactory result unless you keep the original intensity - keeping a constant luminosity will give a too dark result that doesn't even look yellow.

<comment> seph-reed True. In my scenario I was dealing with procedurally generated UIs based off a custom color palette. In order to keep things readable, there has to be a certain level of contrast between text and background. This contrast is based off perceived brightness (lums) rather than rgb diffs.

I've also used it for LED projects, and it helps a lot with that obnoxious brighter/darker wave that you get when rotating through hues.

The reason I brought it up in this thread in particular is because it's about HSV. Anytime I've dealt with HSV, it's because there's some hue rotation going on. I think HS Lum creates a more intuitive color rotation aesthetic for things with less saturation. It maintains shading rather than colorfulness.

</comment></comment></comment><comment><USER2> I just completed a project using wS2811 lights using the FastLED library of functions. The library was built for effectively the same reason, that the base functions for converting between different colour sets were inefficient. I'm curious if some of what was done in Vagrearg is already a part of FastLED.

</comment><comment><USER3> To visualize data on a color scale, I just sample the hue with this, I guess you can also use this to calculate other things.

        vec3 coolhue(float h){
            if (h == 1.f) return vec3(1.f,0,0);
            h *= 6.0f;
            if(h<1.f) {           return vec3(0, 1.f, h);          } // +blue
            if(h<2.f) { h -= 1.f; return vec3(0, 1.f - h, 1.f);  } // -green
            if(h<3.f) { h -= 2.f; return vec3(h, 0, 1.f);          } // +red
            if(h<4.f) { h -= 3.f; return vec3(1.f, 0, 1.f - h);  } // -blue
            if(h<5.f) { h -= 4.f; return vec3(1.f, h, 0);          } // +green
            if(h<6.f) { h -= 5.f; return vec3(1.f - h, 1.f, 0);  } // -red
            return vec3(1,1,1);
        }

This is for GLSL shaders, since shaders don't allow switch()

<comment><USER4> For shaders you're probably faster off if you use the expression

        #define saturate(x) clamp(x,0,1)
        rgb = saturate(vec3(abs(hue-180)/60 - 1, 2 - abs(hue-120)/60, 2 - abs(hue-240)/60))

Since it avoids the if/then. Although I'm not too sure if it will work well in GLSL as HLSL has better support for the saturate function (it can usually be merged with some other operator).

<comment><USER5> Works fine in GLSL ([https://www.shadertoy.com/view/tdV3DW](https://www.shadertoy.com/view/tdV3DW))

Starting on green and mapped to [0, 1] like op's:

        vec3 coolhue2(float h){
            float h6 = 6.*h;

            return saturate(
                vec3(
                    2. - abs(h6 - 4.),
                    abs(h6 - 3.) - 1.,
                    2. - abs(h6 - 2.)
            ));
        }</comment></comment></comment>


When Exxon wanted to be a personal computing revolutionary - <AUTHOR> http://motherboard.vice.com/read/when-exxon-wanted-to-be-a-personal-computing-revolutionary

<comment><USER2> I remember finding an Exxon branded office machine at a swap meet and thinking "really?". So I guess we give Exxon a hat tip to know this was going to be very large industry, but a poor mark for not being able to effectively implement that vision. I wasn't a big fan of the Z8000 but the data sheets for the Z280 had me thinking it would be an excellent basis for a 16 bit machine. Too bad it took way too long to come out.

</comment><comment><USER3> Exxon was involved in a lot of side ventures in the 70s and 80s, largely due to an excess of capital with few places in the industry to invest it and a fear that the rise of oil nationalism could fatally deprive Exxon of the booked reserves it needs.

Most of that ended after Valdez and was accelerated when Lee "Iron Ass" Raymond became CEO in 1993. He was not amused by Exxon's non-oil ventures.

<comment><USER4> GM went through a similar phase with EDS and Hughes in the mid 1980s (which is of course how GM ended up owning DirecTV).

I can't help but think Google is behaving similarly today, at the height of its power. Cars, Nest, fiber, wireless, robotics, biotech; and I suspect that will continue to expand.

<comment><USER5> Except Google's core competency is software. Google has the advantage of being essentially controlled by Sergey Brin and Larry Page.

They only need one of those things to succeed (cars). It's hard to imagine that one of the existing car companies has developed a reliable urban self- driving, that is better than Google's.

Google's fiber play has nothing to do with actually making money. It is about making sure that cable companies abide by net neutrality and not cutoff or strangle Google's bandwidth intensive services like YouTube.

<comment><USER6> _It 's hard to imagine that one of the existing car companies has developed a reliable urban self-driving, that is better than Google's._

I don't know, some are pretty good, and they don't even need the LIDAR: [https://youtu.be/r_RFzC_G5BA](https://youtu.be/r_RFzC_G5BA)

<comment><USER5> Highway self-driving is pretty much consumer-grade now. Infiniti already has it in their vehicles. Telsa is planning on rolling it out this summer. The main problem is urban and suburban driving and dealing with unexpected situations such as jaywalkers or a cyclists running stoplights or a kid running out after a ball.

<comment><USER6> That wasn't just highway, but here's a more urban setting, with pedestrian avoidance: [https://www.youtube.com/watch?v=AihC5flC-38](https://www.youtube.com/watch?v=AihC5flC-38)

</comment></comment></comment></comment></comment></comment><comment><USER7> Computerworld article from 1980:

[https://books.google.com/books?id=OMthpMVWLOAC&pg=RA1-PA83&l...](https://books.google.com/books?id=OMthpMVWLOAC&pg=RA1-PA83&lpg=RA1-PA83&dq=exxon+data+systems&source=bl&ots=xlkY18uhvw&sig=CbLPqdpSm2rjlUzur73RQGWRnBY&hl=en&sa=X&ei=Fzo9VbmPNsndsAXVsoDAAg&ved=0CDQQ6AEwBA#v=onepage&q=exxon%20data%20systems&f=false)

They were projecting $695 million in office automation revenue for 1985. Obviously, that didn't work out. Excellent quote in there:

"Northern believes software development may be Exxon's biggest problem in tying the loose ends together"

<comment><USER8> How does one search Google for old Computerworld (or other old magazine) articles? I don't recall seeing those in the results for Google Books searches.

<comment><USER9> [http://books.google.nl/books/about/Computerworld.html?id=ph6...](http://books.google.nl/books/about/Computerworld.html?id=ph6vc1JYSAIC&redir_esc=y)

</comment></comment></comment><comment><USER10> I don't get this constant push of "internet of things".

Very few devices i have, that are not pure information devices, can be operated for any duration without some sort of human attention.</comment>


Tribe Of Mentors book app made with GlideApps - <AUTHOR> https://tribeofmentors.glideapp.io/ <comment><AUTHOR> This app can help readers easily traverse the 130+ expert profiles showcased in Tim Ferriss' book Tribe Of Mentors, browse their bios, read their answers to Tim's infamous 11 questions, and even check links to their favourite books and gadgets.</comment>


Forgotten Benefactor of Humanity - Norman Borlaug (1997) - <AUTHOR> http://www.theatlantic.com/issues/97jan/borlaug/borlaug.htm

<comment><AUTHOR> Closing paragraph

"If overpopulation anarchy comes, it is likely to arrive first in Africa. Borlaug understands this, and is using his remaining years to work against that cataclysm. The odds against him seem long. But then, Norman Borlaug has already saved more lives than any other person who ever lived."</comment>


CommonLoops: Merging Lisp and Object-Oriented Programming (1988) [pdf] - <AUTHOR> http://www2.parc.com/istl/groups/gir/papers/stefik-commonloops-oopsla66.pdf

<comment><USER2> This is one of the ancestors of CLOS. Some CLOS implemenations are descendants of PCL ("Portable CommonLoops") source code.

E.g. see the 1980's Xerox copyright notices in this SBCL source file:

[https://github.com/sbcl/sbcl/blob/master/src/pcl/wrapper.lis...](https://github.com/sbcl/sbcl/blob/master/src/pcl/wrapper.lisp)

<comment><AUTHOR> One of the decedents was Xerox LOOPS, which was an Interlisp OO extension and ran on their Alto and Star systems (IIRC). I had the opportunity to play around on one of these and hacked some LOOPS code back in the day. LOOPS itself had a very interesting mix of OO and 'data' oriented programming. It introduced the idea of 'composite' objects - a language formalism that allowed for the instantiation of groups of objects based on a template, among other novel language features.

[http://www.markstefik.com/wp- content/uploads/2011/04/1982-Bo...](http://www.markstefik.com/wp- content/uploads/2011/04/1982-Bobrow-Stefik-Data-Object-Pgming.pdf)</comment></comment>


The 50 best iPhone apps of all time - <AUTHOR> https://appinstruct.com/blog/2013/12/50-best-iphone-apps/

<comment><AUTHOR> What did we miss? What don't you agree with?

<comment><USER2> A password manager. I personally use 1Password. Life before it was painful and insecure.</comment></comment>