Dev Tracker - Discussion

Eorzea Time
 
 
 
言語: JP EN FR DE
日本語版のFFXIVPRO利用したい場合は、上記の"JP"を設定して、又はjp.ffxivpro.comを直接に利用してもいいです
users online
フォーラム » FFXI » General » Dev Tracker - Discussion
Dev Tracker - Discussion
First Page 2 3 ... 638 639
 Asura.Iamaman
Offline
サーバ: Asura
Game: FFXI
User: iamaman
Posts: 1190
By Asura.Iamaman 2026-04-28 19:06:43  
It wouldn't surprise me if they used asm stubs for arithmetic just out of habit. Most of them in the late 90s would've learned in environments where that sort of thing mattered and would've trusted the compiler to manage it for them a lot less. The compiler might've handled it for them fine but they still did it out of habit, like how a lot of C developers use macros when inline functions would be just as or more suitable (guilty) for no particular reason. Sometimes developers just do things out of habit and being used to seeing it, but they might also have done it out of a desire to avoid any weird compiler behavior. ***was wild in the late 90s.

Bit shifts are still used by allocators or anything doing aligned arithmetic on pointers. I'd define that as pretty rare but mostly because most modern C code is hot garbage 99% of the time unless it came from Microsoft, Google, or Apple where all the old crusty nerds work who complain about how some functions are slower than others because they have one added instruction.
 Asura.Saevel
Offline
サーバ: Asura
Game: FFXI
Posts: 10486
By Asura.Saevel 2026-04-28 19:35:40  
Asura.Iamaman said: »
It wouldn't surprise me if they used asm stubs for arithmetic just out of habit. Most of them in the late 90s would've learned in environments where that sort of thing mattered and would've trusted the compiler to manage it for them a lot less. The compiler might've handled it for them fine but they still did it out of habit, like how a lot of C developers use macros when inline functions would be just as or more suitable (guilty) for no particular reason. Sometimes developers just do things out of habit and being used to seeing it, but they might also have done it out of a desire to avoid any weird compiler behavior. ***was wild in the late 90s.

Bit shifts are still used by allocators or anything doing aligned arithmetic on pointers. I'd define that as pretty rare but mostly because most modern C code is hot garbage 99% of the time unless it came from Microsoft, Google, or Apple where all the old crusty nerds work who complain about how some functions are slower than others because they have one added instruction.

Haha yeah back then lots of stuff like this was common. The PSX ran on a 33Mhz CPU with 5KB of cache, 2MB of "system" RAM and 1MB of "video" RAM. To get a game like FFVII to run on that with those kinds of graphics required squeezing every ounce of performance via tight code optimizations. The PS2, which FFXI was coded on, and didn't have any Microsoft Visual Studio compiler for, had a 299Mhz 64-bit MIPS CPU, 16KB of instruction cache, 8KB of data cache, 32MB of "system" memory, 4MB of "video" memory and 2MB of "sound" memory.

Every time we dig into any of this games math we always stumble into where percentage numbers are really integers over 1024. Haste isn't 15% (0.15) but rather 150/1024. WS waist / belts are not +10% WS damage but really +100/1024 fTP each. That wouldn't make sense unless the WS's fTP values were themselves coded as X/1024, making it a simple addition that costs 1 cycle to do. These sorts of low level optimizations would be the bread and butter of console game developers.

Like I'd bet that Fimbulvetr's fTP at 3K isn't 9.9 but rather 10138, which would actually account for some of the discrepancies that pop up occasionally. 9.9 doesn't convert cleanly into an integer over 1024, instead turning into 10137.6. 10137 turns into 9.8994, while 10138 becomes 9.9003, and even 10140 becomes 9.9023. The 60% STR is probably 614, which is 0.5996 or 615 at 0.6005.

For percentages this is really important as divides are crazy expensive, like orders of magnitude more. While a multiply can take 2~4 cycles, a division takes 32 to 89 cycles. All multiplication by a percentage becomes a division but you can avoid that by turning it into a pure multiplication.

A: 450 STR
B: 60% STR WSC (0.60)
450 * 0.60 = 270 (this requires a DIV instruction)

Or with shifting
A 450 STR
B: 60 (shift two places)

450 * 60 = 27000, shift two places to 270.
 Lakshmi.Byrth
VIP
Offline
サーバ: Lakshmi
Game: FFXI
User: Byrthnoth
Posts: 6833
By Lakshmi.Byrth 2026-04-28 19:45:23  
Those theories always sound sensible, but damage calculations take place on the server where SE is far less resource constrained.
 Asura.Saevel
Offline
サーバ: Asura
Game: FFXI
Posts: 10486
By Asura.Saevel 2026-04-28 19:54:06  
Lakshmi.Byrth said: »
Those theories always sound sensible, but damage calculations take place on the server where SE is far less resource constrained.

The guys who wrote it almost certainly also wrote the client. Old coding habits die hard and I'm fairly sure this wasn't even built on x86. Japan early 2000's was heavily dominated by Fujitsu SPARC64 and HP PA-RISC.

It's likely long since been moved to another platform, but I doubt they refactored these kinds of low level code optimizations.

And it's a lot more then a theory, what I just described is the exact coding patterns you see in older console games. Lots of using fixed binary digit values to reduce the number of expensive multiplys and crazy expensive divides.
 Lakshmi.Byrth
