Everyone knows that there are "ten-time" programmers who are 10 times more productive than an ordinary programmer. We can’t measure performance, so we don’t know if it’s true. But in fact, there are quite a few people who are unusually productive, enough to prove the existence of a "tenfold programmer."
How do they achieve this?
It is often considered that tenfold performance results from tenfold abilities or tenfold knowledge. I do not think so. I do not want to say that abilities or knowledge are useless. But for many years I have noticed that the most important thing here is a tenfold
intelligibility . The trick is to constantly evade lousy jobs.
')
And under it, I do not mean "intellectually unsatisfactory." The definition of lousy work is that its result goes straight to the toilet.
I myself redid a lot of lousy work, especially when I was inexperienced and naive. (One of the great advantages of experience is that a person becomes less trusting — and this more than compensates for the evaporation from school memory).
I will give you just an illustrative example of the work of hard, developing and going straight to the toilet: my adventures a decade ago with a fixed point.
Do you know what “fixed-point arithmetic” is? I'll tell you. This is when you work with integers, but pretend that these are fractions, implicitly assuming that the integer x is actually x / 2 ^ N for some value of N.
To add two numbers, you simply calculate x + y. For multiplication, you need x * y >> N, because just x * y will be x * y / 2 ^ 2N, right? It is also necessary to be careful that this rubbish does not overflow, somehow deal with different N in the same expression, and so on.
Then, in the early 90s, I ported software to the processor, which was developed in our company. The floating point hardware unit was not planned for it - “we will do everything with a fixed point”.
Here are some of my then affairs:
- There was a ready-for-nothing C ++ template called InteliFixed <N> (it still exists, I don’t cheat). I put in a lot of work to make it, um ... slyat on a slow hand (is it the opposite of “fast hand”?). It was necessary, for example, to make operator + commutative when it receives two fixed-point numbers of different types (what will be the type of result?); make terrible inline assembly code that performs 64-bit intermediate multiplications, inline better; etc.
- My boss told me to keep two versions of the code — one with a floating point, for noble developers of algorithms, and the other with a fixed one — for us, laborers, messing around with working code. Therefore, I synchronized these versions manually.
- The boss also ordered me to figure out how to run part of the code with a floating point, but part did not , in order to find errors in the loss of accuracy. Therefore, I wrote a heuristic C ++ parser that automatically merged two versions into one. He took some functions from the “floating” version, and others from the “fixed” version, selecting them according to a special file of the header type.
Of course, all this shit did not run and even it didn’t compile, exactly? Therefore, I made macros, thanks to which you passed to the function not vector <float> &, but REFERENCE (vector <float>), and wrote a creepy piece of code that supported during execution the case when vector <InteliFixed> is passed (the code inside functions turned it into a vector <float>). - And besides all this meta-programming, there was, of course, programming itself. For example, solving a 5x5 system of equations to match polynomials to noisy data series, all with a fixed point. I managed to make it work using creepy tricks with normalization, and the assembler code used 96 bits of precision. My code worked even better than single-precision floating point code without normalization! Wow!
Months and months, I worked in full force, producing a complex and debugged code - everything, as usual.
But what I really needed to do was:
- Convince the authorities to push the stinky floating point unit into this smelly chip. This would require not so many square millimeters of silicon - I had to insist on calculating exactly how much (hardware FPU was added in the next version of the processor).
- If this did not work out, I had to get to the simulator of the circuit, measure the cost of floating-point emulation and apply it in those places of the code where it is acceptable (gradually we did so).
- To tell the boss that it is impossible to synchronize the two versions as he wants - they will diverge farther and farther away, and in the end even with the help of the devil himself will not be able to merge them and run the resulting code (of course, that’s what happened as a result).
Why did all this epic degenerate into many months of lousy work, for which it was possible to do something useful? Because I did not know what was what; because I did not guess that it was possible to argue with the boss; and because the work was creative and interesting. Which very quickly went to the toilet.
The most difficult thing about “managing” these tenfold people — those about whom everyone knows they are very productive — is to convince them to take up the task. (everything else is much easier - they themselves know how much; if they come to do something, it will be done).
You were waiting for something else, admit it? I mean, if you're so productive, what do you worry about? You work fast; the worst thing that awaits you is that as a result nothing will work out - and then you will quickly do something else, right? I mean, this is slow and not very productive people have to be picky - they are slower and they have less opportunity to switch to something new - right?
But this is just an optical illusion: more productive people do not work so fast - not ten times faster. The reason they
seem 10 times faster is that almost none of what they have been done is thrown away, in contrast to the heap of work that others do.
And thrown cases in productivity are not counted. You regard a person as “the guy who made X”, where the usefulness of X is known to everyone - and forget about all those Y who were not particularly useful, despite the efforts and talent that went into their production. Even if this was something to blame - the manager, or lack of time, or whatever.
If you take well-known examples - you know Ken Thompson from C and Unix - but not from Plan 9, in fact, and not from Go. So far the opposite is true - Go got your attention only because it was the brainchild of those guys who made Unix. You know Linus Torvalds, although Linux is a Unix clone, and Git is a Bitkiper clone, actually,
because these are clones of successful products, and they succeeded because they appeared on time.
The first thing you look at is not originality and not how difficult it was to write, or how good this thing is by some specific criterion: you see how you can use it.
A ten-time programmer, as a rule, is satisfied with a real war, just to avoid doing something that will never be used.
One of these smart people once asked me about
checkedthreads , which I had just finished: “Does anyone use this?” With the same branded irony. I said I did not know; On Hacker News, someone wrote in the comments that he might try.
I know this is a wonderful thing; with its help, you can find all your errors in multi-threaded programs. But this is not a replacement for pthreads, you have to write code using new interfaces - good, simple interfaces, but not the ones that you already use. So it is likely that few will be interested in my library; Although
Helgrind and
thread sanitizer have a lot of false positives, they at least work with the interfaces that people use extensively.
Why would I do this then? Because it took only one day to write the first version (I haven’t decided yet to start parallel nested loops and all that), and I thought that there would be a chance if I wrote about my program in a blog (which I’m doing right now ). If I had written several posts in which I explained how it is practically possible
to track errors in old-fashioned parallel, shared memory, C code even easier than Rust, or Go, or Erlang, then maybe people would pay attention.
But even so, there are too many chances for failure among that tenfold crowd, which I know personally - don't even try. Even if we use something like checkedthreads at our place and very successfully. Actually, I heard the ironic question from the guy who put a lot of work into this innermost version - because it’s very likely that we will use the program.
Do you see? Do not do what is likely to fail - that's where the performance.
How to choose what to work on? There are many things to consider:
- Is there an alternative already available? How bad is she ? If you are tolerant, then don't do anything yourself - a good thing is difficult to improve, and even harder to convince everyone that this improvement is worth the transition to the new version
- How important is this thing? Without it, nothing will work or is it just ryushechki, which no one will notice?
- How much labor will users need to get some benefits? Does it already work with existing code or data? People will have to learn something new or will they be able to work as they used to?
- How many people should know about this thing, so that at least it spread? Will users run the code without knowing anything about it, because it comes with the old code, or will they have to install something? (It’s often better to get some function automatically and then do it in practice, than to install something new and forget it. Think about how many people eventually learned some new function in Excel; and how many backup programs are used in the background).
- How much code gives what benefit? Optimizing the loss of the pulse of a small kernel, compressing sounds in the mpeg format, looks more useful than viewing a million lines of code to achieve acceleration by 1.2 times (although the latter option can be useful by itself; but usually it requires ten times more programmers , but not one "tenfold" programmer).
- Can a new opportunity protect itself? If users do something wrong (or “wrong”), will it become silently useless (like static code analysis when the analyzer no longer understands the program) or stop working until the error is corrected (like checking for an output of the array) )?
- ...
You can easily continue this list; His main idea - what is the probability that I will finish this thing and then it will actually be used? The same idea applies recursively to every function, subfunction, and line of code: is the whole thanks to them useful? And do I not spend time on something else that will bring more benefits?
Of course, it is still more difficult; some useful things are valued above others for various reasons. That's where Richard Stallman appears and demands that we call Linux “GNU / Linux” because GNU wrote most of the user programs. And although I am not going to call the system “Gnu-Linux”, its claims, unfortunately, have their own truth, in the sense that yes, unfortunately, some hard and important work is not as noticeable as other hard and important work.
But justice is another topic. In the end, hardly tenfold performance will give you tenfold compensation.
Therefore, there are not so many reasons to “cheat” and seem more productive than you are. The main reason for which people are productive is an awl in the ass, not giving rest, and not some tangible benefits.
What I want to say is: to do more, you need not so much
to achieve success faster (although this does not hurt), as it is
less likely to fail . And not all failures are caused by lack of knowledge or experience; most of them happen when the program is abandoned at the stage when it is still impossible to use it - or because no one was going to use it from the very beginning.
Therefore, I, as a person who has written a lot of code for the toilet bowl, believe that it is necessary to achieve productivity not so much with
work as with the
lack of work — not to engage in what will eventually be thrown into the garbage bin.
Posted by: Joseph Kreinin
Original:
www.yosefk.com/blog/10x-more-selective.html