Beispiel #1
0
def test_omp_transform():
    '''Tests that the profiling transform works correctly with OMP
     parallelisation.'''

    _, invoke = get_invoke("test27_loop_swap.f90",
                           "gocean1.0",
                           name="invoke_loop1")
    schedule = invoke.schedule

    prt = ProfileRegionTrans()
    omp_loop = GOceanOMPLoopTrans()
    omp_par = OMPParallelTrans()

    # Parallelise the first loop:
    sched1, _ = omp_loop.apply(schedule.children[0])
    sched2, _ = omp_par.apply(sched1.children[0])
    sched3, _ = prt.apply(sched2.children[0])

    correct = (
        "      CALL ProfileStart(\"boundary_conditions_ne_offset_mod\", "
        "\"bc_ssh_code\", profile)\n"
        "      !$omp parallel default(shared), private(i,j)\n"
        "      !$omp do schedule(static)\n"
        "      DO j=2,jstop\n"
        "        DO i=2,istop\n"
        "          CALL bc_ssh_code(i, j, 1, t%data, t%grid%tmask)\n"
        "        END DO \n"
        "      END DO \n"
        "      !$omp end do\n"
        "      !$omp end parallel\n"
        "      CALL ProfileEnd(profile)")
    code = str(invoke.gen())
    assert correct in code

    # Now add another profile node between the omp parallel and omp do
    # directives:
    sched3, _ = prt.apply(sched3.children[0].children[0].children[0])

    code = str(invoke.gen())

    correct = '''      CALL ProfileStart("boundary_conditions_ne_offset_mod", \
"bc_ssh_code", profile)
      !$omp parallel default(shared), private(i,j)
      CALL ProfileStart("boundary_conditions_ne_offset_mod", "bc_ssh_code_1", \
profile_1)
      !$omp do schedule(static)
      DO j=2,jstop
        DO i=2,istop
          CALL bc_ssh_code(i, j, 1, t%data, t%grid%tmask)
        END DO\x20
      END DO\x20
      !$omp end do
      CALL ProfileEnd(profile_1)
      !$omp end parallel
      CALL ProfileEnd(profile)'''
    assert correct in code
def trans(psy):
    '''
    Take the supplied psy object, add OpenACC directives and then enclose
    the whole schedule within a profiling region.

    :param psy: the PSy layer to transform.
    :type psy: :py:class:`psyclone.gocean1p0.GOPSy`

    :returns: the transformed PSy object.
    :rtype: :py:class:`psyclone.gocean1p0.GOPSy`

    '''
    from psyclone.transformations import ProfileRegionTrans
    proftrans = ProfileRegionTrans()

    # Use the trans() routine in acc_transform.py to add the OpenACC directives
    psy = acc_trans(psy)

    invoke = psy.invokes.get('invoke_0_inc_field')
    schedule = invoke.schedule

    # Enclose everything in a profiling region
    newschedule, _ = proftrans.apply(schedule.children)
    invoke.schedule = newschedule
    newschedule.view()
    return psy
Beispiel #3
0
    def add_profile_nodes(schedule, loop_class):
        '''This function inserts all required Profiling Nodes (for invokes
        and kernels, as specified on the command line) into a schedule.
        :param schedule: The schedule to instrument.
        :type schedule: :py::class::`psyclone.psyGen.Schedule` or derived class
        :param loop_class: The loop class (e.g. GOLoop, DynLoop) to instrument.
        :type loop_class: :py::class::`psyclone.psyGen.Loop` or derived class.
        '''

        from psyclone.transformations import ProfileRegionTrans
        profile_trans = ProfileRegionTrans()
        if Profiler.profile_kernels():
            for i in schedule.children:
                if isinstance(i, loop_class):
                    profile_trans.apply(i)
        if Profiler.profile_invokes():
            profile_trans.apply(schedule.children)
