osate / osate2

Open Source AADL2 Tool Environment
http://osate.org
Eclipse Public License 2.0
36 stars 8 forks source link

Latency analysis does not interpret execution time on feature (entrypoint) of thread #1940

Closed reteprelief closed 4 years ago

reteprelief commented 5 years ago

AADL allows users to specify compute execution time for specific features, e.g., specific ports. Similarly the execution time of a subprogram should be used as value for remote subprogram calls.

reteprelief commented 4 years ago

The compute execution time property on a port indicates the compute execution time for a thread when dispatched on a particular port. In other words, a (aperiodic, sporadic, hybrid) thread could be dispatched through different ports and each dispatch could result in a different compute execution time. Note that for periodic threads only the compute execution time of the thread should be used.

For latency analysis we have a flow through ports. If an incoming port of the flow has a compute execution time property then its value should be used instead of the value associated with the thread.

For an example just use an existing latency test example and add the compute execution time property to a port involved in a flow when the thread is not periodic.

AaronGreenhouse commented 4 years ago

This change is conceptually simple: when getting the execution time of thread, if the thread is periodic, use the period property value. Otherwise, try to get the compute_execution_time on the port.

Some issues though:

If I understand the standard correctly,

Going through the code in FlowLatencyAnalysisSwitch it looks like mapComponentInstance() takes care of these side issues. In particular, it assumes that if the period property is set but the dispatch_protocol is not set, then the dispatch type is periodic.

AaronGreenhouse commented 4 years ago

I believe I just need to change the lines

        double executionTimeLower = GetProperties.getScaledMinComputeExecutionTimeinMilliSec(componentInstance);
        double executionTimeHigher = GetProperties.getScaledMaxComputeExecutionTimeinMilliSec(componentInstance);

in mapComponentInstance().

AaronGreenhouse commented 4 years ago

I'm wondering if getScaledMinComputeExecutionTimeinMilliSec() and the max version are correct for ports:

    public static double getScaledMinComputeExecutionTimeinMilliSec(final NamedElement ne) {
        Property computeExecutionTime = lookupPropertyDefinition(ne, TimingProperties._NAME,
                TimingProperties.COMPUTE_EXECUTION_TIME);
        UnitLiteral milliSecond = findUnitLiteral(computeExecutionTime, AadlProject.MS_LITERAL);
        double time = PropertyUtils.getScaledRangeMinimum(ne, computeExecutionTime, milliSecond, 0.0);
        if (ne instanceof ComponentInstance) {
            double scale = getProcessorScalingFactor((ComponentInstance) ne);
            return time * scale;
        }
        return time;
    }

It only scales the result if the named element is a ComponentInstance. I think it needs to check if the named element is a feature, and if so, use the containing ComponentInstance to get the scaling factor.

jjhugues commented 4 years ago

Regarding computation of latency, In the worst-case, sporadic and hybrid are periodic, worst case is likely to be period + WCET

AaronGreenhouse commented 4 years ago

Changed

        double executionTimeLower = GetProperties.getScaledMinComputeExecutionTimeinMilliSec(componentInstance);
        double executionTimeHigher = GetProperties.getScaledMaxComputeExecutionTimeinMilliSec(componentInstance);

to

GetProperties.getScaledMaxComputeExecutionTimeinMilliSec(componentInstance);
        double executionTimeLower = getMinExecutionTimeInMilliSec(flowElementInstance, componentInstance, isPeriodic);
        double executionTimeHigher = getMaxExecutionTimeInMilliSec(flowElementInstance, componentInstance, isPeriodic);

where

    private double getExecutionTimeInMilliSec(final FlowElementInstance fei, final ComponentInstance ci,
            final boolean isPeriodic, final Function<NamedElement, Double> getExecTime) {
        /*
         * If the flow element is a component instance or if the thread is periodic, we use the thread's
         * computation time. Otherwise we try to use the compute execution time from the flow's input feature.
         */
        if (isPeriodic || fei == ci) { // the flow element is a component instance
            return getExecTime.apply(ci);
        } else { // the flow element is a FlowSpecificationInstance
            final FlowSpecificationInstance fsi = (FlowSpecificationInstance) fei;
            final FlowEnd allInEnd = fsi.getFlowSpecification().getAllInEnd();
            if (allInEnd != null) { // we have an input feature
                final FeatureInstance fi = ci.findFeatureInstance(allInEnd.getFeature());
                if (GetProperties.hasComputeExecutionTime(fi)) {
                    return getExecTime.apply(fi);
                }
            }
            return getExecTime.apply(ci);
        }
    }

    private double getMinExecutionTimeInMilliSec(final FlowElementInstance fei, final ComponentInstance ci,
            final boolean isPeriodic) {
        return getExecutionTimeInMilliSec(fei, ci, isPeriodic,
                GetProperties::getScaledMinComputeExecutionTimeinMilliSec);
    }

    private double getMaxExecutionTimeInMilliSec(final FlowElementInstance fei, final ComponentInstance ci,
            final boolean isPeriodic) {
        return getExecutionTimeInMilliSec(fei, ci, isPeriodic,
                GetProperties::getScaledMaxComputeExecutionTimeinMilliSec);
    }
