![]() |
Faster Square Roots - Printable Version +- Forum (https://www.boriel.com/forum) +-- Forum: Compilers and Computer Languages (https://www.boriel.com/forum/forumdisplay.php?fid=12) +--- Forum: ZX Basic Compiler (https://www.boriel.com/forum/forumdisplay.php?fid=11) +---- Forum: How-To & Tutorials (https://www.boriel.com/forum/forumdisplay.php?fid=13) +---- Thread: Faster Square Roots (/showthread.php?tid=155) |
Faster Square Roots - britlion - 02-13-2010 I've got a version that does LONG as well as uInteger later in this thread. I note that the compiler uses the Spectrum ROM square root routine. This routine is hideously slow. It actually calculates x^(0.5) instead, and takes ages about it. The Newton-Raphson method would be a lot faster, and pretty easy to put in. If you are willing to sacrifice accuracy, an integer square root would be faster still. For a lot of situations, an integer root would be just fine - for example, if I had to calculate the nearest of two objects on screen, I'm going to have to use pythagoras' theorum to calculate distances. [ A^2 = B^2 + C^2 ] that needs square roots to make a distance. But probably the nearest whole pixel would be a perfectly good enough result! So, here are two functions, and some code to demonstrate them. One is a perfect replacement for sqr.asm in the library, actually - it's full floating point compatible, 100% accurate, and about 3-6 times faster. It actually uses the FP-Calculator in the rom. It just doesn't use the SQR command. [Note: It comes back with something instead of an error in case of a negative square root request. Boriel - you might want to change that behavor. Not sure.] The integer version... well - look for yourself! I reckon it's about 40 times faster than the fast version. Also: Should be able to do a something similar for a 32 bit LONG integer. Copy and compile this program. I hope you like it: Code: FUNCTION FASTCALL SQRT (radicand as FLOAT) as FLOAT Re: Faster Square Roots - boriel - 02-19-2010 britlion Wrote:I note that the compiler uses the Spectrum ROM square root routine. This routine is hideously slow. It actually calculates x^(0.5) instead, and takes ages about it. The Newton-Raphson method would be a lot faster, and pretty easy to put in. If there's no "code license restrictions" (and I guess not), these functions can be added "as is" in the compilers library, and inserted with an #include directive. What do you think? :roll: Re: Faster Square Roots - britlion - 02-19-2010 I think that's a good idea. We need a section of the wiki to cover library additions then. Note that the float square root I wrote is a perfect replacement for the one in the asm library already - I suppose if people want to save the last few bytes they could use the original (it is shorter), but much slower. Then again, if the optimization is working correctly, if SQR isn't used in the program, the library routine for it wouldn't be in anyway, yes? Re: Faster Square Roots - boriel - 02-19-2010 britlion Wrote:I think that's a good idea.For SQR I'm just calling the ROM (to save memory). That's why I always use the ROM for FP calculations. A wiki section for external functions is a very good idea. In fact, it was planned: Look here <!-- m --><a class="postlink" href="http://www.boriel.com/wiki/en/index.php/ZXBasic">http://www.boriel.com/wiki/en/index.php/ZXBasic</a><!-- m --> on the lower-right square ;-) Re: Faster Square Roots - britlion - 02-19-2010 My floating point Square rooot routine (Function SQRT in the above code) is written so that it can replace sqrt.asm in the library.asm folder. It uses the ROM calculator too - but it doesn't call the SQR routine in it because it's really slow! It uses the calculator in a way that the ROM should have. It's at least triple the speed of the original, for a handful of bytes. Some modified ROMs use this method, I understand. I designed that function so it should plug straight in to your sqrt.asm file directly. It even calls the calculator stack routine you have. By my count your version is 9 bytes, and my version is just 47 bytes. Probably not too big a sacrifice for the speed. Especially if it's not included if SQR isn't used. My recommendation is to adopt my floating point one as the standard for the compiler, and have integer as a #include option. Though we probably need one that works with LONG data type as well. Re: Faster Square Roots - boriel - 02-19-2010 britlion Wrote:My floating point Square rooot routine (Function SQRT in the above code) is written so that it can replace sqrt.asm in the library.asm folder. It uses the ROM calculator too - but it doesn't call the SQR routine in it because it's really slow! It uses the calculator in a way that the ROM should have. It's at least triple the speed of the original, for a handful of bytes. Some modified ROMs use this method, I understand. I designed that function so it should plug straight in to your sqrt.asm file directly. It even calls the calculator stack routine you have.I 47bytes can be really much! But I was thinking in an alternative solution. By using a --fast-float flag, this and other alternatives floating points routines will be uses in place of the standard ones. This could be easily acomplished by adding an #ifdef ... in the libasm/sqrt.asm file ;-) Re: Faster Square Roots - britlion - 02-20-2010 Well, apparently, digging into the Tobos compiler looks like it might be fruitful for fast floating point routines - though they will eat space in RAM, yes. The flag is a good idea. How about something like -Opt-Size and -Opt-Speed to let the compiler choose the smallest or the fastest code available for functions? Circle and draw could probably go the same way, for starters. Re: Faster Square Roots - britlion - 02-20-2010 boriel Wrote:I 47bytes can be really much! Just to be clear, it's only 47, not 147! Only 36 bytes more ![]() Re: Faster Square Roots - boriel - 02-20-2010 britlion Wrote:Well, apparently, digging into the Tobos compiler looks like it might be fruitful for fast floating point routines - though they will eat space in RAM, yes.Hmmm. CIRCLE I think is already fast enough (it uses bresenham's integer sums) ![]() Re: Faster Square Roots - britlion - 02-20-2010 Faster = Good for me :-) Faster plot might be nice, too. I was thinking that CIRCLE could be smaller if you used the (agonizingly slow) Rom routine - and the same with Draw. I doubt you could make them inherently faster; though a faster plot routine would help a lot, yes. Re: Faster Square Roots - boriel - 02-20-2010 britlion Wrote:Faster = Good for me :-)Unfortunately, I can't use neither ROM CIRCLE nor DRAW routine (I tried hard), because these routines mixed Sinclair BASIC parsing with execution. Calling them will trigger ROM parsing routines crashing the system. :? Re: Faster Square Roots - britlion - 02-20-2010 Interesting. I didn't know that. Hisoft Basic compiled code either uses the ROM or something identical to calculate the circles then. Huh. I just found <!-- m --><a class="postlink" href="http://www.andreadrian.de/oldcpu/Z80_number_cruncher.html">http://www.andreadrian.de/oldcpu/Z80_nu ... ncher.html</a><!-- m -->, and then noticed you've borrowed code from that. I'm looking at the 16.16 routines with interest. Might be able to work those up as functions for a library, on the assumption that users looking for speed will be willing to sacrifice accuracy, and use FIXED type numbers. Badass Square Roots - britlion - 02-24-2010 Okay, integer square roots - this is the big version. It can deal with LONG integer input. And right now, I never want to see a four-register 32 bit number again *lol* hooboy are they a pain. I found out the hard way, for example, that "inc HL" doesn't flag overflows. It all boils down to a lot of tests and jumps to allow for lower 16 bits flowing into upper and vice versa. This version is 148 bytes of code. It incorporates the 16 bit version as an alternative super-fast routine. This version is about 50 times faster than the ROM routine for numbers > 65535 and over 100 times faster for numbers <= 65535. It only returns integer square roots (see http://en.wikipedia.org/wiki/Integer_square_root for details) - but for game purposes, these are generally fast enough. If you want floating point numbers, I've also published a routine about 6 times faster than the usual rom one; though, ironically, that uses the rom calculator; but uses a far better algorithm than the actual rom call. If you want to reduce the size by about 1/3, you can cut the jump to 16 bit off the start, and cut out the 16 bit section at the end. It's still going to be 50 times faster than the ROM. I called it sqrtLF - for Square Root Long Fast. But you can rename it if you want. Just sqrt isn't a bad name. You can't call it sqr though, as that is a reserved word. Here you go: Code: FUNCTION FASTCALL sqrtLF (num as uLong) as uInteger |