Beispiel #4
0
def test_profile_basic(capsys):
    '''Check basic functionality: node names, schedule view.
    '''
    from psyclone.psyGen import colored, SCHEDULE_COLOUR_MAP
    Profiler.set_options([Profiler.INVOKES])
    _, invoke = get_invoke("test11_different_iterates_over_one_invoke.f90",
                           "gocean1.0",
                           idx=0)

    assert isinstance(invoke.schedule.children[0], ProfileNode)

    invoke.schedule.view()
    out, _ = capsys.readouterr()

    gsched = colored("GOInvokeSchedule", SCHEDULE_COLOUR_MAP["Schedule"])
    loop = Loop().coloured_text
    profile = invoke.schedule.children[0].coloured_text

    # Do one test based on schedule view, to make sure colouring
    # and indentation is correct
    expected = (gsched + "[invoke='invoke_0', Constant loop bounds=True]\n"
                "    " + profile + "\n"
                "        " + loop + "[type='outer', field_space='go_cv', "
                "it_space='go_internal_pts']\n")

    assert expected in out

    prt = ProfileRegionTrans()

    # Insert a profile call between outer and inner loop.
    # This tests that we find the subroutine node even
    # if it is not the immediate parent.
    new_sched, _ = prt.apply(
        invoke.schedule.children[0].children[0].children[0])

    new_sched_str = str(new_sched)

    correct = ("""GOInvokeSchedule(Constant loop bounds=True):
ProfileStart[var=profile]
GOLoop[id:'', variable:'j', loop_type:'outer']
Literal[value:'2']
Literal[value:'jstop-1']
Literal[value:'1']
Schedule:
GOLoop[id:'', variable:'i', loop_type:'inner']
Literal[value:'2']
Literal[value:'istop']
Literal[value:'1']
Schedule:
kern call: compute_cv_code
End Schedule
End GOLoop
End Schedule
End GOLoop
GOLoop[id:'', variable:'j', loop_type:'outer']
Literal[value:'1']
Literal[value:'jstop+1']
Literal[value:'1']
Schedule:
GOLoop[id:'', variable:'i', loop_type:'inner']
Literal[value:'1']
Literal[value:'istop+1']
Literal[value:'1']
Schedule:
kern call: bc_ssh_code
End Schedule
End GOLoop
End Schedule
End GOLoop
ProfileEnd
End Schedule""")
    assert correct in new_sched_str

    Profiler.set_options(None)
Beispiel #5
0
def test_transform_errors(capsys):
    '''Tests error handling of the profile region transformation.'''

    # This has been imported and tested before, so we can assume
    # here that this all works as expected/
    _, invoke = get_invoke("test27_loop_swap.f90",
                           "gocean1.0",
                           name="invoke_loop1")

    schedule = invoke.schedule
    prt = ProfileRegionTrans()

    with pytest.raises(TransformationError) as excinfo:
        prt.apply([schedule.children[0].children[0], schedule.children[1]])
    assert "supplied nodes are not children of the same parent." \
           in str(excinfo)

    # Supply not a node object:
    with pytest.raises(TransformationError) as excinfo:
        prt.apply(5)
    assert "Argument must be a single Node in a schedule or a list of Nodes " \
           "in a schedule but have been passed an object of type: " \
           in str(excinfo)
    # Python 3 reports 'class', python 2 'type' - so just check for both
    assert "<type 'int'>" in str(excinfo) or "<class 'int'>" in str(excinfo)

    # Test that it will only allow correctly ordered nodes:
    with pytest.raises(TransformationError) as excinfo:
        sched1, _ = prt.apply([schedule.children[1], schedule.children[0]])
    assert "Children are not consecutive children of one parent:" \
           in str(excinfo)

    with pytest.raises(TransformationError) as excinfo:
        sched1, _ = prt.apply([schedule.children[0], schedule.children[2]])
    assert "Children are not consecutive children of one parent:" \
           in str(excinfo)

    # Test 3 element lists: first various incorrect ordering:
    with pytest.raises(TransformationError) as excinfo:
        sched1, _ = prt.apply(
            [schedule.children[0], schedule.children[2], schedule.children[1]])
    assert "Children are not consecutive children of one parent:" \
           in str(excinfo)

    with pytest.raises(TransformationError) as excinfo:
        sched1, _ = prt.apply(
            [schedule.children[1], schedule.children[0], schedule.children[2]])
    assert "Children are not consecutive children of one parent:" \
           in str(excinfo)

    # Just to be sure: also check that the right order does indeed work!
    sched1, _ = prt.apply(
        [schedule.children[0], schedule.children[1], schedule.children[2]])
    sched1.view()
    out, _ = capsys.readouterr()
    # out is unicode, and has no replace function, so convert to string first
    out = str(out).replace("\n", "")

    correct_re = (".*GOInvokeSchedule.*"
                  r"    .*Profile.*"
                  r"        .*Loop.*\[type='outer'.*"
                  r"        .*Loop.*\[type='outer'.*"
                  r"        .*Loop.*\[type='outer'.*")
    assert re.search(correct_re, out)

    # Test that we don't add a profile node inside a OMP do loop (which
    # would be invalid syntax):
    _, invoke = get_invoke("test27_loop_swap.f90",
                           "gocean1.0",
                           name="invoke_loop1")
    schedule = invoke.schedule

    prt = ProfileRegionTrans()
    omp_loop = GOceanOMPLoopTrans()

    # Parallelise the first loop:
    sched1, _ = omp_loop.apply(schedule.children[0])

    # Inserting a ProfileRegion inside a omp do loop is syntactically
    # incorrect, the inner part must be a do loop only:
    with pytest.raises(TransformationError) as excinfo:
        prt.apply(sched1.children[0].children[0])

    assert "A ProfileNode cannot be inserted between an OpenMP/ACC directive "\
           "and the loop(s) to which it applies!" in str(excinfo)
