New Synergy/DE Feature Release Targets .NET 8December 14, 2023
View Recent Webinar Recordings to Get Latest Synergy Product NewsDecember 21, 2023
You can increase the efficiency and maintainability of your Synergy DBL code, whether it be traditional or .NET, by learning some implementation details of the Synergy compilers. By having a general understanding of what the compiler is doing, you can reduce build times and boost program performance. Even so, we don’t need to go into specifics about the compiler’s own source code to gain these benefits.
Taking advantage of compiler optimizations
Let’s determine how many seconds are in a year. Which of the following local DATA statements is more readable?
data seconds = 31536000
data seconds = 365 * 24 * 60 * 60
The second one is more readable. We see there are 365 days per (non-leap) year, 24 hours per day, 60 minutes per hour, and 60 seconds per minute.
Now, which one runs faster? Both run at the same speed. Why? The multiplication operations shown are evaluated at compile time, so in both cases the program contains a single literal value, 31536000. The more readable statement is preferred because it has no runtime performance penalty over precomputing the answer. This works because we didn’t multiply any variables, only numeric literals.
You can avoid bugs by using compile-time functions. In some cases, ^SIZE can be used at compile-time, and its result may be used to initialize variables in a data division. For example,
count, int, ^SIZE(AVAR)+1
Console.WriteLine(%string(COUNT)) ;Prints 26
Changing the size of avar will automatically update the count variable. If count were hard-coded to be 26, it would have to be changed manually. Manual changes are easy to forget, so use compile-time functions to avoid them.
Reducing compilation time, increasing runtime performance
Let’s shift focus to reducing build times for a program. The compiler still processes fields and variables that are not used during runtime. To speed up compilation, remove any unnecessary .INCLUDE directives, IMPORT statements, and parameters. Don’t add references to assemblies that are unused, because this will slow down the compiler as it tries to resolve names and paths.
To temporarily deactivate blocks of code, either comment out the entire block or use preprocessor directives such as .IFDEF. Don’t wrap unused code blocks in an “if (%false)” statement, because the compiler will still look inside the body of the IF statement and process it. However, using “if (%false)” or “if (0)” is okay when the body of the IF statement serves some purpose, such as declaring an error label.
Instead of including a structure in each routine that it’s used in, include the structure in a single location where all the routines have access to it. The compiler produces an object for each routine and method it sees, so this will reduce the total amount of data that is handled during compilation. Singleton objects are objects that only need to be instantiated once per program. Use these in conjunction with static variables such as global data section fields, commons, or static record fields to improve performance at runtime.
Keep routines small. Small subroutines, functions, and methods are far more maintainable. They are also easier to process when using Synergy DBL Integration for Visual Studio (SDI). If you’re using Synergy .NET, note that the .NET JIT (just-in-time) compiler may give up on optimizing medium to large routines.
For error handling in both traditional Synergy and Synergy .NET, I/O error lists are the fastest choice. Both TRY-CATCH statements and ONERROR statements are slower.
Declare function return types explicitly and replace external function declarations with prototypes. Having more exact type information makes it easier for other developers to understand your code and make changes without causing type-related errors.
Use local DATA statements instead of unnamed records in Synergy .NET. Declaring variables using a DATA statement does not need to create an anonymous record object during runtime, which improves performance.
If it’s not possible to remove all unused fields, keep in mind that this mainly affects compilation speed rather than runtime speed. This is because unused fields don’t get instantiated by the runtime (not even in the default level of the debugger). Be aware that unused fields inside a record still increase the memory size of the record at runtime.
Not all integers are the same
In traditional Synergy, int and i4 are the same type, but this isn’t true for Synergy .NET. For .NET, you should use int instead of i4 because int maps to System.Int32, a built-in type for the Common Language Runtime (CLR). An i4 creates an integer descriptor, which is less efficient.
Use integers for loop control variables. Decimal data types are slower and will take up more memory when the stored numeric value is large. USING statements are quite efficient when the control expression is an integer and the match terms are compile-time literals.
Integers are also the reason that ^VAL functions are efficient. ^VAL functions return an integer value that is the same width as the natural integer size for the machine. On a 32-bit machine, it will be a 4-byte integer and on a 64-bit machine, it will be an 8-byte integer. A ^VAL function does not need to process the return value as an argument in the way that a subroutine does.
Your code, your choice
We’ve discussed how to make programs faster, how to get extra performance out of the Synergy compiler, and how to make developer collaboration easier by focusing on code maintainability and readability. The choices made by a Synergy developer can have a major impact for applications running in the far future. This advice regarding the Synergy compilers will allow you to make better, more informed choices. We hope these tips have been helpful, and we look forward to supporting the amazing software that has been built by your developer team. Check out the Synergy/DE documentation to find more information.