There's a lot of reasons. I was in the same position for more than six years, but I didn't necessarily want to be pigeonholed as a compiler guy. I was working remotely from London, with all the downsides that remote working entails: relative lack of camaraderie, out of sight / out of mind, disconnection from the actually quite vibrant local tech scene (I live about 3 miles from Old Street roundabout). But more than anything else, I'd fallen out of love with Delphi, and could no longer motivate myself to try and make it better - the gap I'd try to bridge would be a gap too far for the market to bear.
I'm currently taking stock, thinking about what I want to do next. No specific plans, but it's going to be fairly different than what went before!
I've just come back from an excellent holiday: a motorbike trip from London to southern Spain, via LlĂvia and Andorra. The highlight of the trip was the roads in the Spanish Pyrenees. Many almost abandoned (frequently went for 10 to 30 minutes without seeing another vehicle), mostly good surface, with bends that go on for miles and miles. Not to forget the fantastic views. Largely good weather - we (myself and my girlfriend, on separate bikes) stuck mostly to the east coast of Spain, and only really got wet on a couple of days, when travelling through France. Not too hot, either, one of the primary reasons we went at the tail end of April.
I want to note down some of the things I've learned over the past few touring trips I've taken, and of course distilled from year-round riding through English weather. Perhaps it will help someone else out there, save them from getting wet too many times, or buying more gear than necessary.
For context: I'm a sport-touring rider, with a bias of perhaps 3:1 sport to touring; that is, I prefer a light bike, no pillion - luggage strapped to the pillion seat - and am not too concerned about being super-comfortable on the motorway at motorway speeds; but I like a fairly upright seating position and some wind protection. I'll tolerate motorways if they're necessary to get to the really fun roads sooner, where I can throw the bike around more. I ride a Kawasaki ER6f; as a medium-capacity twin, it's got lots of torque at lower speeds without needing for a screaming engine, but is still fairly light and sporty for having fun, particularly when you keep the revs up around 6k.
In and around London though, I usually ride my scooter, an SH300. A bit of a sleeper, it often surprises guys on big bikes. It looks like a 125 - especially since maxi-scooters are fairly rare in the UK and are somewhat looked down upon - but has significant acceleration to about 40mph. It still rides like a scooter and is able to get in and around traffic with ease that's a lot harder and riskier on a big bike, primarily because of the low centre of gravity.
Motorcycle clothing for touring: my key tip is layers. Specifically, for a jacket, look for something relatively light with loads of ventilation options (i.e. zip vents, zip-off panels; this generally means textiles), but is still somewhat windproof when fully zipped up, and has a zip-in waterproof liner. Generally, people go touring in or towards good weather, so ventilation is very important, but good roads are usually in hilly areas, often shaded with trees, and times with least traffic often don't coincide with peak heat, so you need the windproofing and waterproofing to block out cold air. For extra warmth, pack a fleece, wool top, etc. No need to rely on a third liner for the jacket.
In steady rain, however, a zip-in waterproof liner isn't good enough. It'll probably keep you mostly dry (though in my experience the zip area always leaks from pools of water forming at folds and creases, combined with wind pressure), but there is another problem: the jacket outer will get soaked through, and it will carry the water down your arms and into your gloves. The waterproof liner generally only fits inside rather than outside your gloves, so it will guide the water into your gloves. So my solution is a motorbike rain jacket - i.e. one which is over-sized for its proportions so it fits on top of existing jackets. I have a high-vis fluorescent yellow Bering jacket, for extra safety in torrential downpours, which limit visibility much like fog, especially on the motorway with spray thrown up by tyres. It's very important that this jacket has cuffs with velcro or some similar positively adjustable system (not just elastic) that fits over your gloves (so water can't run into the gloves), but tightly, so that rain and wet air can't get up your sleeve.
For rain in London, I generally wear my winter jacket, which has a Gore Lock Out zip. This is a double-interlocking rubber zip, and is completely waterproof and windproof; it also needs lubrication with silicone oil once a month to keep it smooth.
For trousers, I get by with permanently ventilated textiles with zips for further ventilation. To stay warmer, I can wear jeans underneath; but the fairing on my bike keeps off most of the wind, unless it's very cold, and legs don't feel the cold nearly as much as the torso. Similarly to up top, I have waterproof over-trousers for rain, and in case it gets very cold. The weak area of waterproof trousers is the crotch. Water generally collects on your torso and runs down until it pools there. If the crotch isn't totally waterproof, it will leak. Most waterproof over-trousers don't have reinforced crotch areas, and as such they're good for perhaps 10 hours of light wear before they'll start leaking. If you can find them, get ones with a reinforced crotch and stay away from ones with simple seams right where your legs split; so far, I've had to get by with purchasing new over-trousers when the forecasts say I'll be riding through heavy rain.
Have at least two pairs of waterproof gloves for riding when expecting rain (and even sunny summer trips usually have a thunderstorm at some point). Even waterproof gloves will get really soggy leather on the outside, and damp and clammy inside. After the rain has let up, it's quite pleasant to pull over and put on some dry gloves. Even better if you have heated grips; dry and toasty fingers make all the difference, and help keep you sharp on the controls, even if you're a little wet elsewhere. I have found waterproof over-gloves quite dangerous; they're generally very slippy and hard to get on, much more trouble than they're worth. Same thing with waterproof over-boots, they're usually a poor fit and interfere or get caught on the gear lever; can be dangerous. Get quality waterproof boots; bring two pairs of boots if you don't think you can stand wearing waterproof non-ventilated boots in the expected weather.
Luggage: I get by with an oversized (Givi E55) top-box containing light things for quick access (visor sponge and wipes, spare gloves, spare earplugs, spare light bulbs, spare straps, extra rain gear), and for keeping my helmet and gloves in when parked up (this is why it's oversized) or shopping when staying at a self-catering place (exceeding the load limit for short distances at low speed); and a holdall / gym bag with rectangular base, strapped tightly to my pillion seat using two 220 daN (decanewton, ~220kg) luggage straps, so that it's completely rigid and won't move at all as the bike moves, even in vigorous cornering. I don't bother with a waterproof bag; I put my clothes and other things I want to keep dry in heavy-duty rubble sacks. At the bottom of the bag, I keep my laptop; currently a MacBook Air, on top of a long wooden cutting board approximately the shape of the bag, wrapped up together in a rubble sack. The idea here is to prevent too much bending force being applied to the laptop by the pillion seat. On top, there's clothes intermediating the force from the straps. So far my laptops have survived (in the past, I was using a Toshiba Portege) fine, except for a few minor marks on the screen from being pressed into the keyboard.
Touring by motorbike has been some of the most fun I've ever had in my life. Before I learned to ride, I didn't think much about bikes; at best, I dismissed riders as either adrenaline-junky boy-racers, or hairy old fogeys. The practicality of a scooter as a means of getting around London was my gateway drug. One thing lead to another, and now I'm hooked, for life, if I don't injure myself too badly along the way!
Delphi XE2 introduced namespaces across the runtime library. This stressed unit name lookup inside the compiler, and led to some severe performance regressions in certain cases. So during the runup to the XE2 release, I fired up a profiler and started investigating. It turns out there were numerous situations where lookups were being performed repeatedly with the same arguments, and logically the results should have been consistent across these repeated calls. A relatively cheap and easy fix seemed to be memoization. So I added a caching infrastructure to the compiler and used the profiler to guide me where to inject it.
For the most part - particularly for full builds - the cache works really well. But I've had some reports of bugs that I suspected were caused by the cache hanging on to function results slightly too long, and upon investigation, this turned out to be true. The problem with caches is usually in invalidation; if you don't invalidate the cache soon enough and in all required situations, you end up serving stale results. So there are a few bugs lurking in the Delphi compiler here, which I'm seeking out and squashing.
Some good news, however; I had anticipated that this might be the case, so I added a super secret switch that enables diagnosing a probable cache failure: caches can be selectively disabled, and if a problem goes away with the cache disabled, it's probably because of stale results.
Caches can be disabled by setting an environment variable:
The above environment variable setting disables all the compiler's caches. By including fewer than the four separate cache names, the problem can iteratively be narrowed down to a specific cache.
I've just been fixing one bug caused by the cache that brought home how needed it is. The project is large; almost 2 million lines. An initial build with the cache enabled takes about a minute on my machine; the bug exhibits itself in later incremental compiles when modifying the source code and pressing F9, producing spurious errors. However, once I disabled the cache (or rather, I recompiled the compiler with cache sanity checking enabled, which still filled out the cache, but also invoked the underlying logic, and simply compared the results to verify the cache), the build time took nearly 3 hours!
Note: invalidation is most likely to be a problem on incremental compiles, rather than full rebuilds, especially from within the IDE. The reason is that the compiler may have lots of stale data for one half of an incremental compile that it later decides is out of date (e.g. a dependency changed); this can leave a bunch of stale entries in the cache for all the memoized function calls that occurred in the first half of the compile. The cache is strictly per-compile; it keeps no data across multiple compilations, even partial compilations.
I was answering a question on Stack Overflow, but the user didn't have the latest version of Delphi. My answer included converting an interface to an object instance, which is made possible with the as cast on interfaces in recent Delphi versions. But there is another way of doing it, exploiting the regularity Delphi interface vtable implementations:
{$apptype console}
function Intf2Obj(x: IInterface): TObject;
type
TStub = array[0..3] of Byte;
const
// ADD [ESP+$04], imm8; [ESP+$04] in stdcall is Self argument, after return address
add_esp_04_imm8: TStub = ($83, $44, $24, $04);
// ADD [ESP+$04], imm32
add_esp_04_imm32: TStub = ($81, $44, $24, $04);
function Match(L, R: PByte): Boolean;
var
i: Integer;
begin
for i := 0 to SizeOf(TStub) - 1 do
if L[i] <> R[i] then
Exit(False);
Result := True;
end;
var
p: PByte;
begin
p := PPointer(x)^; // get to vtable
p := PPointer(p)^; // load stub address from vtable
if Match(p, @add_esp_04_imm8) then
begin
Inc(p, SizeOf(TStub));
Result := TObject(PByte(Pointer(x)) + PShortint(p)^);
end
else if Match(p, @add_esp_04_imm32) then
begin
Inc(p, SizeOf(TStub));
Result := TObject(PByte(Pointer(x)) + PLongint(p)^);
end
else
raise Exception.Create('Not a Delphi interface implementation?');
end;
type
ITest = interface
procedure P;
end;
TTest = class(TInterfacedObject, ITest)
F: array[0..200] of Byte;
procedure P;
end;
procedure TTest.P;
begin
Writeln('Hello');
end;
procedure Go;
var
orig: TTest;
i: ITest;
o: TObject;
begin
orig := TTest.Create;
i := orig;
i.P;
o := Intf2Obj(i);
Writeln(o = orig);
end;
begin
Go;
end.
This approach is predicated on the idea that the stub code that Delphi produces for turning the implicit interface argument into an instance argument is predictable. It generally only has two forms, depending on how much of an adjustment it needs to make (which itself depends on how much instance data there is). It ought to work for almost all 32-bit Delphi interfaces that have been implemented by instances, where the vtable was created by the compiler. If not, other stub variations can be analyzed (in the IDE CPU view) and handled too. It ought to be pretty safe, as only this specific code is permitted. It could be made even safer by ensuring that the stub ends with a JMP and that the instance returned has a ClassType descending from TClass.
Update: After a Google search I note that Hallvard also wrote about this some time ago. His code is a little tighter than mine (using Integer constants rather than byte-by-byte comparison); in my defense, I only spent a few minutes on this...
I was using the Chrome browser on my laptop the other day (some browser compat problem). I don't normally use it; Firefox is my preferred browser. I couldn't help but notice how peculiarly laggy it felt to use. It came down to vertical scrolling: this highly common task - for any long article you'll be doing a lot of it - felt jerky and unpleasant. So I did a quick ad-hoc experiment: open up the same web page in both browsers with viewports of the same size, grab the vertical scroll bar, and wiggle it up and and down continuously so the browser is constantly redrawing text.
This is a crop from Process monitor on my desktop machine, which has 8 logical cores, i.e. 4 physical cores with hyperthreading. That means that 100% CPU usage on one core shows up as 12.5% CPU usage. The big wide bump on the left is the CPU usage while I was fiddling with Chrome; the one on the right is Firefox. (The bump in the middle is sqlservr.exe, which wakes up and does meaningless busywork every 60 seconds.) You can see that Chrome uses perhaps 50% more CPU usage on this task than Firefox, with more time spent in the user process and (proportionately) less in the kernel (presumably shifting bits around). Eyeballing an average of CPU usage, Firefox ranged from 6% to 7.5%, while Chrome pegged at 12.5%. Chrome is simply less CPU-efficient redrawing while scrolling, and it's very obvious to my eyes. (I did the same experiment with IE, and it was in the middle, with about 9% CPU.)
I might hazard a guess that Firefox spends more memory on caching bitmaps of the web page, or some similar trick trading off space for time. In any case, it's one of the reasons Firefox is still my primary browser, and also why I'm completely unconcerned about its memory usage. I have yet to encounter significant paging because of Firefox, not least because it's a 32-bit process, limiting its maximum usage, and I have alternately 12G and 4G of memory in my primary desktop and laptop respectively.
Mind you, when I add up the private working set for all 8 chrome.exe processes apparently needed to display this web page, they add up to 86M, only 1M less than my recently restarted firefox.exe session at 87M. (Comparing other memory usage numbers is awkward, as non-working-set memory isn't relevant, while non-private memory would be double-counted with Chrome.)