AaronGreenhouse commented 4 years ago

Created test cases Periodic.aadl and Aperiodic.aadl

package Periodic
public

    system s
    end s;

    system implementation s.impl
        subcomponents
            p: process p.impl;
    end s.impl;

    process p
    end p;

    process implementation p.impl
        subcomponents
            t1_sample: thread t_out {Period => 3 ms;};
            t2: thread t_inout {Period => 15 ms; Deadline => 5ms;};
            t3: thread t_in {Period => 15ms;Compute_Execution_Time => 5ms..10ms;};
        connections
            c1: port t1_sample.p -> t2.p_i;
            x1: port t2.p_o -> t3.p1;
            x2: port t2.p_o -> t3.p2;
            x3: port t2.p_o -> t3.p3;
            x4: port t2.p_o -> t3.p4;
        flows
            e2e_sample1: end to end flow t1_sample.s -> c1 -> t2.p -> x1 -> t3.s1;
            e2e_sample2: end to end flow t1_sample.s -> c1 -> t2.p -> x2 -> t3.s2;
            e2e_sample3: end to end flow t1_sample.s -> c1 -> t2.p -> x3 -> t3.s3;
            e2e_sample4: end to end flow t1_sample.s -> c1 -> t2.p -> x4 -> t3.s4;
        properties
            Latency => 0 ms .. 100 ms applies to e2e_sample1;
            Latency => 0 ms .. 100 ms applies to e2e_sample2;
            Latency => 0 ms .. 100 ms applies to e2e_sample3;
            Latency => 0 ms .. 100 ms applies to e2e_sample4;
    end p.impl;

    thread t_out
        features
            p: out event port;
        flows
            s: flow source p;
    end t_out;

    thread t_in
        features
            p1: in event port;
            p2: in event port {Compute_Execution_Time => 15ms..20ms;};
            p3: in event port {Compute_Execution_Time => 3ms..6ms;};
            p4: in event port {Compute_Execution_Time => 20ms..23ms;};
        flows
            s1: flow sink p1;
            s2: flow sink p2;
            s3: flow sink p3;
            s4: flow sink p4;
    end t_in;

    thread t_inout
        features
            p_i: in event port;
            p_o: out event port;
        flows
            p: flow path p_i -> p_o;
    end t_inout;

end Periodic;
package Aperiodic
public

    system s
    end s;

    system implementation s.impl
        subcomponents
            p: process p.impl;
    end s.impl;

    process p
    end p;

    process implementation p.impl
        subcomponents
            t1_sample: thread t_out {Period => 3 ms;};
            t2: thread t_inout {Period => 15 ms; Deadline => 5ms;};
            t3: thread t_in {Compute_Execution_Time => 5ms..10ms;};
        connections
            c1: port t1_sample.p -> t2.p_i;
            x1: port t2.p_o -> t3.p1;
            x2: port t2.p_o -> t3.p2;
            x3: port t2.p_o -> t3.p3;
            x4: port t2.p_o -> t3.p4;
        flows
            e2e_sample1: end to end flow t1_sample.s -> c1 -> t2.p -> x1 -> t3.s1;
            e2e_sample2: end to end flow t1_sample.s -> c1 -> t2.p -> x2 -> t3.s2;
            e2e_sample3: end to end flow t1_sample.s -> c1 -> t2.p -> x3 -> t3.s3;
            e2e_sample4: end to end flow t1_sample.s -> c1 -> t2.p -> x4 -> t3.s4;
        properties
            Latency => 0 ms .. 100 ms applies to e2e_sample1;
            Latency => 0 ms .. 100 ms applies to e2e_sample2;
            Latency => 0 ms .. 100 ms applies to e2e_sample3;
            Latency => 0 ms .. 100 ms applies to e2e_sample4;
    end p.impl;

    thread t_out
        features
            p: out event port;
        flows
            s: flow source p;
    end t_out;

    thread t_in
        features
            p1: in event port; -- use the thread's property
            p2: in event port {Compute_Execution_Time => 15ms..20ms;};
            p3: in event port {Compute_Execution_Time => 3ms..6ms;};
            p4: in event port {Compute_Execution_Time => 20ms..23ms;};
        flows
            s1: flow sink p1;
            s2: flow sink p2;
            s3: flow sink p3;
            s4: flow sink p4;
    end t_in;

    thread t_inout
        features
            p_i: in event port;
            p_o: out event port;
        flows
            p: flow path p_i -> p_o;
    end t_inout;

