Does Delphi make you a better programmer? Is Object Pascal code more readable?
There is an algorithm with a mystery constant that rose to fame in John Carmack’s Quake III Arena C code for quickly estimating the inverse square root of a 32-bit floating-point number.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
float Q_rsqrt( float number ) { long i; float x2, y; const float threehalfs = 1.5F; x2 = number * 0.5F; y = number; i = * ( long * ) &y; // evil floating point bit level hacking i = 0x5f3759df - ( i >> 1 ); // what the f**k? y = * ( float * ) &i; y = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration // y = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed return y; } |
Table of Contents
Understanding the Code
It is based on Newton’s method for estimating roots. Additionally, it converts the floating-point number to an integer, uses bit shifting, and starts with an approximation of √2^127. The commented out line allows for an additional iteration to improve the estimate, which was not used in Quate III Arena. You can read more about it in Wikipedia or watch some YouTube videos on the topic [including a very deep dive]. Here is a nice high-level video:
Can Delphi Make it Better?
Facebook user Toon Krijthe showed how much clearer and simpler the code would be if it were implemented in Object Pascal/Delphi.
1 2 3 4 5 6 7 8 9 |
function rsqrt(const ANumber: Single): Single; var ResultAsInt: UInt32 absolute Result; begin Result := ANumber; ResultAsInt := $5F3759DF - (ResultAsInt shr 1); Result := Result * ( 1.5 - (ANumber * 0.5 * Result * Result)); // 1st iteration // Result := Result * ( 1.5 - (ANumber * 0.5 * Result * Result)); // 2nd iteration, this can be removed end; |
It makes use of the absolute keyword to map the floating-point number to the integer, which avoids all the “evil floating point bit level hacking.” This is something I love about Delphi and Object Pascal: It gives you access to pointers, and raw memory, etc. but doesn’t force you to use it when you don’t want/need to. Shorter code isn’t always easier to understand, just take a look at any regular expression, but this is an improvement because it removes so much extraneous code. Much more readable.
Object Pascal is so readable it makes programmers better as it makes their code more readable and maintainable. Don’t get me wrong, you can write spaghetti in any programming language/syntax, but starting with a readable one helps. This is why there are so many “Legacy” Delphi programs: They are successful and maintainable. Code that doesn’t work, or isn’t maintainable gets thrown out or re-written.
Need More High-Performance Math?
If you are looking for more fast math routines for Delphi, check out the High-Performance FastMath Library by Embarcadero MVP Erik van Bilsen of Grijjy fame.
Check out FastMath if you need faster calculations that don’t need full accuracy.
FastMath – Fast Math Library for Delphi
FastMath is a Delphi math library that is optimized for fast performance (sometimes at the cost of not performing error checking or losing a little accuracy).
This makes FastMath ideal for high-performance math-intensive applications such as multimedia applications and games. For even better performance, the library provides a variety of “approximate” functions (which all start with a Fast
-prefix). These can be very fast, but you will lose some (sometimes surprisingly little) accuracy. For gaming and animation, this loss in accuracy is usually perfectly acceptable and outweighed by the increase in speed. Don’t use them for scientific calculations though…
Design. Code. Compile. Deploy.
Start Free Trial Upgrade Today
Free Delphi Community Edition Free C++Builder Community Edition
I feel the article is economical with the harsh reality that the executable code generated by Delphi is slower than that generated by modern C++ compilers.
This is a poor example to illustrate your point. Carmack’s code is a low level library function. It’s not meant to be readable. It’s meant to fast.
Look at the Delphi RTL in the system unit. Are all those assembler functions readable? No. But they aren’t trying to be.
When you say that the Delphi code avoids “evil floating point bit level hacking”, it of course does not. That comment just means that we interpret the floating point value as an integer, so that we can operate on it using integer arithmetic. The Delphi code does exactly the same. It just uses different syntax because it’s a different language. It’s not avoiding anything.
You give a shout out to this FastMath library. When you look at the code, e.g. https://github.com/neslib/FastMath/blob/master/FastMath/Neslib.FastMath.Sse2_64.inc, you find thousands of lines of SSE2 assembler instructions. Hardly readable. Don’t get me wrong, that code is not meant to be readable. I’m not criticising the library for that. My point is that readability is not always the goal. Sometimes you sacrifice readability when performance is the ultimate goal.
I wrote some benchmark code to compare the Delphi version of Carmack’s sqrt against the C version. I compiled the Delphi version with release options. The C version with MSVC 2019 with /O2.
The C version ran 3 times faster on 32 bit, 6 times faster on 64 bit. It’s very hard to describe this as a win.
So to summarise you start out by saying that Delphi code is more readable than code in some other languages, with a well known function written in C as your example.
This C function was not written to be readable, it was written to be fast.
And you finish by pointing at a Delphi library that was also written to be fast rather than readable.
All in all, a very confused picture. There is a story to be told I am sure. This isn’t it however.
I was going to suggest you missed my point, but I think we are in agreement, so maybe I missed yours . . .
Just in case, right in the middle of the post I said:
[It makes use of the absolute keyword to map the floating-point number to the integer, which avoids all the “evil floating point bit level hacking.” This is something I love about Delphi and Object Pascal: It gives you access to pointers, and raw memory, etc. but doesn’t force you to use it when you don’t want/need to. Shorter code isn’t always easier to understand, just take a look at any regular expression, but this is an improvement because it removes so much extraneous code. Much more readable.]
I wasn’t trying to say that was the most optimized way to write the function, just pointing out something I found interesting about how much more readable it was. The FastMath library is a good example of fast math routines with Delphi, and it may be less readable, but as you pointed out, library code doesn’t need to be readable, it needs to be fast.
The thing with generalities is there are always exceptions.
Can you write unreadable code with Delphi? Yes.
Can you write readable code with C? Yes.
Can you write slow code with C? Yes.
Can you write fast code with Delphi? Yes.
Is one better than the other? No. They both serve their own purposes. I’m a pragmatist. Use whatever works for you.
But generally speaking, Delphi is more readable because it makes more use of English words than symbols in its syntax. Additionally Delphi doesn’t lock you away from low level functionality like pointers, etc.
I think that you don’t understand what the comment “evil floating point bit level hacking” means. What it is referring to is that the code operates on a floating point value by treating it as an integer value. And the Delphi code does that.
The Delphi code does it using different syntax, but the “evil floating point bit level hacking” remains.
It’s certainly the case that Delphi code, for many domains, is more readable. It’s an OOP language. A far better comparison in terms of readability would be between Delphi and C++. The C language is really targeted at very different domains than Delphi and C++.
My main points is that you picked the wrong example to compare readability (a function written for speed) and really you picked the wrong language to contrast Delphi with.
As for the expression
* ( long * ) &y
Well, that’s perfectly readable to somebody fluent in C. I suspect that you aren’t as fluent in C as you are in Delphi.
You say
“But generally speaking, Delphi is more readable because it makes more use of English words than symbols in its syntax.”
Again I suspect that your judgement is clouded because you are less familiar with other languages. Languages like C++, C#, Java, D, languages in the C tradition, can all be very readable.
You say
“Can you write fast code with Delphi? Yes.”
That’s plain wrong. Delphi codegen is a massive problem, as I know large numbers of your customers repeatedly tell you. As a developer advocate, isn’t part of your job to listen to your customers?
I really don’t want to argue with you, but I will try to address your comments…. (this got way longer then the original blog post, but you seem to believe I don’t care about your points or that I am not listening.)
My whole goal with this blog post was not to prove once and for all that Delphi was more readable or faster or better than C. It was just that I found that interesting bit of code and wanted to share. And I thought it was an interesting example of how Delphi can be more readable. Sorry if that wasn’t clearer in my blog post.
I’ll admit that I misread that comment. I thought it said “evil POINTER hack.” I make mistakes sometimes, and that does change some of the context of my post. I can correct it if you like.
Delphi Codegen
Delphi codegen can be better. It isn’t the fastest, but it is often fast enough, and frequently faster than many alternatives. I can produce benchmarks to prove it, but that is irrelevant (see below). I do listen to developers, both customers and non-customers, and I always advocate for improvements in Delphi and software development in general.
Opportunity Costs
I am also a pragmatist and I understand the opportunity costs of each feature, improvement, and fix. I’m sure as a software developer you know that you cannot always add all the features and fix all the bugs. Each feature adds bugs, and feature requests come faster than you can implement them. Even if all the developers in the world were working on one project then that project would could never be done. Sure, you would have releases, but there will always be improvements to be made, and anyone that says otherwise has never worked on a software project of any complexity.
Relativity: Fast, Faster, Fastest
“Fast” is a relative term. I guarantee a good computer programmer can show you slower and faster implementations of any code you write with any programming language. Slower is guaranteed because the halting problem is a proven fact. You can write Fast code with Delphi and you can frequently write “Faster” code with Delphi. Can you write the FASTEST code with Delphi? Hard to say. “Fastest” as an absolute is not possible with ANY programming language. You could say “fastest with this hardware and our current understanding of computing algorithms” but before you’ve finished saying that, there is new hardware, new instruction sets, and new papers being published on computing theory. Maybe I’m an optimist, but I believe there is always room for improvement.
My programming language familiarity
I learned to program with Commodore BASIC on a Vic20, and later graduated to MS-DOS with Batch Files and BASICA, later moving to Turbo Pascal, C, C++, and many other languages (I did Ruby and Perl before they were cool.) I’ve spent years working in C syntax languages (including quite a while debugging laser printer firmware). I’ve taught courses and talked at conferences on C, C++, Objective-C, Java, JavaScript, etc. There are many things about the C syntax I find very beautiful. But I still wouldn’t call C languages my strength. Heck, there are times I even question how well I know Delphi.
Readability
Is Delphi more readable? If you took someone who only knew C, they would have a better chance of reading Delphi then someone would of reading C who only knew Delphi (or that only knew English, and knew no programming languages). But I may be wrong in that assertion. If a peer reviewed, double blind study has been done on the subject I am not aware of it, and if you wish to sponsor such a study I’m happy to collaborate with you on it.
Summary
Anyway, if you really want to debate the semantics of business practices and computer science theory, let’s arrange a call or something. Not sure it will produce any results, but I literally can (and enjoy) talking about this all day, and it if will make you feel better great. If you want to talk about the Delphi software roadmap, I’ll send you to Product Management and their published product roadmap.
https://blogs.embarcadero.com/rad-studio-november-2020-roadmap-pm-commentary/
If you want to say Delphi can be faster, I think we are all in agreement on that. (if it were done then there would be no roadmap.) If you want to say Delphi’s codegen is horrible, you are entitled to your opinion, but others may disagree. If you want to say Delphi’s codegen is the worst, that is provably wrong.
Thanks
I do hope you see that I’m not trying to be dismissive of your comments and suggestions. I do want Delphi to get better and improve. We all do. I’m just trying to say it isn’t as bad as you make it out.
You say
“frequently faster than many alternatives. I can produce benchmarks to prove it”
Go ahead. Show me. Please do prove it.
My experience is that Delphi code gen is terrible in comparison with mainstream C++ compilers. And even with modern day C# can make Delphi look pedestrian.im talking between two to five times slower. That’s not “fast enough” for me.
I’ve frequently resorted to rewriting my numerical code in C and linking to my Delphi program in order to deal with performance issues.
Hardware evolves. New instruction sets are introduced. The Delphi compiler that I use, Windows 64 bit, has never changed its code gen in a meaningful way since XE2 when it was introduced.
I get that performance isn’t the top priority for Embarcadero. If that’s your choice, fine. But it really grates when you claim otherwise.
I used Delphi and Python since I’ve been brushing up on my Python lately. Might C/C++/Assembly/etc. be faster? Yes, but that wasn’t the point of the experiment. This is just to compare performance with an alternative.
Delphi is roughly 7 million times faster than Python.
Now I’m sure you can produce a test that shows exactly the opposite with only a little effort. That is the beauty of the art we work in. For this test, I took a naive approach and used the first algorithm implemented in both languages when I did a Google search.
While the absolute keyword exists since Turbo Pascal there is a newer replacement available: variant records. This compares to unions in C…
True!