GiNaC is not a C compiler [Was: Optimized C-style output]
I am very interested in this discussion. Thanks, Stefan, for sharing your optimize_C_code() function, and getting this discussion started. I am working on a program that generates source code for a variety of languages, including C, Fortran, python, and Matlab, so I hope whatever functions that come out of this discussion will be usable with languages other than C. Alexei suggested a function called generate_code(...) that generated a complete function that returns the value of the expression. That could be useful; presumably it would check for the symbols used in the expression, and make all of them arguments to the function. I suggest calling this function generate_function(...) instead of generate_code(). Another function, called generate_assignment(...), would only generate assignments, including possibly assignments of temporary variables. My program generates code using variable names that were specified in a file. It would be useful if the target variable name of the generate_assignment() function could be given as an argument. For example, string varname = "v"; string tmpname = "tmp"; string tmptype = "double"; ex f; ... generate_assignment(cout, varname, tmpname, tmptype, f); would generate, for example, double tmp3 = ... double tmp2 = ... double tmp1 = ... v = ... So v is the ultimate target instead of tmp0. (My program will have already generated a proper declaration of v before calling generate_assignment(...), but it might be useful to have the option of including the declaration of v as double when generate_assignment() is called.) Often the target variable is not a simple variable name. A lot of the code that I generate puts the result in a vector or a matrix. The destination might be expressed with a macro. For example, my program generates code to be used with the CVODE library. To assign a value to a vector field component, the destination of the assignment is something like NV_Ith_S(f_,k). For example, NV_Ith_S(f_,1) = -1.0/(L*L)*b*v/m-1.0/L*g*sin(theta); or, to generate an element in a jacobian, DENSE_ELEM(jac_, 1, 0) = -1.0*1.0/L*cos(theta)*g; Currently, that statement is created in a loop with code that is roughly cout << "DENSE_ELEM(jac_," << i << "," << j << ") = " << f << ";\n" With generate_assignment(), this might be ostringstream jac_entry; jac_entry << "DENSE_ELEM(jac_," << i << "," << j << ")"; generate_assignment(cout, jac_entry.str(), "tmp", "double", f); For Fortran (assuming "implicit none" is used), it would be necessary for the code to first figure out the names of all the temporary variables that will be used before actually generating code, so my program can create the appropriate declaration of these variables. Alexei proposed this function: const ex find_common_subex(const ex& e, exmap& repls); So, for Fortran output, I would call this function, and then use repls to generate the declarations of the temporary variables. Then I would call generate_assignment(...) to actually create the code that does the assignment. (This would also work for C/C++.) Alexei also proposed split_large_subex(...). How would I use this together with find_common_subex() if I want to do both to a complicated expression? Best regards, Warren
Hello! On Thu, Mar 29, 2007 at 07:51:56AM -0400, Warren Weckesser wrote:
Alexei suggested a function called generate_code(...) that generated a complete function that returns the value of the expression. That could be useful; presumably it would check for the symbols used in the expression, and make all of them arguments to the function.
It should also sort those symbols, so variables declared before they used.
I suggest calling this function generate_function(...) instead of generate_code().
That would be very misleading. There are functions in C++. There is also GiNaC::function, and related DECLARE_FUNCTION_* macros. So yet another (unrelated) thing named "function" does not sound like a good idea.
For Fortran (assuming "implicit none" is used), it would be necessary for the code to first figure out the names of all the temporary variables that will be used before actually generating code, so my program can create the appropriate declaration of these variables. Alexei proposed this function:
const ex find_common_subex(const ex& e, exmap& repls);
So, for Fortran output, I would call this function, and then use repls to generate the declarations of the temporary variables.
This function can be useful for absolutely diffrerent things. If the exact form of some lengthy subexpressions is not important for current operation (e.g. gcd()), I would call this function to replace them with symbols. If I expect the expression to have a lot of easily recognizable subexpressions I'd call it before .evalf(), then evalf() subexpressions it detected, subs() them back into the original expression, and finally evalf() it.
Then I would call generate_assignment(...) to actually create the code that does the assignment. (This would also work for C/C++.)
Alexei also proposed split_large_subex(...). How would I use this together with find_common_subex() if I want to do both to a complicated expression?
ex e = a2*pow(x1+...+x1024, 2) + ... + a1024*pow(x1+...+x1024, 1024); exmap repls; e = find_common_subex(e, repls); // Now e should be a2*pow(symbol1, 2) ... + a1024*pow(symbol1, 1024), // and repls contains pair symbol1 => x1+...+x1024 e = split_large_subex(e, repls, 128); // e: symbol2 + symbol3 + ... + symbol8; // repls: // symbol1 => x1+...+x1024 // symbol2 => a2*pow(symbol1,2) + ... + a130*pow(symbol1,130) // symbol3 => a131*pow(symbol1,121) + ... + a259*pow(symbol1,259) // ..... // symbol8 => a898*pow(symbol1,121) + ... + a1024*pow(symbol1,1024) So repls contains "simpler" (?) expressions now. You can further split and/or find common subexpressions by iterating over repls entries. BTW, this example illustrates that such an "optimizer" does not optimize anything. It would be better to (recursively) split that expression into e = e1 + e2 e1 => a2*symbol2 + a4*pow(symbol2, 2) + ... + a1024*pow(symbol2, 512) e2 => symbol1*(a3*symbol2 + ... + a1023*pow(symbol2, 511)) symbol2 => pow(symbol1, 2) Best regards, Alexei -- All science is either physics or stamp collecting.
participants (2)
-
varg@theor.jinr.ru
-
Warren Weckesser