end Aperiodic;

They are the same except that Periodic specifies a period for the thread t3. In this case, the compute execution times are not relevant.

The Aperodic example has 4 flows through 4 different in ports, one that uses the compute execution time of the thread, and three that get the compute execution time from the the ports.

My changes seems to work.

I'm still pretty sure the processing scaling factor stuff is not going to work correctly for ports. I have to test this and then probably fix it.

AaronGreenhouse commented 4 years ago

Execution times from the features are not scaled when there is a bound processor. This needs to be fixed.

I have the additional examples

package Aperiodic_Refproc
public
    processor cpu
        properties
            Processor_Capacity => 100.0 MIPS;
    end cpu;

    processor cpu_faster
        properties
            Processor_Capacity => 400.0 MIPS;
    end cpu_faster;

    system s
    end s;

    system implementation s.impl
        subcomponents
            p: process p.impl;
            myCPU: processor cpu_faster;
        properties
            Actual_Processor_Binding => (reference (myCPU)) applies to p;           
    end s.impl;

    process p
    end p;

    process implementation p.impl
        subcomponents
            t1_sample: thread t_out {Period => 3 ms;};
            t2: thread t_inout {Period => 15 ms; Deadline => 5ms;};
            t3: thread t_in {Compute_Execution_Time => 5ms..10ms;};
        connections
            c1: port t1_sample.p -> t2.p_i;
            x1: port t2.p_o -> t3.p1;
            x2: port t2.p_o -> t3.p2;
            x3: port t2.p_o -> t3.p3;
            x4: port t2.p_o -> t3.p4;
        flows
            e2e_sample1: end to end flow t1_sample.s -> c1 -> t2.p -> x1 -> t3.s1;
            e2e_sample2: end to end flow t1_sample.s -> c1 -> t2.p -> x2 -> t3.s2;
            e2e_sample3: end to end flow t1_sample.s -> c1 -> t2.p -> x3 -> t3.s3;
            e2e_sample4: end to end flow t1_sample.s -> c1 -> t2.p -> x4 -> t3.s4;
        properties
            Reference_Processor => classifier (cpu);

            Latency => 0 ms .. 100 ms applies to e2e_sample1;
            Latency => 0 ms .. 100 ms applies to e2e_sample2;
            Latency => 0 ms .. 100 ms applies to e2e_sample3;
            Latency => 0 ms .. 100 ms applies to e2e_sample4;
    end p.impl;

    thread t_out
        features
            p: out event port;
        flows
            s: flow source p;
    end t_out;

    thread t_in
        features
            p1: in event port; -- use the thread's property
            p2: in event port {Compute_Execution_Time => 15ms..20ms;};
            p3: in event port {Compute_Execution_Time => 3ms..6ms;};
            p4: in event port {Compute_Execution_Time => 20ms..23ms;};
        flows
            s1: flow sink p1;
            s2: flow sink p2;
            s3: flow sink p3;
            s4: flow sink p4;
    end t_in;

    thread t_inout
        features
            p_i: in event port;
            p_o: out event port;
        flows
            p: flow path p_i -> p_o;
    end t_inout;

end Aperiodic_Refproc;

and

