def group_timing_lines(bm_iteration_lines):
    """
    Group lines (from one benchmark iteration) by gradient call,
    specifying:
    - Update time
    - Gradient work time
    - For all partial derivatives a sublist of all lines
    Finally, the terminate time for the entire bm_iteration is also
    returned (last line in the list).
    """
    gradient_calls = []

    start_indices = []
    end_indices = []
    for ix, line in enumerate(bm_iteration_lines[:-1]):  # -1: leave out terminate line
        if line[:9] == 'worker_id':
            if bm_iteration_lines[ix - 1][:9] != 'worker_id':  # only use the first of these
                start_indices.append(ix)
        elif line[:12] == 'update_state':
            end_indices.append(ix)

    for ix in range(len(start_indices)):
        gradient_calls.append({
            'gradient_total': bm_iteration_lines[end_indices[ix]],
            'partial_derivatives': bm_iteration_lines[start_indices[ix]:end_indices[ix]]
        })

    try:
        terminate_line = bm_iteration_lines[-1]
    except IndexError:
        terminate_line = None

    return gradient_calls, terminate_line