IBM Support

Concert best practices programming conventions

Question & Answer


Question

What can I do to ensure my Concert programs run efficiently?

Answer

The C++ API ILOG of Concert Technology offers a powerful and flexible API for performing complex tasks. However, with this flexibility may come seemingly equivalent code segments that behave quite differently regarding speed, memory consumption, or other system resources. This document discusses some programming conventions that can help you improve efficiency when you use the Concert classes and methods.


1.  Use a more precise derived class rather than a more general base class to perform a task whenever possible.

2.  Perform operations on arrays of objects rather than individual objects whenever possible.

3.  Be careful when using multiple handles to the same object.

4.  Distinguish functions that take arguments by reference from those that do so by value.

5.  Remember that Concert operators are actually functions that create objects.

6.  If you need to reclaim memory associated with the objects you created before deleting the IloEnv object, be sure to explicitly call the appropriate end() method.

7.  Avoid unnecessary notifications of problem changes.


1.  Use a more precise derived class rather than a more general base class to perform a task whenever possible. When you have a choice between a more general and a more precise class to perform a task, use the methods of the more precise one. The more general class may perform additional, unnecessary operations. Also, the more precise class provides access to more specific methods to obtain information about the associated object.

For example, use the class IloRange rather than its more general class IloConstraint when creating linear constraints. First of all, IloRange has methods that IloConstraint lacks that can be useful for processing linear constraints, for example, IloRange::getExpr. Second, the IloCplex methods for obtaining solution information about linear constraints expect instances of IloRange rather than instances of IloConstraint. For example, the concept of dual values applies to IloRange, but not to the more general IloConstraint such as IloSOS1 or IloPiecewiseLinear.

In contrast, extensions beyond linear constraints resulting from IloSOS1, IloSOS2, IloPieceWiseLinear, and other functions that create nonlinear constraints that CPLEX linearizes do require the class IloConstraint.

Another example specific to CPLEX users involves the MIP callback function used to control the CPLEX branch and cut algorithm. The classes IloCplex::MIPCallbackI and IloCplex::ControlCallbackI are parent classes from which specific branch and cut callback functions (such as those in the class IloCplex::CutCallbackI) derive. In some cases, information about the branch and cut algorithm is available from both the parent class and the subclass. In such cases, use the methods of the subclass to obtain the information you want; the method of the parent class may need to perform unnecessary additional computations. For example, when querying variable values for the solution to be considered in an incumbent callback, use IloIncumbentCallbackI::getValues rather than the more general IloMIPCallbackI::getIncumbentValues.

2.  Perform operations on arrays of objects rather than individual objects whenever possible. When you perform an operation on a Concert object, the operation may affect other objects as well. For example, when you delete a variable, Concert must not only remove that variable from the model, but it must also modify the constraints containing the deleted variable. Concert can carry out the notification more efficiently if you assemble all your common objects into a single array, then make a single function call with the array to perform the operation. For example, suppose the model building process includes the following extractables:

IloModel m(env);
IloNumVarArray x(env, n);

After building and solving the model, suppose you want to delete some of the variables in the IloNumVarArray x from the model. The first code segment below is more efficient than the second.

// Delete entries of x that are binary variables  (more efficient)
IloNumVarArray deletex(env);
for (int i=0; i < n; i++) {
// Assemble extractables to delete
if ( x.getType() == ILOBOOL )  deletex.add(x[i]);
}
deletex.endElements();                         // Delete all at once

// Delete entries of x that are binary variables   (less efficient)
for (int i=0; i < n; i++) {
if ( x.getType() == ILOBOOL )  x[i].end();  // Delete one at a time
}

Useful functions for performing operations all at once include IloScalProd, IloSum, IloModel::remove(const IloExtractableArray), IloExtractableArray::add(const IloExtractableArray) and IloExtractableArray::endElements(). For example, suppose we have the following objects and extractables:


IloNumArray a(env, n);
IloNumVarArray x(env, n, 0.0, IloInfinity);
IloExpr ex(env);

Then the second code segment below will build a linear expression more efficiently than the first.

for (i = 0; i < n; i++) ex += a[i]*x[i];    // Slower
model.add(ex <= 10);
--------------------------------------------------------------------------------
--
model.add(IloScalProd(a,x) <= 10);          // Faster

