February 8, 2016

Standard Intermediate Assembly For CPUs

Over in GPU land, a magical thing is happening. All the graphics card vendors and big companies got together and came up with SPIR-V as the technological underpinning of Vulkan, the as-of-yet unreleased new graphics API. SPIR-V is a cross-platform binary format for compiled shaders, which allows developers to use any language that can compile to SPIR-V to write shaders, and to run those compiled shaders on any architecture that supports SPIR-V. This is big news, and if it works as well as everyone's hoping it does, it will set the stage for a major change in how shaders are compiled in graphics engine toolchains.

How is this possible? The SPIR-V specification tells us that it is essentially a cross-platform intermediate assembly language. It's higher level than conventional assembly, but lower than an actual language. The specifics of the language are fine-tuned towards modern graphics hardware, so that the instructions can encode sufficient metadata about what they're doing to enable hardware optimizations, while still allowing those instructions to be efficiently decoded by the hardware and implemented by the chip's microcode.

While SPIR-V is specifically designed for GPUs, the specification bears some resemblance to another intermediate assembly language - LLVM IR. This move by GPU vendors towards an intermediate assembly representation mirrors how modern language design is moving towards a standardized intermediate representation that many different languages compile to, which can then itself be compiled to any CPU architecture required. The LLVM IR is used for C, C++, Haskell, Rust, and many others. This intermediate representation decouples the underlying hardware from the high level languages, allowing any language that compiles down to LLVM IR to compile to any of it's supported CPU architectures - even asm.js.

However, we have a serious problem looming over our heads in CPU land. Did you know that the x86 mov instruction is turing complete? In fact, even the page fault handler is turing complete, so you can run programs on x86 without actually executing any instructions! The x86 architecture is so convoluted and bloated that it no longer has any predictable running time for any given set of instructions. Inserting random useless mov instructions can increase speed by 5% or more, false dependencies destroy performance, and Intel keeps introducing more and more complex instruction sets that don't even work properly. As a result, it's extremely difficult for any compiler to produce properly optimized assembly code, even when it's targeting a specific architecture.

One way to attack this problem is to advocate for RISC - Reduced Instruction Set Computer. The argument is that fewer instructions will be easier to implement, reducing the chance of errors and making it easier for compilers to actually optimize the code in a meaningful way. Unfortunately, RISC has a serious problem: the laws of physics. A modern CPU is so fast that it can process an instruction faster than the electrical signal can get to the other side of the chip. Consequently, it spends the majority of it's time just waiting for memory. Both pipelining and branch prediction were created to deal with the memory latency problem, and it turns out that having complex instructions gives you a distinct advantage. The more complex your instruction is, the more the CPU has to do before it needs to fetch things from memory. This was the core observation of the Itanium instruction set, which relies on the compiler to determine which instructions can be executed in parallel in an attempt to remove the need for pipelining. Unfortunately, it turns out that removing dependency calculations is not enough - this is why many of Intel's new instructions are about encapsulating complex behaviors into single instructions instead of simply adding more parallel operators.

Of course, creating hardware that supports increasingly complex operations is very unsustainable, which is why modern CPUs don't execute assembly instructions directly. Instead, they use Microcode, which is the raw machine code that actually implements the "low-level" x86 assembly. Of course, at this point, x86 is so far removed from the underlying hardware it might as well be a (very crude) high level language all by itself. For example, the mov instruction usually doesn't actually move anything, it just renames the internal register being used. Because of this, the modern language stack looks something like this:

Modern Language Stack

Even thought we're talking about four CPU architectures, what we really have is four competing intermediate layers. x86, x86-64, ARM and Itanium are all just crude abstractions above the CPU itself, which has it's own architecture dependent microcode that actually figures out how to run things. Since our CPUs will inevitably have complex microcode no matter what we do, why not implement something else with it? What if the CPUs just executed LLVM IR directly? Then we would have this:

LLVM IR Microcode Stack

