Ancient City Ruby 2016

What is Processor?

Sometimes, writing Rails apps is awful. Do you know what's nearly always more awful? Handrolling assembly. Ruby lets us not think about how the processor works, but we're programmers. We're uniquely positioned to intellectually appreciate the wonderful, complex, engineering that goes into a processor.

In this talk, you’ll learn a little more about what it means for Ruby to be an “interpreted” language, how a processor executes programs, and what magical tricks processor designers use to make our programs go faster with every generation. If you’ve ever written a Ruby program, and understand that a computer has a processor in it, this talk is probably for you.

Ancient City Ruby 2016

00:00:10.519 Hello everyone! I'm here to discuss what a processor is. During my talks, I often find myself wondering about the intricate workings of technology around us. This talk will cover abstractions, programming languages, processors, and all the fascinating aspects that relate to them. First, let me introduce myself. My name is Sam Phippen, and you can find me on Twitter and GitHub under the same name. If you wish to stay in contact after the conference, feel free to reach out.
00:00:22.710 I'm wearing this t-shirt from a company called Test Level. Unfortunately, my wardrobe consists mainly of conference and company t-shirts because that's just who I am. I've had a few people question if I work for Test Level, so I wanted to clarify that I don’t, but they are wonderful people, and Justin Souls, whom many may know from the company, is a great guy. I may not be representing your company directly, but Justin, I do appreciate what you do!
00:01:02.470 Reflecting on Kerry's talk earlier, she asked us to consider how we think about code and how others write it. She proposed that we ask ourselves how Sam would write code, and my answer is simple: it's often a chaotic barrage of thoughts and actions. I remember burning out my keyboard once on a MacBook because I reached the key press limit after four intense years of usage. So, in a nutshell, I advise you—maybe don’t code like me!
00:01:44.380 Before diving into the content of this talk, I want to provide a quick disclaimer. What I’ll be sharing today are what I like to call "convenient lies." These are simplified beliefs we programmers hold regarding how computers function, designed to help us complete our tasks without delving into the overwhelming details of the countless components within our machines.
00:02:06.930 These convenient lies include abstractions we use for networks and file systems, which allow us to avoid confronting the staggering complexities that underpin quantum physics within our computers. Many concepts I discuss may not be strictly accurate, but they will serve as useful models to think about computer operations. If you're well-versed in low-level programming concepts, that's fantastic, but my aim today is not to provide in-depth technical insights. Instead, I hope to spark your interest in how computers execute programs.
00:02:34.080 Let’s talk about Ruby. Many of us are here because we love programming in Ruby more than in other languages. Being a Ruby developer is unique—this language allows us to focus more on the logic and relationships concerning our problems and less on how the computer perceives this logic. When coding in Ruby, we think more in terms of our domain—the users and systems we are involved with—rather than the intricate details of how processes operate.
00:02:56.690 For instance, expressing a date in Ruby might be done with a simple phrase like "three days ago," rather than cumbersome calculations involving dates and seconds. New developers are often taught to think in terms of their application's domain, which consists of concepts like shopping carts and delivery addresses, rather than technical details like memory management.
00:03:18.800 The opposite of this domain-driven approach is the more technical viewpoint of understanding how a computer operates. However, sometimes programmers need to merge these viewpoints, especially in cases like developing a driver for a graphics card. In that scenario, your domain overlaps with low-level computer mechanics.
00:03:58.010 In general, those of us who focus on building web applications in Ruby usually deal with real-world issues rather than computer-centric programming. Ruby helps obscure many underlying details of the computer’s functioning, allowing us to work more productively. One potential measure of abstraction from a programming language is how much you think about the computer while coding; in this sense, Ruby lets you think less about the mechanisms compared to languages like C or assembly.
00:04:29.430 To illustrate this further, let's compare Ruby with other programming languages regarding how much they require us to think about computers. If we position Ruby centrally on the abstraction line, then I would place Python similarly. Like Ruby, Python is a dynamic, interpreted language with an object model and similar functionality. When discussing programming in Python, developers also spend most of their time in the domain, rather than mechanical computer concepts.
00:05:03.560 This might stir some debate, especially at a Ruby conference, but if you disagree with my comparison, feel free to talk to me later over a drink. A crucial point to make is—while Ruby and Python allow abstraction from the computer, there are languages like SQL that allow even higher levels of abstraction.
00:05:37.500 SQL lets you express queries in a way that you focus on solving a problem rather than detailing how to carry out those processes. In SQL, you describe a sought-after outcome and let the system decide the pathway to achieve it, contrasting with Ruby and Python where you write explicit, ordered instructions that significantly interact with the computer’s state.
00:06:03.760 Moving down the abstraction ladder, we refer to the C programming language. C is less abstract than Ruby or Python because it engages programmers with more intricate computer operations. While I won’t exhaustively list every programming language between Ruby and C, it’s essential to recognize the myriad of languages that exist—each with unique properties and use cases.
00:06:35.060 Inside Ruby, programs usually require an interpreter. When you write a Ruby program, the Ruby interpreter reads each line and determines appropriate instructions to pass to the processor for execution. This process tightly couples the programmer’s source code with the interpreter during the execution phase.
00:06:56.820 In contrast, when we compile a C program, it undergoes a transformation into machine code stored separately, saved in binary format, which the processor understands and executes directly. This compilation separates the source code and execution time, allowing for easier distribution of programs as singular files—unlike Ruby's structure, which typically consists of multiple source files.
00:07:23.920 The difference between interpreted and compiled languages showcases the various execution models in programming. It should be noted that C is termed a 'low-level programming language' largely due to its historical association with processors, particularly with the PDP-11 computer.
00:07:47.160 Introducing the PDP-11: The features of its processor led to the development of C. This computer introduced 'byte addressable memory' for programmers, a feature that was absent in earlier languages. Creators of C aimed to utilize this innovative approach in their programming, and even today, some may incorrectly suggest that understanding C is essential to being a good programmer.
00:08:07.040 C does not model modern processors directly. Instead, it reflects programming paradigms aligning more closely with the needs of much older hardware. The fact that C remains prevalent through many generations of programming—it enables modern compilation techniques that optimize how it works on advanced hardware.
00:09:00.640 Next, let’s touch on assembly language. Before delving into assembly, let's clarify what registers are. Registers are tiny memory locations within the processor that enable fast access to data while executing instructions. They play a crucial role in the efficiency of computations by allowing the processor to persistently access frequently used data without needing to search the main memory.
00:09:39.370 Now, let’s look at a simple assembly program. Each instruction in an assembly file corresponds to one operation executed by the processor. These instructions lack higher level control flow structures present in languages like Ruby. Instead, each line represents an action to perform, such as moving a value into a register or executing a calculation. In simple terms, assembly requires programmers to understand deep low-level behaviors which are abstracted away in high-level languages.
00:10:11.200 Assembly language programs need to be compiled into a format that the processor can execute directly. However, unlike Ruby or C, assembly is process-specific, meaning the same assembly code cannot run on all types of processors. Each processor family has its own assembly language, so you might need different code for an x86 machine compared to an ARM machine.
00:10:53.460 Going one level deeper, we find binary instructions—the representation of the assembly code into a format that the processor can understand. Essentially, the binary file consists of numbers that represent operations and operands, similar to how assembly code translates into machine instructions, but formatted in a way that can be directly executed.
00:11:16.480 To execute such binary programs, processors employ a cycle called fetch-decode-execute-retire. In this model, the processor fetches instructions from memory, decodes them to understand their components, executes them using the appropriate arithmetic or logic units, and then writes back results while preparing for the next instruction.
00:11:45.180 This execution process typically begins with fetching the instructions into the instruction register from main memory, relying on the program counter to track their locations. Subsequently, the decode stage entails interpreting this instruction. If the instruction indicates adding two values, the processor will activate its arithmetic logic units to undertake that calculation.
00:12:16.510 Once the arithmetic operation happens, results are saved back into registers. The retire phase ensures that data is kept consistent, ensuring our processor is ready to continue with subsequent instructions by incrementing the program counter and clearing out previous data.
00:12:48.520 Though I have simplified this process significantly, understanding how this cycle works—including the details of the registers and internal components of the processor—provides insight into how programming languages interact with hardware architectures. It is this intricate dance we perform between high-level abstractions and low-level hardware that can be quite fascinating.
00:13:14.400 In conclusion, the aim of this talk has been to illuminate how computers work through the lens of programming language abstractions. Programmers are intriguingly positioned to comprehend this complexity, which, while not always directly applicable to daily programming tasks, enriches our understanding of technology. Thank you for your attention today, and I invite you to continue the conversation!
00:13:57.690 You can reach me on Twitter under the handle 'Sam Phippen', and my website is funandplausible.com. This talk is licensed under CC BY-NC Share-Alike. Thank you again for listening! Cheers!