VIP
Offline
サーバ: Lakshmi
Game: FFXI
User: Byrthnoth
Posts: 6833
By Lakshmi.Byrth 2026-04-28 19:55:33  
Sure, but sometimes they mix in IEEE standard float math because the server isn't actually resource constrained
 Asura.Saevel
Offline
サーバ: Asura
Game: FFXI
Posts: 10486
By Asura.Saevel 2026-04-28 20:06:49  
Lakshmi.Byrth said: »
Sure, but sometimes they mix in IEEE standard float math because the server isn't actually resource constrained

Oh never said they didn't, if you have a good FPU then both multiplys and divides can be done in half the time while preserving accuracy. Of course a FPU divide is still something like 10~36 cycles so still expensive and if you don't care about decimal place accuracy, you can cut it out entirely. Of course that FPU might be busy doing something else, like assisting in graphics rendering (this is what the PS2 did).

The OG coders who wrote the server engine almost certainly were console developers and it shows because of some of the weird values that come out when we test this games mechanics. Devs who joined much later, after the OG dudes moved on, likely didn't bring the same coding techniques with them but also didn't rewrite the old functions to match modernish conventions.

Realistically speaking none of these optimizations are required, but likely a result of coding habits. Think of your own habits you learned earlier in your career and how those habits shape how you approach projects. For a bunch of dudes who spent their entire career programming in the quasi assembly language that consoles used, this would of seemed "normal".
Offline
Posts: 282
By Genoxd 2026-04-29 00:38:02  
Asura.Saevel said: »
Genoxd said: »
FFXI is compiled with MSVC bundled with Visual Studio 6 and the code, as far as I can tell, is C++.

I don't know what universe you're living in, but FFXI was not written in pure assembly. Usually people write C/C++ and then stitch in assembly in pieces of functions IF they need to. That is a big IF.

Your post seems to imply that you can't do bit shifting in C/C++ too from the reference to "/1024". If you're implying that, then that is also incorrect.

Edit because phone typing is hard

That is all FFXI PC Client, which is not the original client, much less the server. FFXI original coding was done back in 2000~2002 by a team that specialized in SNES and PSX console games.

Bit shifting isn't something frequently done in C, or any other language as it's an arcane technique that's not needed much anymore. Where it is useful is when your compute is a 1.8 to 3.5Mhz CPU or if your really rocking then 33Mhz. Then every cycle, every byte of memory count and every IO operation count.

The math of bit shifting is to ensure every multiplication is done on whole integers, which are computationally much easier to handle inside Arithmetic Logic Units (ALUs).

For example lets do a simple C = A * B, where A is 4837 and B is 3.85.

Since ALU's have to operate on whole integers we have to break B into two separate values, B1 which is 3, B2 which is shifted to 85. Then multiply A by B1 and B2 separately, then shift A2 while storing the decimal place into A3. Finally all three are added together into a packed fixed decimal value. This is what the C compiler is doing for you because it's assuming you care about the decimal results as you started off with a decimal value to begin with.

Now instead lets just add two decimal places to B by shifting it to 385. We then multiply it once by A, shift again to drop the decimal places to get 18622. Much faster because we don't care about the decimal place.

Now for why it's 1024 and not 1000, because this is all being done on processors that only speak binary and in binary each number place is a value of 2 not 10.

That means a weapon skill with a fTP value of "3.85" would not be stored as a fixed decimal value 3.85, but instead as a shifted binary value of 3942, or 0000111101100110.

If a weapons base damage after WSC / fSTR was 894 (0000001101111110), and the fTP was "3.85", it would really be 001101111110 * 111101100110 = 01101011100011000110100. We then remove the ten decimal places that 1024 represents to get 0110101110001, which is 3441.

894 * 3.85 = 3441.9

That right there is what FFXI's server is doing and why every percentage seems to be /1024. They are shifting ever percentage value by ten binary digits to get rid of the decimal place and turn it into a pure integer multiplication, then shifting it back which drops those digits resulting it looking like it's "flooring", which it isn't. Because the values were never stored in any form of fixed decimal value there was never a remainder to drop.

Agner does a good job of breaking down instruction cycle time and latency, though it's only on x86 CPUs and there is no guaranteed that SE is running this game on an x86 CPU as there were many popular 64-bit RISC designs during that time.

https://www.agner.org/optimize/#manual_instr_tab

Bit shifting can be done in a single clock cycle, multiplication takes 2~4 depending if you are referencing a register or memory location. Reading or writing memory, MOV, costs 1 cycle. By using bit shifting we can shave a bunch of cycles off our computation time, at the cost of losing accuracy and having to ensure everything is an integer.

Nobody does this anymore because it's simply not needed in the age of super scalar out of order execution CPUs with large L1 instruction and data caches. It wouldn't really give much of a benefit nowadays, but back in the 90's and early 2000's these optimizations were critical to squeezing every drop of performance out of consoles.

Yes. I am a software engineer and I work in C/C++. I also know assembly. Bit shifting is still used to this day but generally only for optimizations.
Random nodejs webdev might never use it, but it is still used.

This is not limited to archaic assembly. Minecraft uses bit shifting in Java all over the place. Minecraft was started and released in 2009.
First Page 2 3 ... 638 639