Instead of implementing x86-64 with microcode, implement the LLVM intermediate assembly code with microcode. This would make writing platform-independent code trivial, and would allow for way more flexibility for hardware designers to experiment with their CPU architecture. The high-level nature of the instructions would allow the CPU to load large chunks of data into registers for complex operations and perform more efficient optimizations with the additional contextual information.

Realistically, this will probably never happen. For one, directly executing LLVM IR is probably a bad idea, because it was never developed with this in mind. Instead, Intel, AMD and ARM would have to cooperate to create something like SPIR-V that could be efficiently decoded and implemented by the hardware. Getting these competitors to actually cooperate with each other is the biggest obstacle to implementing something like this, and I don't see it happening anytime soon. Even then, a new standard architecture wouldn't replace LLVM IR, so you'd still have to compile to it.

In addition, an entire new CPU architecture is extraordinarily unlikely to be widely adopted. One of the primary reasons x86-64 won out over Itanium was because it was capable of running x86 code at native speed, and Itanium's x86 emulation was notoriously bad. Even if we somehow moved to an industry-wide standard assembly language, the vast majority of the world's programs are still built for x86, so an efficient translation between x86 and our new intermediate representation would be paramount. That's without even considering that you'd have to recompile your OS to take advantage of the new assembly language, and modern OSes still have some platform-specific hand-written assembly in them.

Sadly, as much as I like this concept, it will probably remain nothing more than a thought experiment. Perhaps as we move past the age of silicon and look towards new materials, we might get some new CPU architectures out of it. Maybe if we keep things like this in mind, next time we can do a better job than x86.

September 28, 2015

There Will Never Be One True Programming Language

A disturbing number of otherwise intelligent programmers seem to believe that, one day in the distant future, everyone will use the same magical programming language that solves everyone's problems at the same time. Often, this involves garbage collection, with lots of hand-waving about computers built with Applied Phlebotinum.

For the sake of argument, let's assume it is the year 30XX, and we are a budding software developer on Mars. We have quantum computers that run on magic pixie dust and let us calculate almost anything we want as fast as we want so long as we don't break any laws of physics. Does everyone use the same programming language?

No. Obviously, even if we built quantum computers capable of simulating a classical computer a thousand times faster than normal for some arbitrary reason[1], we would still want to write software in a way that was easy to comprehend, and qubits are anything but. So, the standard programming language our Mars programmer would be using would not interact with the quantum computer at all. Perhaps it would be some form of functional language, which the cool kids seem to think will save the entire software industry and make everything faster, better, more reliable and probably cure cancer in the process[2].

But now, this same programming language that allows you to ignore the quantum computer must also be used to write it's own compiler that runs on... a quantum computer. So it needs to simultaneously be an elegant language for writing quantum programs. It also needs to be the perfect language for writing games and business applications and hypernet sites and machinery and brain implants and rocket trajectories and robot AI and planetary weather simulators and life support systems and hologram emittors and—

... Wouldn't it be a lot easier to just have domain-specific languages?

If you want to get technical, you can do all that with Lisp. It's just lambda calculus. It's also a giant pain in the ass when you use it for things that it was never meant for, like low level drivers or game development. You could try extending Lisp to better handle those edge-cases, and that's exactly what many lisp-derivatives do. The problem is that we will always be able to find new edge-cases. So, you would have to continue bolting things on to your magical language of ultimate power, playing a game of context-free whack-a-grammar for the rest of eternity.

This is ultimately doomed to fail, because it ignores the underlying purpose of programming languages: A programming language is a method of communication. Given two grammars, one that can do everything versus one that can do the one thing you're trying to do really well, which one are you going to pick for that particular project? Trying to get everyone to use the same language while they are trying to solve radically different problems is simply bad engineering. Do you really want to write your SQL queries using Lisp? You can only stuff so many tools into a swiss army knife before it becomes completely impractical.

