2012-05-28
Yes, git add makes a difference (no matter what people think)
One of the things said about git is that
it's less user friendly and takes longer to learn than Mercurial; the first exhibit for this difference
is usually git add and by extension git's index. Unfortunately,
a common reaction
among git fans to both the general issue and git add in specific
is a kind of defensive denial, where they hold forth that it's not
that difficult and people learn it fine and really, git is user
friendly.
You may already have gotten an idea of my views on this. I'm here to
tell you, from a mostly outsider perspective, that git add really
does make a real difference in initial user friendliness, one that
makes Mercurial easier to pick up and use for straightforward uses.
(I've used git to a certain extent, for example for my Github stuff, but I am not up to the experienced user level. I'm not really at that level with Mercurial either, partly because I haven't needed to be and partly because I'd rather learn git; Mercurial is easier but I like git more.)
Before people freak out too much, let me be explicit: all of this is about initial user friendliness, the ease of doing straightforward things and picking up the system. In the long run I think that the git index is the right design decision (for a programmer focused VCS) because it creates an explicit model for doing a number of important but tricky things, a model that can be manipulated and inspected and reasoned about, and once you learn git and use it regularly dealing with the index becomes second nature. But people generally do not defend the index in these terms; instead, they try to maintain with a straight face that it's no real problem for people even at the start.
(If you think that the index does not cause problems for git beginners, I would gently suggest that you trawl through some places where they ask questions.)
The usability problem with git add is not just the need for git
add itself as an extra step, it is that the existence of the index
has additional consequences that ripple through to using other bits of
git. For example, let us take the case of the disappearing diff:
; git diff a [...] -hi there +hi there, jim ; git add a ; git diff ;
If you already know git you know what's going on here (and you're going
to reach for 'git diff --cached'). If you're learning git, well, your
change just disappeared. Of course this happens the other way around
too; 'git diff' shows you nice diffs, then you do 'git commit' and
it tells you nothing to commit. Wait, what? The diffs are right there.
(There's worse bear traps in the woods for beginners, too, like doing a
'git add' and then further editing the file. Here 'git diff' will
show you a diff but it is not what will be committed.)
All of this is a cognitive burden. When you use git, you have to
learn and remember the existence of the index and how this affects what
you do, and you probably need to take extra steps or pay extra attention
to what 'git commit' and so on tell you. This cognitive burden is
real, although it can (and will be) overcome with familiarity and what
it enables has important benefits. It is a mistake and a lie to try to
pretend otherwise. Honesty in git advocacy is to say straightforwardly
that the index is worth it in the end (possibly unless you have simple
work patterns).
(A system where the index or its equivalent is an advanced feature, one
not exposed by default, really does have a simpler initial workflow. If
it's designed competently (and Mercurial is), everything 'just works'
the way you expect; hg commit commits what hg diff shows you and so
on. In real life this makes a difference to people's initial acceptance
of a new VCS, especially if the simple workflow is adequate for almost
everything you'll ever do with the system. This is not true of the sort
of advanced VCS use that programmers can practice routinely, but it can
be of other VCS uses.)
Sidebar: the problem with 'git commit -a'
At this point some people may come out of the woodwork to tell me about
git commit -a, or even about creating an alias like 'git checkin'
that always forces -a. There are two pragmatic problems with this.
First, the index still exists even if you're trying to pretend otherwise.
This means that you can accidentally use the index; you can run git add
because something said to, or you can run straight git commit, and so on.
All of these will create confusion and cause git to do what (to you) looks
like the wrong thing.
(In fact you have to run git add every so often, to add new files.)
Second, it is not at all obvious from simply reading documentation that
using git commit -a is a fully reliable way of transmuting git into
Mercurial. Maybe it is, maybe it isn't, but as a beginner you don't
know (not without doing more research than I myself have done). Because
many git operations are fundamentally built around the existence of
the index, the safest assumption to make is that the index really does
matter and git commit -a is probably an incomplete workaround.
(For example, at the point where you do git add to add a new file
you'll become familiar with git diff HEAD in order to get the true
diffs for what will be committed when you run git commit -a, which I
hope illustrates my point adequately. And maybe there's a better command
for doing that, which also illustrates my point because git diff HEAD
is what I came up with as a relative git novice.)
2012-05-21
More on CISC (well, x86) versus RISC
Sometimes I am sadly slow at replying to comments. So I am hoisting my my reply to a comment on my first entry on why x86 won against RISC into an entry.
From --M's comment:
It's my understanding that modern CPUs don't execute the x86 machine code directly, they're actually RISC processors that execute a program that can run and optimise x86 instructions.
While this is true as far as I know, it doesn't mean that RISC won in the end. Not really, and not as RISC was understood back in the early 1990s.
Roughly speaking, we can say that there are two aspects of RISC, RISC as an internal CPU implementation technology and RISC as an Instruction Set Architecture (ISA) design approach. Of course these were initially tied together; it was your use of a RISC ISA that made it possible to create a simple CPU for that ISA using RISC implementation technology. A more complex ISA would not have allowed you to build a RISC CPU for it. The early 1990s view of 'RISC' called for both aspects, with a RISC ISA implemented by a RISC CPU.
However, as nothings noted in his comment, all sorts of CPU designers started stealing implementation ideas and technology from RISC CPUs pretty much the moment they were created (well, when people started writing about the ideas). In a sense this should be expected; from one view, RISC CPU cores that run code translated from a different ISA are simply the logical extension of microcode. Since the microcode ISA is not user-visible, CPU designers are free to change it completely from CPU generation to CPU generation and thus free to adopt good ideas for microcode architecture wherever they can find them.
RISC as a CPU implementation technology is alive and well in the x86; it 'won' in that sense. But that sense is relatively meaningless for most purposes, because we don't really care how CPUs go fast. What people really cared about in the early 1990s was RISC ISAs, and (1992 style) RISC ISAs unambiguously lost. CPUs implementing the x86 ISA were pushed to performance and price levels that could not be matched by CPUs implementing RISC ISAs and as a result RISC ISAs have basically died out.
Sidebar: why no one ever transitioned from x86 CPUs
nothings in his comment:
Of course, it's true Intel also had a process advantage for Itanium, so the story is a little more complex, and probably biased towards 'desire for x86 compatibility' (although the Mac certainly was able to switch processors, so I'm not sure it would have been impossible for Windows to have, so I think it may also just have been just a bad design in Itanium [...]
I've sort of written about this before. My short answer is that making such a transition away from x86 would have required a new CPU that delivered an actual tangible benefit to users over x86 CPUs and (due partly to AMD) there was no such alternate CPU available. Every CPU with better performance than x86 had significant drawbacks such as much worse price/performance ratios.
Fundamentally, Apple successfully transitioned from PowerPC to x86 because there was a benefit to users. x86 CPUs could do things that PowerPC CPUs could not; they ran faster, they ran cooler, and so on. As a secondary reason, Apple was able to execute the transition because they could simply not make future, better PowerPC machines that ran Mac OS; if users wanted to upgrade a machine to get more power, they had to go to x86. Windows has never had this option; pretty much as long as faster x86 CPUs appear, people can build machines that use them and run Windows.
2012-05-15
Some stuff on 'time since boot' timestamps
From today on Twitter:
@standaloneSA: Is it just me, or does it seem silly that the #NetFlow timestamp field for the flow references "ms since the router booted". Seems obtuse.
@thatcks: @standaloneSA It's probably easy to implement in the router and it creates an absolute ordering w/o worries about time going backwards.
In the Twitter way, this is a little bit cryptic so I'm going to elaborate on my guess here.
Suppose that routers were supposed to generate an absolute timestamp for their events instead of this relative one, for example UTC in milliseconds. This would create two problems.
First, routers would somehow need to know or acquire the correct UTC time (with millisecond resolution) and then maintain it. This is to some degree a solved problem but it adds complexity to the router. It also leads to the second problem, because a router is unlikely to boot with the correct UTC time (down to the millisecond).
The second problem is that the moment you have a system generating an absolute timestamp you need to deal with the certainty that the correct time, as the system sees it, will jump around. The router will boot will some idea of the UTC time but it's quite likely to be a bit off (remember that we're calling for millisecond accuracy here), then over time it will converge on the correct UTC time. As it does so, its version of UTC time may go forward abruptly, go backwards abruptly, or go forward more slowly than UTC time is really advancing. Backwards time jumps screw up event ordering completely, and all of the options screw up the true relative time between events; if you have two events timestamped UTC1 and UTC2, you actually have only a weak idea how long it is between them.
The valuable property that milliseconds since boot has is that it is a clear monotonic timestamp. It only ever goes forward and it goes forward at what should be a very constant rate, which means that it creates a clear order of events and a clear duration between any two events (well, for events from the same stream of monotonic timestamps). Monotonic timestamps are not a substitute for absolute time but neither is absolute time a substitute for monotonic timestamps; you really need both, which means that you need a map between them.
There are two possible places to build such a map: each device can do its own or it can be done in a central aggregator. I believe that the right answer is to do it in the central aggregator because this means that you have only a single version of absolute time, the aggregator's view (each device, aggregator included, may have a slightly different view of the current 'correct' absolute time for the reasons outlined above). Using only a single version of absolute time means that you have a single coherent map of all of the monotonic timestamps to (some) absolute time.
(Of course you need devices that generate monotonic timestamps to tell you when they reset their timestamps, eg when they boot.)
My impression is that using elapsed time since boot is actually common in a number of environments. For example, Linux kernel messages are usually reported this way these days (which has its own issues if you're trying to work backwards to roughly when in absolute time something happened).
2012-05-13
A basic step in measuring and improving network performance
There is a mistake that I have seen people make over and over again when they attempt to improve, tune, or even check network performance under unusual circumstances. Although what set me off now is this well intentioned article, I've seen the same mistake in people setting off to improve their iSCSI performance, NFS performance, and probably any number of other things that I've forgotten by now.
The mistake is skipping the most important basic step of network performance testing: the first thing you have to do is make sure that your network is working right. Before you can start tuning to improve your particular case or start measuring the effects of different circumstances, you need to know that your base case is not suffering from performance issues of its own. If you skip this step, you are building all future results on a foundation of sand and none of them are terribly meaningful.
(They may be very meaningful for you in that they improve your system's performance right now, but if your baseline performance is not up to what it should be it's quite possible that you could do better by addressing that.)
In the very old days, the correct base performance level you could expect was somewhat uncertain and variable; getting networks to run fast was challenging for various reasons. Fortunately those days have long since passed. Today we have a very simple performance measure, one valid for any hardware and OS from at least the past half decade if not longer:
Any system can saturate a gigabit link with TCP traffic.
As I've written before in passing, if you have two machines with gigabit Ethernet talking directly to each other on a single subnet you should be able to get gigabit wire rates between them (approximately 110 MBytes/sec) with simple testing tools like ttcp. If you cannot get this rate between your two test machines, something is wrong somewhere and you need to fix it before there's any point in going further.
(There are any number of places where the problem could be, but one definitely exists.)
I don't have an answer for what the expected latency should be (as
measured either by ping or by some user-level testing tool), beyond
that it should be negligible. Our servers range from around 150
microseconds down to 10 microseconds, but there's other traffic going
on, multiple switch hops, and so on. Bulk TCP tends to smooth all of
that out, which is part of why I like it for this sort of basic tests.
As a side note, a properly functioning local network has basically no packet loss whatsoever. If you see any more than a trace amount, you have a problem (which may be that your network, switches, or switch uplinks are oversaturated).
The one area today where there's real uncertainty in the proper base performance is 10G networking; we have not yet mastered the art of casually saturating 10G networks and may not for a while. If you have 10G networks you are going to have to do your own tuning and measurements of basic network performance before you start with higher level issues, and you may have to deliberately tune for your specific protocol and situation in a way that makes other performance worse.