package Periodic_Refproc
public
    processor cpu
        properties
            Processor_Capacity => 100.0 MIPS;
    end cpu;

    processor cpu_faster
        properties
            Processor_Capacity => 400.0 MIPS;
    end cpu_faster;

    system s
    end s;

    system implementation s.impl
        subcomponents
            p: process p.impl;
            myCPU: processor cpu_faster;
        properties
            Actual_Processor_Binding => (reference (myCPU)) applies to p;           
    end s.impl;

    process p
    end p;

    process implementation p.impl
        subcomponents
            t1_sample: thread t_out {Period => 3 ms;};
            t2: thread t_inout {Period => 15 ms; Deadline => 5ms;};
            t3: thread t_in {Period => 15ms;Compute_Execution_Time => 5ms..10ms;};
        connections
            c1: port t1_sample.p -> t2.p_i;
            x1: port t2.p_o -> t3.p1;
            x2: port t2.p_o -> t3.p2;
            x3: port t2.p_o -> t3.p3;
            x4: port t2.p_o -> t3.p4;
        flows
            e2e_sample1: end to end flow t1_sample.s -> c1 -> t2.p -> x1 -> t3.s1;
            e2e_sample2: end to end flow t1_sample.s -> c1 -> t2.p -> x2 -> t3.s2;
            e2e_sample3: end to end flow t1_sample.s -> c1 -> t2.p -> x3 -> t3.s3;
            e2e_sample4: end to end flow t1_sample.s -> c1 -> t2.p -> x4 -> t3.s4;
        properties
            Reference_Processor => classifier (cpu);

            Latency => 0 ms .. 100 ms applies to e2e_sample1;
            Latency => 0 ms .. 100 ms applies to e2e_sample2;
            Latency => 0 ms .. 100 ms applies to e2e_sample3;
            Latency => 0 ms .. 100 ms applies to e2e_sample4;
    end p.impl;

    thread t_out
        features
            p: out event port;
        flows
            s: flow source p;
    end t_out;

    thread t_in
        features
            p1: in event port;
            p2: in event port {Compute_Execution_Time => 15ms..20ms;};
            p3: in event port {Compute_Execution_Time => 3ms..6ms;};
            p4: in event port {Compute_Execution_Time => 20ms..23ms;};
        flows
            s1: flow sink p1;
            s2: flow sink p2;
            s3: flow sink p3;
            s4: flow sink p4;
    end t_in;

    thread t_inout
        features
            p_i: in event port;
            p_o: out event port;
        flows
            p: flow path p_i -> p_o;
    end t_inout;

end Periodic_Refproc;
Etienne13 commented 4 years ago

Regarding computation of latency, In the worst-case, sporadic and hybrid are periodic, worst case is likely to be period + WCET

In case the port on which the WCET is given dispatches the thread for each received data/event/eventdata, I agree with this assertion. This seems to be the assumption on examples given hereabove.

Otherwise, I think it requires to pay more attention. What if the dispatch occurs when the thread has received at least two items (e.g. Input_rate says two items per dispatch) ? What if the port does not dispatch the execution of the thread (e.g. not referenced in the Dispatch_Trigger property)?

Last remark, I still think it is odd to use the compute execution time (i.e. WCET) as if it was the response time of the task. I already filed an issue about this some time ago. I think it is on the former repo of analysis plugins.

Hope this helps ;)

Etienne.

jjhugues commented 4 years ago

@Etienne13 , you are right. I was making the assumption the system was locally (per process) schedulable. If we want instead to use response time, we must revisit the whole machinery and address osate/osate2-plugins#81

lwrage commented 4 years ago

@Etienne13 Some comments after talking to Peter about this:

AaronGreenhouse commented 4 years ago

Fixed getScaledMaxComputeExecutionTimeinMilliSec() to work with features:

    private static double scaleTime(final double time, final NamedElement ne) {
        ComponentInstance ci = null;
        if (ne instanceof FeatureInstance) {
            ci = ((FeatureInstance) ne).getComponentInstance();
        } else if (ne instanceof ComponentInstance) {
            ci = (ComponentInstance) ne;
        }

        if (ci != null) {
            double scale = getProcessorScalingFactor(ci);
            return time * scale;
        } else {
            return time;
        }
    }

    /**
     * get max execution time scaled in terms of the processor the thread is
     * bound to If it is not bound then return the specified execution time
     *
     * @param ne
     *            thread component instance
     * @return scaled time or 0.0
     */
    public static double getScaledMaxComputeExecutionTimeinMilliSec(final NamedElement ne) {
        Property computeExecutionTime = lookupPropertyDefinition(ne, TimingProperties._NAME,
                TimingProperties.COMPUTE_EXECUTION_TIME);
        UnitLiteral milliSecond = findUnitLiteral(computeExecutionTime, AadlProject.MS_LITERAL);
        double time = PropertyUtils.getScaledRangeMaximum(ne, computeExecutionTime, milliSecond, 0.0);
        return scaleTime(time, ne);
    }

Also fixed getScaledMinComputeExecutionTimeinMilliSec() and getScaledComputeExecutionTimeinSec().

AaronGreenhouse commented 4 years ago

Added unit tests.