Beispiel #6
0
def test_transform(capsys):
    '''Tests normal behaviour of profile region transformation.'''

    _, invoke = get_invoke("test27_loop_swap.f90",
                           "gocean1.0",
                           name="invoke_loop1")
    schedule = invoke.schedule

    prt = ProfileRegionTrans()
    assert str(prt) == "Insert a profile start and end call."
    assert prt.name == "ProfileRegionTrans"

    # Try applying it to a list
    sched1, _ = prt.apply(schedule.children)

    correct = ("""GOInvokeSchedule(Constant loop bounds=True):
ProfileStart[var=profile]
GOLoop[id:'', variable:'j', loop_type:'outer']
Literal[value:'2']
Literal[value:'jstop']
Literal[value:'1']
Schedule:
GOLoop[id:'', variable:'i', loop_type:'inner']
Literal[value:'2']
Literal[value:'istop']
Literal[value:'1']
Schedule:
kern call: bc_ssh_code
End Schedule
End GOLoop
End Schedule
End GOLoop
GOLoop[id:'', variable:'j', loop_type:'outer']
Literal[value:'1']
Literal[value:'jstop+1']
Literal[value:'1']
Schedule:
GOLoop[id:'', variable:'i', loop_type:'inner']
Literal[value:'1']
Literal[value:'istop']
Literal[value:'1']
Schedule:
kern call: bc_solid_u_code
End Schedule
End GOLoop
End Schedule
End GOLoop
GOLoop[id:'', variable:'j', loop_type:'outer']
Literal[value:'1']
Literal[value:'jstop']
Literal[value:'1']
Schedule:
GOLoop[id:'', variable:'i', loop_type:'inner']
Literal[value:'1']
Literal[value:'istop+1']
Literal[value:'1']
Schedule:
kern call: bc_solid_v_code
End Schedule
End GOLoop
End Schedule
End GOLoop
ProfileEnd
End Schedule""")
    assert correct in str(sched1)

    # Now only wrap a single node - the middle loop:
    sched2, _ = prt.apply(schedule.children[0].children[1])

    correct = ("""GOInvokeSchedule(Constant loop bounds=True):
ProfileStart[var=profile]
GOLoop[id:'', variable:'j', loop_type:'outer']
Literal[value:'2']
Literal[value:'jstop']
Literal[value:'1']
Schedule:
GOLoop[id:'', variable:'i', loop_type:'inner']
Literal[value:'2']
Literal[value:'istop']
Literal[value:'1']
Schedule:
kern call: bc_ssh_code
End Schedule
End GOLoop
End Schedule
End GOLoop
ProfileStart[var=profile_1]
GOLoop[id:'', variable:'j', loop_type:'outer']
Literal[value:'1']
Literal[value:'jstop+1']
Literal[value:'1']
Schedule:
GOLoop[id:'', variable:'i', loop_type:'inner']
Literal[value:'1']
Literal[value:'istop']
Literal[value:'1']
Schedule:
kern call: bc_solid_u_code
End Schedule
End GOLoop
End Schedule
End GOLoop
ProfileEnd
GOLoop[id:'', variable:'j', loop_type:'outer']
Literal[value:'1']
Literal[value:'jstop']
Literal[value:'1']
Schedule:
GOLoop[id:'', variable:'i', loop_type:'inner']
Literal[value:'1']
Literal[value:'istop+1']
Literal[value:'1']
Schedule:
kern call: bc_solid_v_code
End Schedule
End GOLoop
End Schedule
End GOLoop
ProfileEnd
End Schedule""")
    assert correct in str(sched2)

    # Check that an sublist created from individual elements
    # can be wrapped
    sched3, _ = prt.apply(
        [sched2.children[0].children[0], sched2.children[0].children[1]])
    sched3.view()
    out, _ = capsys.readouterr()  # .replace("\n", "")
    # out is unicode, and has no replace function, so convert to string first
    out = str(out).replace("\n", "")
    correct_re = (".*GOInvokeSchedule.*"
                  r"    .*Profile.*"
                  r"        .*Profile.*"
                  r"            .*Loop.*\[type='outer'.*"
                  r"            .*Profile.*"
                  r"                .*Loop.*\[type='outer'.*"
                  r"        .*Loop.*\[type='outer'.*")
    assert re.search(correct_re, out)