Of course, a language that could do everything would also allow you to define any arbitrary new language within it, but this is equivalent to building an entire new language from scratch, because now everyone who wants to contribute to your project has to learn your unique grammar. However, having a common ground for different languages is a powerful tool, and we are headed in that direction with LLVM. You wouldn't want to write a program in LLVM, but it sure makes writing a compiler easier.

Instead of a future with one true programming language, perhaps we should be focused on a future with standard low-level assembly. A future with many different languages that can all talk to each other. A future where we don't need to argue about which programming language is the best, because every language could be used for whatever task it is best suited for, all in the same project. Different compilers could interpret the standard low-level assembly in their own ways, optimizing for different use-cases.

A universal standard for low-level assembly would solve everyth—

mandatory xkcd

... Actually, nevermind[3].



1
As of right now, this is not actually true for the vast majority of computations we know how to do with qubits. We know that certain classes of problems are exponentially faster with quantum computers, but many other functions would get a mere linear increase in speed, at most. Whether or not this will change in the future is an active area of research.
2 They totally didn't say the same thing about object-oriented programming 30 years ago.
3 </sarcasm>

August 5, 2015

Abortion Has No Moral High Ground

June 26, 2015 was a historic moment. That was the day the Supreme Court legalized gay marriage on a federal level in the United States. As far as I'm concerned, this victory was the inevitable result of a point of view that relied entirely on logical fallacies and irrational hatred. Yet, it was a battle that was fought for centuries, and continues on, as obstinate conservatives vow to continue fighting to the bitter end. The fact that gay rights was ever a political issue will be considered barbaric by future civilizations, because being gay is inherently harmless, and attempting to control who people are allowed to love is an egregious violation of individual rights.

It was this innate emotional connection that won the battle. Every human being on this planet can empathize with the desire to love freely. We were united in our desire for love to win, just like it does in all of our fairy tales.

Abortion, on the other hand, is likely to remain an issue for hundreds of years. I'm not sure if it will ever be put to rest until we invent technology that renders the entire debate irrelevant. The problem is that, with abortion, there is no moral high ground. This is because, under all circumstances, abortion leaves us with two choices: we can pick an arbitrary point in time where a fetus suddenly has rights, or we will be forced to violate someone's rights no matter what option we choose.

First, let me clarify why we cannot base the point at which a fetus suddenly gains rights on any meaningful science: babies do not have on switches. Conciousness is an emergent phenomenon, and we have known for years that animals have some degree of awareness. They have a consciousness much like we do, but operating on a lower level (in some cases, it may simply be on a different level). Babies can't even pass the mirror test until they're about 18 months old. Because of this, we will be forced to pick some completely arbitrary point in time at which the baby suddenly gains rights, if we want to avoid violating those rights.

Now, if we don't do that (and I find it incredibly hard to believe that we will, given the number of people who think that a fertilized egg has human rights), we have two choices:
1. We forbid abortion, violating both the mother's right to her own body and her right to live (0.019% of pregnancies are fatal).
2. We allow abortion, violating the baby's right to live.

There is no way out of this. We cannot use logic to pick a point in time where the baby suddenly gains rights because it will inherently be arbitrary. We'd basically have to say "Well, if you're 80% human, we'll give you rights, because, well, 80% sounds like a good number." No matter how much reasoning we have behind that number, it's still fundamentally arbitrary and therefore it will always be a valid argument to say it is morally wrong. This means we are forced to violate someone's rights no matter what we do. Some people think the baby is more important. Some people think the women is more important. Some people think whose important depends on how far along the baby is. There is no nice, clean solution to this. We have to screw somebody over no matter what we do.

My personal view, which is that we should allow abortions only in the first trimester, is not born out of some moral argument. It is simple engineering pragmatism: it is the least objectionable and most practical solution that I can come up with. I am forced to fall back to my engineering background for this problem because there is no valid moral solution. If someone asks me why I think it's okay that we're killing a fetus, I would say that it's not okay. However, it's also not okay to deny a women the right to her own body. It's not okay to allow unwanted pregnancies to result in unwanted children that get left in orphanages. It's not fair that the hypothetical wanted child that could have been born later will now never be born. No part of this situation is ever okay, on either side of the debate.

