![]() ![]() |
![]() ![]() Post #1 | |
![]() Coding like a Rockstar! ![]() Posts: 1,468 From: ??? Joined: 28-May 09 ![]() | Code Optimisation Faster Opcodes Remember: Just because the "Space Saved" is low for most things, the fastest opcodes aren't measured entirely by how many bytes they used, but mainly by how much work the game is doing behind the scenes to retrieve each parameter and process it. 004F vs. 00D7 SANNY 004F: create_thread @MS_BIKE_MISSIONS Creates a thread with up to 35 extra parameters. The game would check to see if these parameters exist before moving on to the next opcode. When extra params are defined, each value specified is carried to the specified thread and stored in it's local variables e.g: SANNY 0006: 20@ = 13 0376: 18@ = create_random_actor_at -1576.88 55.26 8.57 00D7: create_thread @THREAD 412 20@ 18@ end_thread :THREAD // 0@ == 412 // 1@ == 13 // 2@ == an actor After :THREAD is created, it has the values: 412, 13 and an actors handle stored in 0@, 1@ and 2@. As you may notice, the variables start from 0@ and go up per value. SANNY 00D7: create_thread @NONAME_1 // without extra params Exactly the same as 004F except that it does not pass any values to the new thread. Guess which one is faster? 00D7 should always be used to create a thread unless you actually want to pass parameters to it... Rockstar seemed to use 004F and 00D7 in their first block of create_thread commands. It is unclear why, since none of them require any parameters passed. 03AF vs. 0000 Naming threads is useful for many things. It helps the game differentiate them as well as allowing for certain memory operations that could be used. Say there is a large CLEO Script... You don't own it but you want to perform operations on it (mod it) without editing the file itself... Naming the thread allows for the use of 0AAA to get it's pointer. There's also the use of the end_named_thread commands. However... 03AF uses 9 more bytes than 0000, which is still allowed. Overall, I'd recommend the use of 03AF for things that are more than just a simple, small script. If you are using this in a large script (or the main.scm), use it. If you are making a relatively simple mod, 0000 is the best option. 0662 & 0900 vs. HEX...END 0662 is a nopcode that seems to have been picked up by many modders for the purpose of adding messages in their scripts that can be decompiled. This is usually so they can send messages to anyone who decides to decompile their scripts. 0900 is not a nopcode (as far as I know). It is in fact an unknown opcode used to activate an unknown flag on objects, used a couple of times in the main.scm - However, it seems that modders like to use it as such a nopcode. The hex...end structure is much more sophisticated. It inserts strings straight into the script without opcodes. It's a much better way of performing these operations, though some still may want to use opcodes. SANNY hex "My Text" 00 end The 00 byte at the end is almost certain to be needed and basically marks the end of the string (a null terminator). This saves about 4-6 bytes. Obviously, if you don't use/need the null terminator (you're reading the exact amount of bytes) then it saves 1 extra byte ![]() Coordinate Retrieval Since they're easy to find, a lot of people use the following to retrieve coords of entities: SANNY 0400: store_coords_to 1@ 2@ 3@ from_object 0@ with_offset 0.0 0.0 0.0 0407: store_coords_to 1@ 2@ 3@ from_car 0@ with_offset 0.0 0.0 0.0 04C4: store_coords_to 0@ 1@ 2@ from_actor $PLAYER_ACTOR with_offset 0.0 0.0 0.0 However if you're not setting an offset to retrieve, it's a big waste of 12 bytes and extra processing time. Here are the lighter solutions: SANNY 00A0: store_actor $PLAYER_ACTOR position_to 0@ 1@ 2@ 00AA: store_car 0@ position_to 1@ 2@ 3@ 01BB: store_object 0@ position_to 1@ 2@ 3@ Most people don't use them as they don't appear in the common search for "coords". You'll want to use the other opcodes when you are getting the offset for more than 1 of the dimensions. If it's just the one dimension you want to get the offset off, use a math opcode (less bytes etc). Bad Habits
Ordering Stuff to remember:
-------------------- | CLEO 4.3.22 | A?i?a?o?3D | UI SDK | Black Market Mod 1.0.1 | GInput 0.3 | Cheat Keyboard | Tactile Cheat Activation | Stream Ini Extender 0.7 | SuperVars | ScrDebug | Vigilante Justice: San Andreas | |
![]() Post #2 | |
![]() Coding like a Rockstar! ![]() Posts: 1,468 From: ??? Joined: 28-May 09 ![]() | Long strings always save more bytes than short strings.. Plus they can be used in almost every situation. ![]() -------------------- | CLEO 4.3.22 | A?i?a?o?3D | UI SDK | Black Market Mod 1.0.1 | GInput 0.3 | Cheat Keyboard | Tactile Cheat Activation | Stream Ini Extender 0.7 | SuperVars | ScrDebug | Vigilante Justice: San Andreas | |
![]() Post #3 | |
![]() The master of cut retort ![]() Posts: 239 From: Warsaw, PL Joined: 21-July 10 ![]() | Deleting 'if' opcodes from script (but not if and & if or) saves 4 bytes per one 'if'. Good way to reduce size of script if you are sick of optimisation ![]() Can I post here, or it's only Deji's topic? This post has been edited by Silent: Jul 23 2010, 12:15 PM |
![]() Post #4 | |
![]() User banned by request due to personal reasons. - SV Posts: 24 From: 0xFFFFFFFF Joined: 14-March 10 ![]() | sure you can, what type of forums was it if you weren't allowed ![]() Well I basicly use only one code optimization method regulary: - swaping code out from the main code just as loading models an the check - creating function-like gosubs for preventing code duplication which is a former rule in every programming/scripting language I'm not sure why, but I realy love gosubs ![]() SANNY gosub @ModelLoad you don't realy have to check this part out (if you publish your sources of course)btw Deji, reserving vars by setting them either to 0 or 0.0 (which indeed is useless) ain't such a bad idea if you want people learning from there code its better to initialise them with zero as reserving them with var - end and it just makes the code longer, var - end does the same: it sets the vars to zero but they don't show up in the script which is common in most popular programing languages This post has been edited by Siggi: Jul 23 2010, 03:04 PM -------------------- ![]() User banned by request due to personal reasons. - SV |
![]() Post #5 | |
![]() Coding like a Rockstar! ![]() Posts: 1,468 From: ??? Joined: 28-May 09 ![]() | var...end doesn't set them unless you tell it to. They're mainly for telling Sanny how to treat that particular variable. Useful for the for loops as otherwise Sanny can't tell the difference if you want to store the value of another variable. If you wanted to teach, put the useless code that sets them to something they are in a block comment... That serves the need to visualize the initial state of the variables and also optimizes ![]() And yes, you can post here... Be sure to share your optimization knowledge, though ![]() -------------------- | CLEO 4.3.22 | A?i?a?o?3D | UI SDK | Black Market Mod 1.0.1 | GInput 0.3 | Cheat Keyboard | Tactile Cheat Activation | Stream Ini Extender 0.7 | SuperVars | ScrDebug | Vigilante Justice: San Andreas | |
![]() Post #6 | |
![]() User banned by request due to personal reasons. - SV Posts: 24 From: 0xFFFFFFFF Joined: 14-March 10 ![]() | It does, it reservs them for further calcuations, any script language will automaticly set them equal to 0, thats a golden rule in programming ![]() var - end tells the script engine to reserve some memory for storing variables, even in languages which are written very close to the OS set vars to 0 if they weren't defined at the same time as they were initialised: (C++/C#) CODE int intMyVar; // intMyVar is zero now float fltMyVar; // fltMyVar is either zero or 0.0 int intMyVar = 4; // intMyVar is now initialised and defined as 4 float fltMyvar = 4.0; // fltMyVar is now initialised and set to 4.0 Skriptlanguages like LUA don't allow you to initialise a var only, it has to be defined at the same time: CODE MyVar = 0 MYVar2 = 0.0 wrong: CODE MyVar MyVar2 both are equal to nothing (= nil in LUA) var will resever memory so it will initialise this memory by setting it to 0 or 0.0 (not sure how it treats arrays) cheers, This post has been edited by Siggi: Jul 24 2010, 01:15 PM -------------------- ![]() User banned by request due to personal reasons. - SV |
![]() Post #7 | |
![]() Coding like a Rockstar! ![]() Posts: 1,468 From: ??? Joined: 28-May 09 ![]() | I think there was a theory behind use of 0900 for strings, but I'm not sure myself. It's that, compiled, 0900 becomes 00 09. This will automatically end the last string (with the null-terminator 00) and define the next using the 09 data type.. Although that data type is for short strings, so I'm still a little baffled. Maybe it just ended up being used even though no one knew what they were using it for. -------------------- | CLEO 4.3.22 | A?i?a?o?3D | UI SDK | Black Market Mod 1.0.1 | GInput 0.3 | Cheat Keyboard | Tactile Cheat Activation | Stream Ini Extender 0.7 | SuperVars | ScrDebug | Vigilante Justice: San Andreas | |
![]() Post #8 | |
![]() Coding like a Rockstar! ![]() Posts: 1,468 From: ??? Joined: 28-May 09 ![]() | SANNY 01B4: set_player 0 can_move 0 Does exactly the same as SANNY 01B4: set_player $PLAYER_CHAR can_move 0 But of course, the single value saves a byte more ![]() You could use 1 for player 2, etc. This is what's stored in the vars anyway (points to one of two memory pools). I wanted to test these optimizations on the main.scm, so I used Sanny's regex replace (very useful - shame about lack of multiline). Only took a few minutes to finish automatically replacing everything with optimized versions ![]() CODE Original : 194146 b Optimized : 182279 b Reduction : 11867 b Not that much, really.. but still a noticable amount. Lets see if we can find more optimizations to increase the reduction ![]() -------------------- | CLEO 4.3.22 | A?i?a?o?3D | UI SDK | Black Market Mod 1.0.1 | GInput 0.3 | Cheat Keyboard | Tactile Cheat Activation | Stream Ini Extender 0.7 | SuperVars | ScrDebug | Vigilante Justice: San Andreas | |
![]() Post #9 | |
![]() Coding like a Rockstar! ![]() Posts: 1,468 From: ??? Joined: 28-May 09 ![]() | This one's for likers of low-construct, high-optimization... Who should really consider ASM when thinking about moving onto programming, lol ![]() SANNY {$CLEO} 0000: Conditionals Are Permanent! :0 while true wait 0 if 0ADC: test_cheat "COND" then if and 0039: 1@ == 1 0039: 2@ == 1 :1 0ACA: show_text_box "1" wait 1000 else_jump @2 jump @0 :2 0ACA: show_text_box "2" wait 1000 end end What I'm trying to point out here is that the SCM is totally different from any other language in many ways. In most languages, an IF statement is expected to contain a condition which returns true or false and it is expected that there be something for the code to do straight away depending on the result. In SCM, each thread in the game is structured with data about it. This data includes a byte which is 1 if the last condition returned true and 0 if it returned false. When a condition or IF statement (which groups multiple conditions) in parsed, the game tests whether the conditions are true or false, then sets the thread byte accordingly. Conditionals and IF statements don't have to be followed by a true/false jump/gosub etc. All a conditional does is perform any action required, check the statement and set the byte. 004D is a seperate opcode. It tests the byte and jumps if the byte is set. If not, it moves on. So basically, you can freely check a condition in one place and use that anywhere else. This is why 0485 and 059A work. They could also be used before an else_jump to force the jump either way... not that there's much use in that. But it's nice to know. -------------------- | CLEO 4.3.22 | A?i?a?o?3D | UI SDK | Black Market Mod 1.0.1 | GInput 0.3 | Cheat Keyboard | Tactile Cheat Activation | Stream Ini Extender 0.7 | SuperVars | ScrDebug | Vigilante Justice: San Andreas | |
![]() ![]() Post #10 | |
![]() Coding like a Rockstar! ![]() Posts: 1,468 From: ??? Joined: 28-May 09 ![]() | There are many opcodes that have no function in SA... What I learned is that they crash the game upon use. It's good that I doubt stuff I learn from others until I can proove it myself, because this is not true... I'll explain how the opcode handler works and what these opcodes can be used for in terms of optimization by showing a SCM representation: SANNY const OPCODE = 0x0001 end {$CLEO} :Script while 0039: 0@ = false wait 0 0AB1: call_scm_func @OpcodeHandler 1 OPCODE 0@ end 0A93: end_custom_thread :OpcodeHandler // 0@ contains the opcode 0871: switch 0@ jumps 7 default 0 @default jumps 0 @0000 1 @0001 2 @0002 3 @0003 4 @0004 5 @0005 6 @0006 :0000 0AB2: ret 1 true :0001 // (0001 code here) 0AB2: ret 1 false :0002 // (0002 code here) 0AB2: ret 1 false :0003 // (0003 code here) 0AB2: ret 1 false :0004 1@ = 2 jump @SetVar :0005 // (0005 code here) 0AB2: ret 1 false :0006 1@ = 1 :SetIntegerVar // some more code which prooves 0004 and 0006 are exactly the same, lol 0AB2: ret 1 false :default { if an inexistant opcode is passed, the opcode returns -1. this is NOT false, so the script stops parsing and continues to process the game... just like 0001, except more optimised! } 0AB2: ret 1 -1 So you can use any inexistant opcode within range of these handlers (from 003D to 09DF) in place of "wait 0", which saves a whole 2 bytes!!! Plus it looks cool and adds that extra bit of edge to your script... noobs will run around panicking when they see a loop without wait ![]() It does save processing time, but that's microsecond optimisation ![]() Working example: SANNY {$CLEO} 0000: while true 01CD: render_game_frame end -------------------- | CLEO 4.3.22 | A?i?a?o?3D | UI SDK | Black Market Mod 1.0.1 | GInput 0.3 | Cheat Keyboard | Tactile Cheat Activation | Stream Ini Extender 0.7 | SuperVars | ScrDebug | Vigilante Justice: San Andreas | |
![]() Post #11 | |
![]() Coding like a Rockstar! ![]() Posts: 1,468 From: ??? Joined: 28-May 09 ![]() | I did some speed tests against DMA and regular memory operations (mainly out of boredom). It's not much of a surprise that DMA is faster... CODE Direct Memory Access 5 million times - 0.903 10 million times - 1.926 25 million times - 4.491 Write Memory 5 million times - 1.275 10 million times - 2.512 25 million times - 6.191 So.. if you ever need to repeat a memory operation 25 million times, I hope you'll use DMA ![]() Then, I tried testing normal global variable setting 25 million times... CODE Global Var Setting 25 million times - 4.127 Somewhat faster than DMA by the looks of things, since we're not working with arrays, but still.. not much. And just in case you're curious... the result for local variables on 25 million iterations was 4.177. Still, I wouldn't go to extremes to use these for optimisation. -------------------- | CLEO 4.3.22 | A?i?a?o?3D | UI SDK | Black Market Mod 1.0.1 | GInput 0.3 | Cheat Keyboard | Tactile Cheat Activation | Stream Ini Extender 0.7 | SuperVars | ScrDebug | Vigilante Justice: San Andreas | |
![]() ![]() |