Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
Bug 341944 - [evaluator] Introduce smart accumulators
Summary: [evaluator] Introduce smart accumulators
Status: NEW
Alias: None
Product: OCL
Classification: Modeling
Component: Core (show other bugs)
Version: 3.1.0   Edit
Hardware: PC Windows Vista
: P3 enhancement (vote)
Target Milestone: ---   Edit
Assignee: OCL Inbox CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks: 509309
  Show dependency tree
 
Reported: 2011-04-05 12:34 EDT by Ed Willink CLA
Modified: 2017-04-29 04:18 EDT (History)
0 users

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Ed Willink CLA 2011-04-05 12:34:49 EDT
The pivot model is 4 times slower than the non-pivot model for:

let i : Integer = 10000000 in Sequence{1..i}->iterate(j : Integer; acc : Integer = 0 | acc + j)*2/i-1

but 5 times faster for:

Sequence{1..10000}->iterate(i : Integer; acc : Sequence(Integer) = Sequence{1, 1} | acc->including(acc->at(i) + acc->at(i+1)))->last() 

I suspect this is because the pivot model iterations have a semi-smart collection accumulator that can be updated in place. The same does not apply to the simple Integer in the first example.

The accumulator is only semi-smart; to be truly smart it should detect that the ->including() is the last use, so the old collection can be re-used avoiding the copy and add.

Is the degraded comparative pivot model speed solely attributable to use of BigInteger and/or dynamic dispatch? The 4 times slower is surprising.

-----

Introduce smart IteratorValues so that the Iterator changes in-place.

Introduce smart non-collection accumulators.

Detect an ability to do collection re-use.
Comment 1 Ed Willink CLA 2011-07-15 06:16:28 EDT
For

Sequence{3..20000}->iterate(i : Integer ; acc : Sequence(Integer) = Sequence{0, 1} | acc->append(acc->at(i - 2) + acc->at(i - 1)))->last()

the Indigo Xtext OCL Console takes about 7 seconds.

The same using the WIP direct Java code generator takes 3.5 seconds. The direct Java code generator has inline dispatching and int-sized integers where possible.

If, the write-after-last-read append usage is manually exploited, an in-place Accumulator can be used and the speed improves to just under 200ms. Clearly Collection thrashing is the dominant problem once code gen is good.

Therefore introduce a write-after-last-read analysis to support use of Collection Accumulators.

If 3.3 seconds is due to Collection thrashing, the non Collection speed up is 7-3.5=3.5 to 200ms, so the direct Java may be 10 to 20 times faster.
Comment 2 Ed Willink CLA 2011-07-16 05:59:28 EDT
(In reply to comment #0)
> The pivot model is 4 times slower than the non-pivot model for:
> 
> let i : Integer = 10000000 in Sequence{1..i}->iterate(j : Integer; acc :
> Integer = 0 | acc + j)*2/i-1

Hmm. Doing this in the Indigo Consoles, the non-pivot takes about 12 seconds and the pivot a few minutes!

Using the direct Java code generator, with a lazy SequenceRangeValue and int/long/BigInteger Values as appropriate takes 8 seconds, which is at last faster, even though using arbitrary number ranges and dynamic dispatch. Maybe an updating accumulator can do better still.
Comment 3 Ed Willink CLA 2012-01-30 12:06:26 EST
A Fibonacci value may be calculated by:

> Sequence{1..10000}->iterate(i : Integer; acc : Sequence(Integer) = Sequence{1,
> 1} | acc->including(acc->at(i) + acc->at(i+1)))->last() 

[This dummy comment just gets Fibonacci and performance and recursion as Bugzilla hits.]
Comment 4 Ed Willink CLA 2016-08-28 05:12:37 EDT
A smart accumulator is a localized solution. A lazy collection can do more.

The Pivot architecture has a SetValue (interface) with typically a Collection of underlying elements.

Since we use EMF, the elements are often the EList.data, so the Set isn't a true Set.

This should be clearer with underlying ListAsSet, SmallSet, UnenforcedSet capabilities to handle use of an unederlying list, optimizing for fewer that ten =-identified elements, trusting that the source guarantees uniqueness.

If we ensure that all CollectionValues are used by known Variables, we can detect singly used Collections to allow mutation. If a mutation occurs to a shared Collection we can create an Array-based copy for the immutable collection, rather than yet another ArrayList/HashSet-based copy for the mutable.

LazyCollections such as an AsEcoreCollection can perform to-ecore conversion on the fly avoiding the need to reify both old and new forms. Cascades of such conversions may be optimized to remove redundancy. Knowledge of multiple / single client Variables may determine whether the on-the-fly computation should be cached in an array for re-use by a further variable.

The above are all helpful to OCL used in bulk but much more valuable to QVT where static analysis can help with many of the usage style guarantees.
Comment 5 Ed Willink CLA 2017-01-02 13:51:51 EST
(In reply to Ed Willink from comment #4)
> A smart accumulator is a localized solution. A lazy collection can do more.

Not necessarily

Sequence{1..10000}->iterate(i : Integer; acc : Sequence(Integer) = Sequence{1, 1} | acc->including(acc->at(i) + acc->at(i+1)))->last() 

could result in a daisy chain of 10,000 lazy IncludingIterators that might be eagerly evaluated for an operation such as at().No real benefit.

Perhaps the lazy structure provides an opportunity to reflect on the evaluation before performing it. Need some way to do this for a wide variety of evaluations rather than just a growing list of peepholes.
Comment 6 Ed Willink CLA 2017-04-29 04:12:55 EDT
(In reply to Ed Willink from comment #5)
> Not necessarily
> 
> Sequence{1..10000}->iterate(i : Integer; acc : Sequence(Integer) =
> Sequence{1, 1} | acc->including(acc->at(i) + acc->at(i+1)))->last() 

The daisy of chain of iterators hazard can be avoided by recognizing that the final access of a variable such as "acc" can be replaced by an eager mutable operation i.e. rewrite

acc->including(acc->at(i) + acc->at(i+1))

as:

acc->mutableIncluding(acc->at(i) + acc->at(i+1))

This rewrite could be localized in FeatureCallExp if there was a FeatureCallExp.implementation to resolve redirect, but currently there is only Feature.implementation (no site-specific choices).

Until Bug 341944 makes that possible:
? modify OperationCallExp.referredOperation - needs a target Operation
? only support the CG equivalent
? have a Map lookup in visitOperationCallExp
? have a context awareness in Including.Instance
Comment 7 Ed Willink CLA 2017-04-29 04:18:11 EDT
(In reply to Ed Willink from comment #6)
> Until Bug 341944 makes that possible:
Correction Bug 509309

Does addition of a transient require a major version change.

? add the transient as manual rather than genmodelled code ?

---

If we have the transient, how is it populated?

'mutable' may not be the only context sensitive optimization.

? a LibraryFeature has an optional Strategy which if not null may have an extensible API to determine context sensitive alternatives.