Stories
Slash Boxes
Comments

SoylentNews is people

posted by martyb on Saturday October 31 2015, @03:33AM   Printer-friendly
from the A|Journey|Through|the|CPU|Pipeline dept.

It is good for programmers to understand what goes on inside a processor. The CPU is at the heart of our career.

What goes on inside the CPU? How long does it take for one instruction to run? What does it mean when a new CPU has a 12-stage pipeline, or 18-stage pipeline, or even a "deep" 31-stage pipeline?

Programs generally treat the CPU as a black box. Instructions go into the box in order, instructions come out of the box in order, and some processing magic happens inside.

As a programmer, it is useful to learn what happens inside the box. This is especially true if you will be working on tasks like program optimization. If you don't know what is going on inside the CPU, how can you optimize for it?

A primer for those with a less formal background.


Original Submission

 
This discussion has been archived. No new comments can be posted.
Display Options Threshold/Breakthrough Mark All as Read Mark All as Unread
The Fine Print: The following comments are owned by whoever posted them. We are not responsible for them in any way.
  • (Score: 2, Insightful) by Francis on Saturday October 31 2015, @04:25AM

    by Francis (5544) on Saturday October 31 2015, @04:25AM (#256792)

    It bothers me a bit that people start programming on languages that hide all the complexity of programming and then never get back to see what's actually going on. I'm not sure that you need to go so far as to learn assembly, but the little bit of C that I learned had a huge influence on how I look at programming.

    One of the nice things about C is that it has this way of punishing people for being lazy and incompetent. Sure, there is bad C code just like any other language, but without a lot of the safety features that higher level languages provide, it's a lot harder to delude yourself into thinking you're doing a good job.

    I kind of liked that quote about Ritchie. To paraphrase "Having not hurt enough people with C, Ritchie goes on to invent the pistol with a barrel facing backwards."

    Starting Score:    1  point
    Moderation   +1  
       Insightful=1, Total=1
    Extra 'Insightful' Modifier   0  

    Total Score:   2  
  • (Score: 1) by tftp on Saturday October 31 2015, @04:55AM

    by tftp (806) on Saturday October 31 2015, @04:55AM (#256797) Homepage

    It bothers me a bit that people start programming on languages that hide all the complexity of programming and then never get back to see what's actually going on

    Why don't we go deeper and declare:

    It bothers me a bit that people start programming with machine instructions that hide all the complexity of programming and then never get back to see what's actually going on in the hardware, on the level of individual flip-flops and bus cycles

    If you say that it's going too far, then I have to ask where is the line, and who drew it? When you say string foo = "abc" + def.toString(); it is perhaps wise to be sure that toString() does not come with too high a price. However that is done on the high level. Do you really want to know what Java bytecode is generated? Note that the bytecode is not designed for you, the programmer - it is designed for another machine, the compiler. You may find it very difficult to grok the optimized code; it's not written as a human would have done it.

    The programmer's task is to write working programs. If the program works, the programmer is successful - in the vast majority of cases. Those who write real-time code, or a complex multithreaded code, must know a bit more - but that would be mostly not about the specific CPU, but about the logic of the program.

    without a lot of the safety features that higher level languages provide, it's a lot harder to delude yourself into thinking you're doing a good job.

    C is horrible for writing reliable software. It will allow you to write any garbage into any of your memory locations, and that could be discovered only months later, when your code is burned into a ROM and launched on a rocket toward another planet. A modern language will not allow you to do that in the first place; it will tie your hands at the compile time.

    • (Score: 1) by Francis on Saturday October 31 2015, @05:21AM

      by Francis (5544) on Saturday October 31 2015, @05:21AM (#256801)

      The fact that modern languages tie your hands at compile time is exactly why they're crap for understanding what's going on. C is a good choice because you can get a compiler for a huge variety of platforms, it's close enough to the hardware that you can actually see what's going on there, but not so close to the hardware that you are tied into a specific bit of hardware.

      I'm not suggesting that C is the end all and be all of programming languages, but it's beyond ridiculous to suggest that having your hands tied at compile time is a good thing. You can have similar problems happen with any improperly tested code. High level programming languages solve some of the problems, not all of the problems and if you haven't actually had to experience the consequences of sloppy code, it can be very hard to understand why best coding practices are what they are.

      Given the absolute shit that passes for code these days, I think it's time to just admit that learning on higher level languages and avoiding low level programming languages might have some draw backs.

      • (Score: 2) by frojack on Saturday October 31 2015, @05:43AM

        by frojack (1554) on Saturday October 31 2015, @05:43AM (#256809) Journal

        Still, a background in assembly never hurt anyone intending to start a career in programming.
        You're not going to do any big projects in assembler, nobody does that these days, and truth be known nobody ever did.

        Being aware how many clocks an instruction takes, why you might shift instead of multiply, managing your registers, only to find you ran out in the deepest part of your code and you either have to refactor or stuff something into someplace in memory.

        Yeah you are going to develop in C or something a lot more high-level. But it is worth while to understand what is really going on. What you think is optimized code, might look like a lot different when it gets to the machine level.

        Using a compiler that shall remain nameless, we use to look for optimization opportunities by having the the compiler generate the assembly code. Then we would measure the amount of assembly instructions of each high-level operation..... With a Ruler. By laying the listing out on the floor, and using a ruler to measure how many feet of assembly were generated, we could see hopelessly inefficient compilation. Just changing a little code, usually from complex computation statements to a series of discrete elementary operations made a huge difference in speed.

        --
        No, you are mistaken. I've always had this sig.
        • (Score: 2) by martyb on Saturday October 31 2015, @02:38PM

          by martyb (76) Subscriber Badge on Saturday October 31 2015, @02:38PM (#256893) Journal

          Still, a background in assembly never hurt anyone intending to start a career in programming.

          Most certainly agree. How is data stored in memory? Knowing something about the underlying hardware, I can, for example, understand how a C struct is laid out in memory and how pointers are implemented.

          You're not going to do any big projects in assembler, nobody does that these days, and truth be known nobody ever did. (emphasis added)

          I can only imagine your concept of a big project is different than mine. Unless you consider IBM's VM operating system to be a small project? It was written entirely in assembler, or at least the parts of it that I worked on — system config, scheduler, dispatcher, free storage manager, etc. Wikipedia has some background on IBM's BAL — Basic Assembly Language. [wikipedia.org] Though I do not have first-hand information, I have a strong suspicion that IBM's COBOL and FORTRAN compilers were also written in assembler.

          Yeah you are going to develop in C or something a lot more high-level. But it is worth while to understand what is really going on. What you think is optimized code, might look like a lot different when it gets to the machine level.

          I, too, have witnessed this. Sometimes, for whatever reason, the compiler just gets it wrong. A compiler is a program, and is subject to mistakes, as well. I have found and reported bugs in a couple of them. Still, for the vast majority of the time, compilers do a good enough job and much more quickly than hand-crafted assembler would often be. So, I'm entirely with you on this point.

          Using a compiler that shall remain nameless, we use to look for optimization opportunities by having the the compiler generate the assembly code. Then we would measure the amount of assembly instructions of each high-level operation..... With a Ruler. By laying the listing out on the floor, and using a ruler to measure how many feet of assembly were generated, we could see hopelessly inefficient compilation. Just changing a little code, usually from complex computation statements to a series of discrete elementary operations made a huge difference in speed.

          I appreciate the anecdote. And, yes, compilers occasionally make what seem to be poor choices in particular cases.

          That said, it bears mentioning for those who might not be aware that there are space and time tradeoffs, e.g.: loop unrolling. To illustrate, here is some pseudo code where we assume that x is defined as an array of unsigned bytes:

          for (i=0; i<=7; i++) x[i] = 0;

          could be unrolled as:

          x[0] = 0;
          x[1] = 0;
          x[2] = 0;
          x[3] = 0;
          x[4] = 0;
          x[5] = 0;
          x[6] = 0;
          x[7] = 0;

          By removing increment and conditional branch instructions, one ultimately has fewer instructions to perform — it would run more quickly. So, the assembly listing would be longer, but the code would generally run more quickly.

          On the other hand, one could define, say, an array of long ints (32 bits) defined to be located at the same address as x[] and one would need only two instructions to zero out those locations:

          a[0] = 0L;
          a[4] = 0L;

          Note, however, that if taken to extremes, one gets to the point that the additional size of the code exceeds what can fit in cache and costs a cache miss which could eliminate the time savings. As with most things, there are tradeoffs.

          --
          Wit is intellect, dancing.
          • (Score: 2) by frojack on Saturday October 31 2015, @07:36PM

            by frojack (1554) on Saturday October 31 2015, @07:36PM (#256970) Journal

            I can only imagine your concept of a big project is different than mine. Unless you consider IBM's VM operating system to be a small project? It was written entirely in assembler, or at least the parts of it that I worked on — system config, scheduler, dispatcher, free storage manager, etc. Wikipedia has some background on IBM's BAL — Basic Assembly Language. [wikipedia.org] Though I do not have first-hand information, I have a strong suspicion that IBM's COBOL and FORTRAN compilers were also written in assembler.

            Having some exposure to compiler writing from a long ago contract, I would doubt much of (if any) a compiler was written in assembler. You usually start with some high level language (even something as *cough* "high level" as basic, on some existing platform, to evaluate your compiler source code and produce assembler output. You start small, read something in, say, look at the generated assembler code till it works good enough, "can" that code, and then start working on Writing something out. Building up a collection of library routines.

            Before long, you are writing the compiler for your new language IN ITSELF. Very often, compiler developers would get away from any other language, say C, and end up writing their new FORTRAN or Algol compilers in FORTRAN or Algol, or whatever.

            Often, these Compiler-Compilers are the best way to get from one platform to another. You just change the assembly instructions to those of the new processor.

            --
            No, you are mistaken. I've always had this sig.
        • (Score: 2) by bzipitidoo on Saturday October 31 2015, @03:07PM

          by bzipitidoo (4388) on Saturday October 31 2015, @03:07PM (#256900) Journal

          I like optimizing, it's fun, and I'm pretty good at assembler, but I have to agree with the original poster that in most cases, it's not the best use of your time to optimize at the assembly language level. That really only pays dividends in heavily used code that has already been optimized in every other way. I've had experiences in which I spent time optimizing assembly code, only to have it all made moot because a new generation of CPUs came out which totally changes the optimization. The switch of x86 from underlying CISC to RISC was just such a seismic shift. Before, you'd look for creative ways to employ every available opcode to eliminate as many instructions as possible, after, you'd avoid those complicated instructions that the CPU designers had sidelined, easy to do as they were obsolete. No one needs packed decimal capability in hardware, there are so many other, better ways to handle numbers than that. You were only interested in the packed decimal instructions in those rare cases where they could save a few lines of assembler code doing sometime totally unrelated to packed decimal. CPUs have had bugs before-- as I recall, there was a minor screwup in the early Pentium 4 in which they had the branch prediction backwards. For those CPUs only, redoing loops in your code to refactor the exit condition to avoid that bad branch prediction yielded quite a bit of speed improvement. Another embarrassment in more than one older CPU was that the native division instruction was so slow that division was often, sometimes always(!) faster if done with shifts and subtracts. Even multiplication, which is easier, had corner cases in which a shift is just plain faster.

          First, the problem should be worth solving. Then, design and algorithmic improvements should be sought. Mostly that is replacing very bad choices with a better balanced choice that may not be the very best algorithm but is good enough while being easy to implement, or readily available in a library that is well-tested, stable, and free of any major bugs. It's not done to make slow code faster, it's done to replace bad code that is so slow and/or resource intensive that it is impractical to run or has a time bomb that will cause failure after a few days or weeks of operation, or other major problems, with code that is just good enough to work. A simple example from SQL: break apart a complicated SQL statement that joins 3 or more tables into simpler separate statements that use temporary storage for intermediate results. A triple join is okay when tables are very small, only a few hundred rows, but it scales horribly, and may become unusably slow at just a few thousand rows, to say nothing of 100,000+ rows. That hand optimization depends upon the database engine too, whether it can optimize multiple joins well, or not.

          One bad programmer I worked with briefly had a weird bit of paranoia about initialization. He'd move initialization code into loops, claiming that computers were so fast it didn't matter that a data structure was being initialized 60,000 times instead of once, and it made him feel better about the reliability of the code. Well, it did matter. Once I figured this out about him, I knew that every time I saw a sudden performance drop in any part of the software, it was time to look at the loops to see what he'd messed up now.

          So, assembler optimization is very low on my list of areas to seek speed improvements. Redoing and undoing bad programming tends to pay much bigger dividends. There is a lot of low hanging fruit out there, a lot of bad code.

      • (Score: 2) by HiThere on Saturday October 31 2015, @05:59PM

        by HiThere (866) Subscriber Badge on Saturday October 31 2015, @05:59PM (#256937) Journal

        C is vastly informative and useful AFTER you've learned an assembler. Beforehand, not so much.

        Something like the i6502 is a sufficient assembler to learn. MIX might be even better. It introduces all the basic concepts clearly. C is only clear in this way AFTER you already understand what is happening.

        I once tried (and failed) to write a FORTH interpreter in i6502 assembler. Afterwards I knew a LOT more about programming than I ever learned with Pascal and C. (I probably could have written the interpreter, but I lost interest in FORTH.)

        --
        Javascript is what you use to allow unknown third parties to run software you have no idea about on your computer.
      • (Score: 1) by khallow on Saturday October 31 2015, @06:56PM

        by khallow (3766) Subscriber Badge on Saturday October 31 2015, @06:56PM (#256958) Journal

        but it's beyond ridiculous to suggest that having your hands tied at compile time is a good thing

        What's the number one source of problems in code? The programmer compiling the code. Doesn't matter how good the programmer is. That's why hand tying can be a good thing no matter how good a programmer you are.

        • (Score: 1) by Francis on Saturday October 31 2015, @08:02PM

          by Francis (5544) on Saturday October 31 2015, @08:02PM (#256975)

          That's ridiculous. You can write bad code in any language. This is more or less exactly the same problem as insurance. Insurance doesn't make people any safer either, it just gives people license to do things that are more reckless than what they would have had to do, because there's something there that's supposed to protect them.

          Yes, it will probably catch some types of mistake that lead to problems, but they're not going to catch all the bugs and whether or not you've got some supervision from the compiler, you still have to audit the code and look through it for mistakes, a compiler isn't going to change that. This kind of reminds me of the BSD compiler that was doctored to include a backdoor even when the source didn't include one.

          • (Score: 1) by khallow on Saturday October 31 2015, @08:09PM

            by khallow (3766) Subscriber Badge on Saturday October 31 2015, @08:09PM (#256978) Journal

            That's ridiculous. You can write bad code in any language. This is more or less exactly the same problem as insurance. Insurance doesn't make people any safer either, it just gives people license to do things that are more reckless than what they would have had to do, because there's something there that's supposed to protect them.

            It's a correct observation. So what makes it ridiculous?

            And the point of insurance is not to make people safer.

            Yes, it will probably catch some types of mistake that lead to problems

            There we go.

            but they're not going to catch all the bugs

            It's not perfect so it's no good.