C++ compile and execution times

Mike Stump mrs@apple.com
Fri Nov 22 13:02:00 GMT 2002


On Thursday, November 21, 2002, at 07:38 PM, Roger Sayle wrote:
>> On Thursday, November 21, 2002, at 05:37 PM, Roger Sayle wrote:
>>> The problem I've noticed is that the recursion in "fold" appears
>>> to be O(n^2) instead of O(n).
>>
>> I don't believe it is.  Try it out.  I think one the first ply is
>> folded, thus limiting the amount of work done.
>
> I believe it really is O(n^2).

No, honest.  I don't think it is.  The typical fold code looks like 
this:

       else if (TREE_CODE_CLASS (TREE_CODE (arg0)) == '<')
	return fold (build (COND_EXPR, type, arg0,
			    fold (build1 (code, type, integer_one_node)),
			    fold (build1 (code, type, integer_zero_node))));

In this case, notice that there is only one fold per pre-built node.
Three nodes, three folds, this is linear.

> Given a unary-tree, even if each level is actually transformed only 
> once,
> fold will still retraverse the levels below it confirming that nothing 
> can
> be done.  So in a tree/list N-levels deep, the leaf will be checked N 
> times
> (the level above that N-1 etc...).  It takes a synthetic example, such 
> as
> (((A+-B)+-C)+-D)+-E to show it, but big-O notation is for the worst 
> case.

In that case,  we have:

     case MINUS_EXPR:
       /* A - (-B) -> A + B */
       if (TREE_CODE (arg1) == NEGATE_EXPR)
	return fold (build (PLUS_EXPR, type, arg0, TREE_OPERAND (arg1, 0)));

Notice there can only be one extra call to fold per node, that's linear.

The cleaverness of fold, and why it isn't a problem comes from the fact 
the
folder does one ply of work on only the top most part of the tree.  It 
is
only ever necessary to play with the top, as we know that fold had 
_already_
been called while constructing the tree for lower layers, thus 
obviating any
need to search deeper into the tree.  If you were right, the code would 
look
like:

       else if (TREE_CODE_CLASS (TREE_CODE (arg0)) == '<')
	return fold (build (COND_EXPR, type, fold(arg0),
			    fold (build1 (code, type, integer_one_node)),
			    fold (build1 (code, type, integer_zero_node))));
and

     case MINUS_EXPR:
       /* A - (-B) -> A + B */
       if (TREE_CODE (arg1) == NEGATE_EXPR)
	return fold (build (PLUS_EXPR, type, fold(arg0), fold(TREE_OPERAND 
(arg1, 0))));

but, it doesn't look like that, ever.  Also, if you were right, we'd 
never need
to fold as we build, just once after it was all built.  But, we 
_always_ fold
everytime we build, and any builder that doesn't, is a missed 
opportunity, never
to be re-visited.

And last, if you want, measure the worse case test case, and see if it 
is linear
(in fold), double the size, and see if fold takes twice as long, or 
four times as long.



More information about the Gcc mailing list