3.  Be careful when using multiple handles to the same object. An expression (an instance of IloExpr) is passed by value to an extractable (an instance of IloExtractable). Therefore, you can delete the original expression after passing it by value without affecting the extractable that received it. Similarly, instances of IloNumColumn and IloIntSet are passed by value to any predefined Concert Technology objects. More generally, if you have multiple handles passed to Concert objects pointing to instances of IloExpr, IloNumColumn, or IloIntSet, modifying one handle prompts Concert Technology to perform a lazy copy. In other words, it first copies the implementation object for the handle you are modifying and then makes the modification. The other handles pointing to the original implementation object remain unchanged, and your modification has no impact on them. Lazy copying does not apply to other Concert Technology objects, nor does it apply to arrays of the previously mentioned objects. In general, we recommend that you avoid using multiple handles to the same object if you don't feel comfortable with the lazy copy mechanism. Users who try to take advantage of lazy copying in their programs may encounter surprising results if they do not fully understand when it does or does not apply. You can easily avoid any unexpected behavior with lazy copying by always using a single, unique handle to access any Concert objects that your program may subsequently modify.

4.  Distinguish functions that take arguments by reference from those that do so by value. Functions that take arguments by reference access the argument by the address that was provided. Functions that take arguments by value create a copy of the argument that was provided. This copying can create unexpected additional memory use, particularly when C++ copy constructors are involved.

5.  Remember that Concert operators are actually functions that create objects. Because overloaded operators in Concert represent mathematical relations used to build expressions and constraints, one can easily forget that, unlike standard operators, they are actually functions. For example, the following two lines create the same constraint, but internally different Concert functions are invoked:

c.add(x <= y);

c.add(x - y <= 0);

That first line invokes the overloaded operator

public IloConstraint operator<=(IloNumExprArg base, IloNumExprArg base2)

while the second line invokes

public IloRange operator<=(IloNumExprArg base, IloNum val)

While typically this distinction has no effect on the program behavior, in some cases subtle differences can arise. For example, IloCplex::getDuals will accept an instance of IloRange as an argument, but not an instance of IloConstraint.

6.  If you need to reclaim memory associated with the objects you created before deleting the IloEnv object, be sure to explicitly call the appropriate end() method. Keep in mind that all Concert objects are handles that point to an implementation class, even for arrays of handles pointing to other objects (e.g. IloIntVarArray). Furthermore, Concert isn't necessarily aware of all the other handles referring to an object. So, the destructors for these objects will not free the memory associated with the implementation classes. Therefore, if you want to reclaim memory associated with some Concert objects, do not rely on their destructor calls to do so. Rather, all of the relevant Concert objects have an end() method that will free the memory. You need to use these methods explicitly if your program needs to free this memory before it calls IloEnv::end() when it finishes.

7.  Avoid unnecessary notifications of problem changes. When deleting modeling objects, the IloCplex object is notified in order to update the extracted model. This can be time consuming since one notification will occur for each call to the end() methods. Therefore, you should delete your IloCplex object (cplex.end()) before iteratively ending your modeling objects (variables, constraints). For more information about ending the extractable objects, please see this technote.

[{"Product":{"code":"SSSA5P","label":"IBM ILOG CPLEX Optimization Studio"},"Business Unit":{"code":"BU059","label":"IBM Software w\/o TPS"},"Component":"Not Applicable","Platform":[{"code":"PF025","label":"Platform Independent"}],"Version":"9.2;9.1.3;9.1.2;9.1;9.0;8.1;8.0;7.5;7.1;7.0;6.6;12.4;12.3;12.2;12.1;12.0;11.2.1;11.2;11.1.1;11.1;11.0.1;11.0;10.3;10.2.1;10.2;10.1.1;10.1;10.0","Edition":"","Line of Business":{"code":"LOB10","label":"Data and AI"}},{"Product":{"code":"SSSA5P","label":"IBM ILOG CPLEX Optimization Studio"},"Business Unit":{"code":"BU059","label":"IBM Software w\/o TPS"},"Component":"General","Platform":[{"code":"PF002","label":"AIX"},{"code":"PF010","label":"HP-UX"},{"code":"PF016","label":"Linux"},{"code":"PF027","label":"Solaris"},{"code":"PF033","label":"Windows"},{"code":"PF017","label":"Mac OS"}],"Version":"12.2","Edition":"All Editions","Line of Business":{"code":"LOB10","label":"Data and AI"}}]

Historical Number

cplex/Document/139

Document Information

Modified date:
16 June 2018

UID

swg21400056