Visual LISP: Understanding Project Properties In last month's article I described a Visual LISP™ project as a mechanism for organizing functions and LSP files. But that's not all it is. A project provides a mechanism for optimizing programs to make them run faster and also gives you control over who can access the code underlying the specific programs you've created in Visual LISP. That means your programs can't be tampered with. We're going to look at these project properties in more depth in this article.
Before we can manipulate these properties, you must create a project. If you do not know what a Visual LISP project is, please see last month's article before going any further. We covered the basics about projects and how to create them in that article. Project Property DefaultsWhen you first create a project, properties' default settings apply. Figure 1 shows the Build Options tab in the Project Properties dialog box for a new project. As you can see, the default settings call for the Standard compilation mode and the generation of one module for each file. These settings allow for no performance optimization of the related program and generate a FAS (compiled) file for each LSP (source) file. | Figure 1: Project Properties dialog box, Build Options tab. |
|
To view and edit project properties, select the Project Properties icon from the project list as shown in Figure 2 and click the Build Options tab in the Project properties dialog box.
At the start of the program development cycle, the default settings are usually appropriate. But as you approach program testing, you're sure to want to adjust some of them. But you can't adjust what you don't thoroughly understand, so let's take a closer look at these Build options. That way you'll create source code that optimizes program performance. | | Figure 2: Project Properties icon. |
| Standard or Optimize?You have two options for compilation mode: Standard (the default) and Optimize. When you select Standard, all of the names in a program remain untouched. That means that function names and global variable names can be referenced from outside of the compiled program. That is, global symbols can be shared across applications. But this can be both a good thing and a bad thing depending on how your application reacts to changes outside of its immediate control.
In normal operation Visual LISP handles any reference to these names—whether from inside or outside the project—by going to an internal table containing all the names (symbols) to locate data. So each time you reference something Visual LISP first looks in the table to find where it is and then depending on what you've asked LISP to do, either retrieves or writes data to that location. If you bypass this table by referencing locations directly, your programs will execute faster. Exactly how much faster depends greatly on your coding style and the problem being solved.
Optimization doesn't work well with programs that make use of the dynamic nature of Visual LISP. For example, if you try to optimize a program that creates or manipulates lists that are themselves functions, Visual LISP can't access the lists and there goes your optimization. You don't have to avoid optimization in these cases, but you must make sure everything is defined in the proper context so that optimization can take place.
When an application's global symbols and function names can no longer be referenced outside of the program itself, they are considered "dropped." That is, they are not accessible from other applications. But there are exceptions. By default any function name starting with C: does not drop from view. Visual LISP knows you want the operator to use those as command functions. And you can expand the list of symbols that are not dropped by selecting the Edit Global Declarations button, which we will look at shortly. Single Module for All?When you've finished application development, use the "Single module for all" option to optimize program performance. But during application development, use the "One module for each file" option. Why? Because by creating a single module for each file you cut development and compilation time. With this option selected, new versions and updates can be compiled and loaded as they are changed by just loading the FAS or LSP and running a new test.
Whereas using a single module for all means that all the LSP files are recompiled into the new FAS before running a new test. You'll see what I mean when you're working with very large Visual LISP projects. The time saved by not compiling the entire suite of code each time a module is updated for a test run can be substantial. When the "One module for each file" option is active, the system compiles only those files that have changed since the last FAS suite was built. For minor changes that are typical during a debugging session, recompiling only the file that was changed makes much more sense than recompiling everything. After compiling the update, you can load the changed FAS file(s) for another test run. | Figure 3: Optimize Build Options in Project Properties. |
|
Using the "Single module for all" option results in just one FAS file containing all the functions in an application. The single FAS file loads faster and enhances program performance since the location of all functions is known as the FAS file is created. A single FAS file is also easier to distribute to a group of users since only one file needs to be supplied to each of them. | Link Mode OptionsNow let's look at the options on the Link mode pane on the Build Options tab of the Project Properties dialog box (see Figure 3). These options only become available when you have selected the Optimize radio button on the Compilation Mode pane. With these options, you can tell Visual LISP how much optimization to introduce into the FAS file creation.
When you select Link, the resulting FAS file contains direct calls to the location of a function instead of using the symbols table. When you select Link, the application runs substantially faster, particularly when many calls to functions are involved. However, once you've selected Link, you cannot redefine functions since the address of the linked function is already stored. You won't want to do that until you've completed most of the debugging work. Otherwise, you have to recompile the entire application for each change you make during program module testing.
With Link, you keep a symbols table available so that outside applications can reference the same function names in your application. Inside the FAS file, calls to functions are by direct address. An outside call to a function utilizes the table of symbols to find the location of the function to run. Using the Link option is basically a compromise: you gain the optimization of direct calls in the FAS file yet supply a link so that external applications can access your work as well.
If you want to close off your application so that only the command functions are available to users, then select the Internal link option. The resulting FAS does not have all the function names in the symbols look-up table, is smaller as a result, and runs a little faster as well. How Not to Drop a SymbolWhat do you do when you want to optimize your program yet allow external applications to reference functions in your program that are not command functions? How do you selectively tell Visual LISP which specific functions to retain in the symbol table?
Use the VL-ACAD-DEFUN expression when defining the function in your module. VL-ACAD-DEFUN is just like DEFUN except that the symbol name for the function is put in to the "Do not drop from external view" list.
You can also expose your symbols, both functions and variables, to external applications by building a Global Declarations file. This holdover from an earlier version of Visual LISP will eventually be replaced by other tools like VL-ACAD-DEFUN. But for now, it provides a flexible mechanism for the task at hand.
To edit the Global Declarations File, click the Global Declarations Edit button on the Build Options tab, which opens the GLD file for editing. The GLD file, a Visual LISP source file, contains specific list definitions of symbols that are to be protected. When the Edit Global Declarations button is first selected a GLD file-creation option is shown.
The GLD file is read and processed during the creation of the FAS file. The functions in the GLD file are DROP, NOT-DROP, LINK, NOT-LINK, and others. If you have a symbol name that you want to keep available outside of your FAS file, include it in the NOT-DROP list.
Through the GLD file, you can control exactly which of your application's functions and variables are accessible from external applications. But unless you're already expert with the Visual LISP integration development environment (VLIDE), I would use this file with caution because it will eventually become obsolete in a later version of Visual LISP. Keeping a Function as a ListWhen you want to create an optimized application but also want to provide a dynamic list that serves as a function, use Visual LISP DEFUN-Q subr. Functions defined using DEFUN-Q can be accessed using the DEFUN-Q-LIST-REF and DEFUN-Q-LIST-SET. Note that if you have older programs that edit lists defined as functions, the subrs just listed should be substituted for any existing SET and list reference expressions.
The functions defined using DEFUN-Q follow all of the same rules as normal functions created with DEFUN. That is, the symbol name is followed by the parameter list, which is followed by the expressions. The parameter list has both local and bound parameters, same as with a DEFUN. The only differences between a function created using DEFUN and one created with DEFUN-Q is that the original list (and symbolic references) is kept intact for later reference and changes. Toggle OptionsLet's look at two toggle options—Safe optimize and Localize variables—on the Build Options tab that are only enabled when you've selected Optimize on the Compilation Mode pane. Localizing the variables makes all variable references work by direct location and not through a symbol table. Selecting this feature greatly improves the execution speed of programs that use a lot of variables inside a loop. Safe optimize means that a series of rules are applied to determine if a symbol can be localized or dropped from the exposed symbol table and if a function can be used as a direct link instead of an indirect link. When the Safe optimize toggle is on, the compiler always takes the safe road and quickly removes a symbol from the drop list for a variety of reasons. What Works Best?If one approach to setting a project's properties and optimizing an application always worked best for all situations, then there simply wouldn't be any options. But everyone's programming style in VLIDE is different and the goal is to give you as many program-creation/optimization choices as possible.
Generally if you use features in Visual LISP such as MAPCAR, APPLY, and LAMBDA, you won't really need the optimization features. Optimization is used more in Visual LISP applications that do not take advantage of advanced list manipulations and the dynamic nature of a runtime evaluator. In other words, it's most useful in programs typically written by AutoCAD® users, applications written to perform AutoCAD-related activities that improve productivity. Generally, I recommend starting in Standard mode and creating a single module for each file. Then, as the application matures into a singular unit and most if not all of the modules are working fine together, create a single FAS file with full optimization and see if you can get it to fail through testing. In most cases, if you have played by the proper rules of programming, the transition to full optimization is easy.
We're interested in hearing from you. Did you get useful information from this article? Is there a topic in Visual LISP you'd like to see covered? If so, let us know. Until next time, keep on programmin'! |