Beispiel #7
0
def test_profile_basic(capsys):
    '''Check basic functionality: node names, schedule view.
    '''
    Profiler.set_options([Profiler.INVOKES])
    _, invoke = get_invoke("test11_different_iterates_over_one_invoke.f90",
                           "gocean1.0", idx=0)

    assert isinstance(invoke.schedule.children[0], ProfileNode)

    invoke.schedule.view()
    out, _ = capsys.readouterr()

    coloured_schedule = GOSchedule([]).coloured_text
    coloured_loop = Loop().coloured_text
    coloured_kern = GOKern().coloured_text
    coloured_profile = invoke.schedule.children[0].coloured_text

    # Do one test based on schedule view, to make sure colouring
    # and indentation is correct
    correct = (
        '''{0}[invoke='invoke_0',Constant loop bounds=True]
    {3}
        {1}[type='outer',field_space='go_cv',it_space='go_internal_pts']
            {1}[type='inner',field_space='go_cv',it_space='go_internal_pts']
                {2} compute_cv_code(cv_fld,p_fld,v_fld) '''
        '''[module_inline=False]
        {1}[type='outer',field_space='go_ct',it_space='go_all_pts']
            {1}[type='inner',field_space='go_ct',it_space='go_all_pts']
                {2} bc_ssh_code(ncycle,p_fld,tmask) '''
        '''[module_inline=False]'''.format(coloured_schedule, coloured_loop,
                                           coloured_kern, coloured_profile)
    )
    assert correct in out

    prt = ProfileRegionTrans()

    # Insert a profile call between outer and inner loop.
    # This tests that we find the subroutine node even
    # if it is not the immediate parent.
    new_sched, _ = prt.apply(invoke.schedule.children[0]
                             .children[0].children[0])

    new_sched_str = str(new_sched)

    correct = ("""GOSchedule(Constant loop bounds=True):
ProfileStart[var=profile]
Loop[]: j= lower=2,jstop-1,1
ProfileStart[var=profile_1]
Loop[]: i= lower=2,istop,1
kern call: compute_cv_code
EndLoop
ProfileEnd
EndLoop
Loop[]: j= lower=1,jstop+1,1
Loop[]: i= lower=1,istop+1,1
kern call: bc_ssh_code
EndLoop
EndLoop
ProfileEnd
End Schedule""")

    assert correct in new_sched_str

    Profiler.set_options(None)