If we want to resolve the debate, society as a whole simply has to try and figure out how to minimize the damage. That is all we can do. There is no right answer. There are only wrong answers, and we have to pick the least wrong one.

July 28, 2015

Why I'll Never Get The Life I Wanted

"We must let go of the life we have planned, so as to accept the one that is waiting for us." - Joseph Campbell
I never liked money. It was always an inconvenience, a roadblock that had to be maneuvered around to get to where I actually wanted to go. For a long time, I had a nice life for myself planned out. I'd take a few consulting jobs and travel the world, staying with friends and squeezing by on a slim budget. In my spare time, I could develop open-source software and give it away for free. I could focus on making the world a better place, instead of making a ton of money that served no purpose.

I'd never need to be well known. I could simply live the life of a nomad, earning just enough for food and maybe the occasional roof, when I wasn't crashing at a friend's place. I never wanted anything else. I wanted freedom. I wanted to explore. I wanted to find beautiful places in nature, and to build cool things with code. Take some pictures, write some music. Maybe learn to draw. A carefree life where I didn't worry about climbing some ridiculous corporate ladder, or proving myself. A life where I could exist outside of the toxic society that I was born into.

There is a bitter irony in my current situation. After a year-long stint at a large software corporation that paid me absurd amounts of money, I now have plenty of savings to pursue the life I had always wanted...

But I can't. Or perhaps, I won't. There's nothing actually stopping me. In fact, later this year, I intend to go on a nice vacation across the west coast, while I still can. So I can at least experience what my life might have been for a brief moment, before I am dragged back into hell. A hell where my friends can't afford to let me stay at their houses for extended periods of time, because most of them live in poverty. A hell where income inequality lets me do whatever I please by taking everything from those less fortunate than me. A world so full of shit I can't simply ignore it.

There can be no carefree lifestyle, because it is nothing more than an illusion. I refuse to live a life that no one else can simply because they weren't born into a rich, stable middle-class family in a wealthy city. When I was young, I thought I was choosing this life for myself. I thought I was choosing to eschew consumerism and materialistic rewards for a more fulfilling life because I was making a wise choice. Instead, my ability to ignore our toxic society was simply because I was born in the right place. Other people don't try to climb corporate ladders because they like to, they do it because they have to.

I'm lucky. I don't. I can do whatever I want. Not because I'm smarter, or better, or wiser, but simply because I was given the opportunity. I've met a lot of people now who want what I want. They want to make the same choices I would. Their hearts are in the right places, but they're all living in poverty. One has a crippling auto-immune disorder. One lives with abusive parents. Half of them are gay. They all wanted to make the right choices, but they couldn't, because they weren't lucky. Because they got the short straw. Because life decided to fuck them over.

This isn't what I wanted. I wanted to make the world a better place. I thought I could do that by avoiding Wall Street and it's disgusting corporate corruption and greed. I thought anyone could live a quiet, frugal life if they just avoided consumerism and greed. I thought there would be other intelligent rich people who would strive to make the world a better place. I thought good things happened to good people.

I was an idiot. The only way a rich person is going to actually make the world a better place is if I do. The only way my friends will ever get a job that isn't horrible is if I build a billion dollar company and hire them myself. The only way to make the future a place I'd want to live in is if I drag the world into it kicking and screaming. The only way we'll get programs that aren't giant towers of duct tape and prayers is if I force people to actually use good engineering practices.

I grew up believing the world was a magical place where people got jobs and lived the lives they wanted. I understand now that, if I truly believe in that world, I have to make it happen myself. It's not the life I had planned, but life is like an improvisation. If you try to play the song you intended, you'll miss out on all the opportunities created by your mistakes. If I must throw myself into building a business, then so be it.

It's not like I can do anything else; I'd never forgive myself if I simply rode off into the horizon, ignoring the plights of all the human beings I cared about.

