def detect_changes()

in mozperftest_tools/mozperftest_tools/profile_enhancer.py [0:0]


    def detect_changes(self, vismet_before_ns, vismet_after_ns, threshold=5):
        """Returns the changes (improvements, AND regressions) found.

        We go through both lists and once we find a mismatch, we begin
        marking either a regression, OR, an improvment and then store it
        once we hit an equalization point.

        DO NOT use approximations here because the visual completeness
        can absolutely change in flash. This means that we begin counting
        a regression, then stop when we hit a point that is unregressed
        in the other.

        Use the threshold to determine how large the distance in progress
        must be to be classified as a change.
        """ 
        changes = []

        # Standardize the timestamps
        vismet_before = []
        vismet_after = []
        for prog in vismet_before_ns:
            new_prog = copy.deepcopy(prog)
            new_prog["timestamp"] = prog["timestamp"] - vismet_before_ns[0]["timestamp"]
            vismet_before.append(new_prog)
        for prog in vismet_after_ns:
            new_prog = copy.deepcopy(prog)
            new_prog["timestamp"] = prog["timestamp"] - vismet_after_ns[0]["timestamp"]
            vismet_after.append(new_prog)

        start_ts = None
        end_ts = None
        curr_ind = 0
        for i, prog in enumerate(vismet_before):
            if prog["percent"] == vismet_after[curr_ind]["percent"]:
                curr_ind += 1
            elif (
                prog["percent"] > vismet_after[curr_ind]["percent"] and
                prog["timestamp"] <= vismet_after[curr_ind]["timestamp"]
            ):
                # Create a regression
                # From this point until we find an inflection in the progress
                # we no longer care about the timestamp. This is because we already
                # know we're slower in the vismet_after starting from now, and
                # until the progress flips again. The same is true for an improvement.
                start_ts = vismet_after[curr_ind]["timestamp"]
                while (
                    prog["percent"] > vismet_after[curr_ind]["percent"] and
                    curr_ind + 1 < len(vismet_after)
                ):
                    curr_ind += 1
                    end_ts = vismet_after[curr_ind]["timestamp"]

                changes.append({
                    "type": "regression",
                    "start": start_ts + vismet_after_ns[0]["timestamp"],
                    "end": end_ts + vismet_after_ns[0]["timestamp"],
                })
            elif (
                prog["percent"] < vismet_after[curr_ind]["percent"] and
                prog["timestamp"] >= vismet_after[curr_ind]["timestamp"]
            ):
                # Create an improvement
                start_ts = vismet_after[curr_ind]["timestamp"]
                while (
                    prog["percent"] < vismet_after[curr_ind]["percent"] and 
                    curr_ind + 1 < len(vismet_after)
                ):
                    curr_ind += 1
                    end_ts = vismet_after[curr_ind]["timestamp"]

                changes.append({
                    "type": "improvement",
                    "start": start_ts + vismet_after_ns[0]["timestamp"],
                    "end": end_ts + vismet_after_ns[0]["timestamp"],
                })

            if curr_ind >= len(vismet_after):
                # Finished searching
                break

        # Merge the changes we've found in case there's overlap,
        # this could be resolved above but I prefer getting steps
        # of regressions as an intermediate value for debugging
        merged_changes = []
        curr_change = None
        for change in changes:
            if curr_change is None:
                curr_change = change
            elif (
                curr_change["type"] == change["type"] and 
                curr_change["end"] == change["start"]
            ):
                curr_change["end"] = change["end"]
            else:
                merged_changes.append(curr_change)
                curr_change = change
        if curr_change:
            merged_changes.append(curr_change)

        return merged_changes