July 21, 2015

I Tried To Install Linux And Now I Regret Everything

I am going to tell you a story.

This story began with me getting pissed off at Windows for reasons that don't really need to be articulated. Just, pick your favorite reason to hate Windows, and let's pretend that was the tipping point. I had some spare space on my secondary drive, so I decided to give Linux another whirl. After all, it had been three or four years since I last attempted anything like this (it's been so long I don't rightly remember the last time I tried), and people have been saying that Linux has gotten a lot better recently!

The primary reason I was attempting this is because of Valve's attempts to move to linux, which have directly resulted in much better nVidia driver support. Trying to install proper nVidia drivers is the thing that wrecked my last attempt. This time, I figured, I could just install the nVidia drivers straight from the repo and everything would be hunky dory, and I could just spend the rest of my time beating on the rest of linux with a comically oversized wrench until it did what I wanted.

I'd had good experiences with XFCE and Fedora on my Linux VM, but I didn't really like Fedora itself (but it interfaced very well with my VM environment). I wanted to install Ubuntu, because it has the best support and I don't like trying to dig through arcane forum posts trying to figure out why my computer screen has suddenly turned into an invisible pink unicorn. Unfortunately, Ubuntu is a bloated mess, and I hate it's default desktop environment. In the past, I had tried Linux Mint, which had been okay, but support had been shaky. I spotted Lubuntu, which is supposed to be a lightweight ubuntu on top of LXDE, a minimal window manager similar to XFCE. This was perfect! So I downloaded Lubuntu 15.04 and installed it on my secondary drive and everything was nice and peachy.

Well, until Linux started, anyway. The first problem was that the package manager in pretty much every linux distro lists all the packages, including all the ones I don't understand. I was trying to remove some pre-included Paint application and it had two separate packages, one named after itself, and one named <name>-common, but the common package wasn't automatically removed when the program was! The nVidia packages also had both an nvidia-drivers-340[1] package, and an nvidia-drivers-340-update package, both of which had identical descriptions. I just went with the most basic one because it seemed sensible, but I felt really sorry for someone less tech savvy trying to find anything in that godforsaken list.

So after everything updated and I restarted and told the package manager to start installing my nVidia drivers, I started noticing annoying things about LXDE. A lot of annoying things. Here, let me list them for you:

  • The file manager, when unmounting anything, would helpfully close itself.
  • Trying to change the time to not display military time involves editing some arcane string whose only value is %r, and I still don't know what that means and don't want to know what that means. All I wanted to do was change it to say AM or PM!
  • In order to get a shortcut onto the desktop, you had to right-click the menu item and then a new menu would show up that would let you add it to the desktop. You can't drag anything out of the start menu.
  • The shortcuts seemed disturbingly fickle, occasionally taking 4 rapid clicks to get things to start up.
  • Steam simply didn't start at all. Unfortunately, we will never know why.
  • Skype managed to spawn a window halfway above the top of the screen, which resulted in me having to look up alt+space in order to rescue it.
  • Skype also cut off everything below the baseline of the bottom-most line of text, so you couldn't tell if something was an i or a j.
  • Like in Windows, in Linux, modal dialogs have a really bad habit of sucking up keyboard focus, focusing on a button, and then making my next keystroke click the button. The fact that this is a widespread UI problem across most operating systems is just silly.

There were more nitpicks, but I felt like a large number of these issues could have probably be resolved by switching to a better window manager. I was honestly expecting better than this, though. LXDE was supposed to be like XFCE, but apparently it's actually XCFE with all it's redeeming qualities removed. However, it turns out that this doesn't matter! To discover why, we have to examine what happened next.

My friend suggested to get Linux Mint, which has a better window manager and would probably address most of those issues. So, I downloaded the ISO. I already had a USB set up for booting that had Lubuntu on it, so I wanted to see if I could just extract the contents of the ISO and put them on the USB stick. I have no idea if this would have actually worked or not, and I never got to find out because upon clicking and dragging the contents of the ISO out of the archive manager, with the intent of extracting them, the entire system locked up.

Now, I'm not sure how stupid trying to drag a file out of an ISO was, but I'm pretty sure it shouldn't render my entire computer unusable. This seems like an unfair punishment. Attempts to recover the desktop utterly failed, as did ctrl-alt-del, alt-F2, alt-anything else, or even trying to open a terminal (my friend would later inform me that it is actually ctrl-alt-F2 that opens the terminal, but I couldn't ask him because the entire desktop had locked up!). So I just restarted the machine.

That was the last time I saw my Lubuntu desktop.

Upon restarting my machine, I saw the Lubuntu loading screen come up, and then... nothing. Blackness. About 20 seconds later, an error message pops up: "Soft Lock on CPU#0!". Then the machine rebooted.

After spending just one hour using linux, it had bricked itself.[2] I hadn't even gotten steam working yet, and now it was completely unusable. This is not "getting better", this is strapping a rocket to your ass and going so fast in the wrong direction you break the sound barrier. Now, if you have been paying attention, you will note that I had just finished installing my nVidia drivers before the desktop locked up, and that the drivers probably wouldn't actually be used by the desktop environment until the process was restarted. After mounting my linux partition in windows and extracting my log files, I sent them to my friend, who discovered that it had indeed been the nVidia driver that had crashed the system.[3]

This is problematic for a number of reasons, because those drivers were written for Ubuntu based distros, which means my system could potentially lock up if I installed any other ubuntu based distro, which is... just about all the ones that I cared about. Including Linux Mint. At this point, I had a few options:

1) Install another ubuntu based distro anyway and hope it was a fluke.
2) Install something based on Debian or maybe Fedora.
3) Use the open-source drivers.
4) Fuck this shit.

Unfortunately, I have little patience left at this point, and after seeing Linux first lock up after trying to drag files before bricking itself, I don't really have much confidence in the reverse-engineered open-source nVidia drivers, and I certainly am not going to entrust my video card to them in the hopes it doesn't melt. I really, really don't want to play whack-a-mole with Linux distros, trying to find the magical one that wouldn't wreck my graphics card, so I have simply opted for option 4.

But it doesn't end there.

About two or three hours after this all happened (I had been fairly distracted, and by distracted I mean blinded by rage), I noticed that my windows clock was way off. At this point, I remembered something my friend had told me about - Linux, like any sane operating system would, sets the hardware clock to UTC and then modifies it based on the timezone. Windows, on the other hand, decided it would be a fantastic idea to set the hardware clock to local time. Of course, this should have been an easy fix. I just set the time back, forced an update, and bam, time was accurate again. Crisis averted, right?

No, of course not. Linux had not yet finished punishing me for foolishly believing I was worthy of installing something of it's calibre. Because then I opened Skype, and started receiving messages in the past. Or more accurately, my chat logs were now full of messages that had been sent tomorrow.

It was at this point I realized what had happened. It wasn't that the timezone had been changed, or something reversible like that. The hardware clock itself had been modified to an incorrect value. Skype had spent the past two hours happily sending messages with a timestamp 8 hours in the future because some idiot at Microsoft thought it was a good idea to set the hardware clock to local time, and now all these incorrect client side timestamps had been propagated to the cloud and synced across all my devices.

I slowly got up from my computer. I walked over to my bed, lied down, curled into a ball, and wept for the future of mankind.



1 I may have gotten these package names slightly wrong, but I can't verify them because Linux won't boot up!
2 It's probably entirely possible to rescue the system by mounting the file system and modifying the x config file to not use nvidia, but this was supposed to just be "install linux and set it up", not "install linux and spend the next 5 hours frantically trying to get it to boot properly."
3 After submitting my log files to #ubuntu to report the issue, my friend discovered that the drivers for ATI/AMD graphics drivers are also currently broken for many ubuntu users. What timing! This certainly makes me feel confident about using this operating system!