Exemplo n.º 1
0
def test_profile_nemo_auto_kernels(parser):
    '''Check that valid kernels are instrumented in the NEMO API
    and that loops that do not contain kernels are not. '''
    Profiler.set_options([Profiler.KERNELS])
    psy, schedule = get_nemo_schedule(
        parser, "program do_loop\n"
        "integer :: ji\n"
        "integer, parameter :: jpj=32\n"
        "real :: sto_tmp(jpj)\n"
        "do ji = 1,jpj\n"
        "  sto_tmp(ji) = 1.0d0\n"
        "end do\n"
        "do ji = 1,jpj\n"
        "  write(*,*) sto_tmp(ji)\n"
        "end do\n"
        "end program do_loop\n")
    Profiler.add_profile_nodes(schedule, Loop)
    pnodes = schedule.walk(ProfileNode)
    # The second loop will contain a CodeBlock and therefore is not a kernel
    assert len(pnodes) == 1
    code = str(psy.gen).lower()
    # Check that it's the first loop that's had profiling added
    assert ("  type(profile_psydatatype), target, save :: profile_psy_data0\n"
            "  call profile_psy_data0 % prestart('do_loop', 'r0', 0, 0)\n"
            "  do ji = 1, jpj" in code)
Exemplo n.º 2
0
def test_lower_to_lang_level_single_node():
    ''' Test the lower_to_language_level() method when a Schedule contains
    a single ProfileNode.

    '''
    Profiler.set_options([Profiler.INVOKES])
    symbol_table = SymbolTable()
    arg1 = symbol_table.new_symbol(symbol_type=DataSymbol, datatype=REAL_TYPE)
    zero = Literal("0.0", REAL_TYPE)
    one = Literal("1.0", REAL_TYPE)
    assign1 = Assignment.create(Reference(arg1), zero)
    assign2 = Assignment.create(Reference(arg1), one)

    kschedule = KernelSchedule.create(
        "work1", symbol_table, [assign1, assign2, Return()])
    Profiler.add_profile_nodes(kschedule, Loop)
    assert isinstance(kschedule.children[0], ProfileNode)
    assert isinstance(kschedule.children[-1], Return)
    kschedule.lower_to_language_level()
    # The ProfileNode should have been replaced by two CodeBlocks with its
    # children inserted between them.
    assert isinstance(kschedule[0], CodeBlock)
    # The first CodeBlock should have the "profile-start" annotation.
    assert kschedule[0].annotations == ["profile-start"]
    ptree = kschedule[0].get_ast_nodes
    assert len(ptree) == 1
    assert isinstance(ptree[0], Fortran2003.Call_Stmt)
    assert kschedule[1] is assign1
    assert kschedule[2] is assign2
    assert isinstance(kschedule[-2], CodeBlock)
    assert kschedule[-2].annotations == []
    ptree = kschedule[-2].get_ast_nodes
    assert len(ptree) == 1
    assert isinstance(ptree[0], Fortran2003.Call_Stmt)
    assert isinstance(kschedule[-1], Return)
Exemplo n.º 3
0
def test_profile_nemo_no_acc_kernels(parser):
    ''' Check that the automatic kernel-level profiling does not add any
    calls for the case of two kernels within an OpenACC kernels region.
    No calls are added because the PSyData routines would have to have been
    compiled for execution on the GPU. '''
    acctrans = ACCKernelsTrans()
    Profiler.set_options([Profiler.KERNELS])
    psy, schedule = get_nemo_schedule(
        parser, "program do_loop\n"
        "integer, parameter :: jpi=5, jpj=5\n"
        "integer :: ji, jj\n"
        "real :: sto_tmp(jpi,jpj)\n"
        "do jj = 1, jpj\n"
        "  do ji = 1,jpi\n"
        "    sto_tmp(ji,jj) = 1.0d0\n"
        "  end do\n"
        "end do\n"
        "do ji = 1, jpi\n"
        "  sto_tmp(ji,1) = 0.0d0\n"
        "end do\n"
        "end program do_loop\n")
    acctrans.apply(schedule.children)
    Profiler.add_profile_nodes(schedule, Loop)
    code = str(psy.gen).lower()
    assert "profile_psy" not in code
Exemplo n.º 4
0
def test_lower_to_lang_level_multi_node():
    ''' Test the lower_to_language_level() method when a Schedule contains
    multiple ProfileNodes.

    '''
    # We use a GOcean example containing multiple kernel calls
    Profiler.set_options([Profiler.KERNELS])
    _, invoke = get_invoke("single_invoke_two_kernels.f90", "gocean1.0", idx=0)
    sched = invoke.schedule
    table = sched.symbol_table
    Profiler.add_profile_nodes(sched, Loop)
    sched.lower_to_language_level()
    sym0 = table.lookup("profile_psy_data")
    assert isinstance(sym0, DataSymbol)
    sym1 = table.lookup("profile_psy_data_1")
    assert isinstance(sym1, DataSymbol)
    cblocks = sched.walk(CodeBlock)
    ptree = cblocks[0].get_ast_nodes
    code = str(ptree[0]).lower()
    assert "call profile_psy_data % prestart('invoke_0', 'r0'" in code
    assert cblocks[0].annotations == ["profile-start"]
    assert cblocks[1].annotations == []
    ptree = cblocks[2].get_ast_nodes
    code = str(ptree[0]).lower()
    assert "call profile_psy_data_1 % prestart('invoke_0', 'r1'" in code
    assert cblocks[2].annotations == ["profile-start"]
    assert cblocks[3].annotations == []
Exemplo n.º 5
0
def test_profile_kernels_dynamo0p3():
    '''Check that all kernels are instrumented correctly in a
    Dynamo 0.3 invoke.
    '''
    Profiler.set_options([Profiler.KERNELS])
    _, invoke = get_invoke("1_single_invoke.f90", "dynamo0.3", idx=0)
    Profiler.add_profile_nodes(invoke.schedule, Loop)

    # Convert the invoke to code, and remove all new lines, to make
    # regex matching easier
    code = str(invoke.gen()).replace("\n", "")

    correct_re = ("subroutine invoke.*"
                  "use profile_psy_data_mod, only: profile_PSyDataType.*"
                  r"TYPE\(profile_PSyDataType\), target, save :: "
                  "profile_psy_data.*"
                  r"call profile_psy_data%PreStart\(\"single_invoke_psy\", "
                  r"\"invoke_0_testkern_type:testkern_code:r0.*\", 0, 0\).*"
                  "do cell.*"
                  "call.*"
                  "end.*"
                  r"call profile_psy_data%PostEnd")
    assert re.search(correct_re, code, re.I) is not None

    _, invoke = get_invoke("1.2_multi_invoke.f90", "dynamo0.3", idx=0)
    Profiler.add_profile_nodes(invoke.schedule, Loop)

    # Convert the invoke to code, and remove all new lines, to make
    # regex matching easier
    code = str(invoke.gen()).replace("\n", "")

    correct_re = ("subroutine invoke.*"
                  "use profile_psy_data_mod, only: profile_PSyDataType.*"
                  r"TYPE\(profile_PSyDataType\), target, save :: "
                  r"(?P<profile2>\w*) .*"
                  r"TYPE\(profile_PSyDataType\), target, save :: "
                  r"(?P<profile1>\w*) .*"
                  r"call (?P=profile1)%PreStart\(\"multi_invoke_psy\", "
                  r"\"invoke_0:testkern_code:r0\", 0, 0\).*"
                  "do cell.*"
                  "call.*"
                  "end.*"
                  r"call (?P=profile1)%PostEnd.*"
                  r"call (?P=profile2)%PreStart\(\"multi_invoke_psy\", "
                  r"\"invoke_0:testkern_code:r1\", 0, 0\).*"
                  "do cell.*"
                  "call.*"
                  "end.*"
                  r"call (?P=profile2)%PostEnd")

    groups = re.search(correct_re, code, re.I)
    assert groups is not None
    # Check that the variables are different
    assert groups.group(1) != groups.group(2)
    Profiler.set_options(None)
Exemplo n.º 6
0
def test_auto_invoke_empty_schedule(capsys):
    ''' Check the auto-invoke profiling option rejects an empty Schedule, i.e
    the routine has no statements. '''
    Profiler.set_options([Profiler.INVOKES])
    symbol_table = SymbolTable()
    # Create Schedule with Return at the start.
    kschedule = KernelSchedule.create(
        "work1", symbol_table, [])
    Profiler.add_profile_nodes(kschedule, Loop)
    assert not kschedule.walk(ProfileNode)
    _, err = capsys.readouterr()
    assert ("Not adding profiling to routine 'work1' because it does not "
            "contain any statements." in err)
Exemplo n.º 7
0
def test_profile_kernels_gocean1p0():
    '''Check that all kernels are instrumented correctly
    '''
    Profiler.set_options([Profiler.KERNELS])
    _, invoke = get_invoke("single_invoke_two_kernels.f90", "gocean1.0",
                           idx=0)
    Profiler.add_profile_nodes(invoke.schedule, Loop)

    # Convert the invoke to code, and remove all new lines, to make
    # regex matching easier
    code = str(invoke.gen()).replace("\n", "")

    # Test that kernel profiling works in case of two kernel calls
    # in a single invoke subroutine - i.e. we need to have one profile
    # start call before two nested loops, and one profile end call
    # after that.
    # Also note that the '.*' after compute_cu_code is necessary since
    # the name could be changed to avoid duplicates (depending on order
    # in which the tests are executed).
    correct_re = ("subroutine invoke.*"
                  "use profile_psy_data_mod, only: profile_PSyDataType.*"
                  r"TYPE\(profile_PSyDataType\), target, save :: "
                  "profile_psy_data.*"
                  r"TYPE\(profile_PSyDataType\), target, save :: "
                  "profile_psy_data.*"
                  r"call (?P<profile1>\w*)%PreStart\(\"psy_single_invoke_two"
                  r"_kernels\", \"invoke_0:compute_cu_code:r0\", 0, 0\).*"
                  "do j.*"
                  "do i.*"
                  "call.*"
                  "end.*"
                  "end.*"
                  r"call (?P=profile1)%PostEnd.*"
                  r"call (?P<profile2>\w*)%PreStart\(\"psy_single_invoke_two"
                  r"_kernels\", \"invoke_0:time_smooth_code:r1\", 0, 0\).*"
                  "do j.*"
                  "do i.*"
                  "call.*"
                  "end.*"
                  "end.*"
                  r"call (?P=profile2)%PostEnd")
    groups = re.search(correct_re, code, re.I)
    assert groups is not None
    assert groups.group(1) != groups.group(2)

    Profiler.set_options(None)
Exemplo n.º 8
0
def test_auto_invoke_return_last_stmt(parser):
    ''' Check that using the auto-invoke profiling option avoids including
    a return statement within the profiling region if it is the last statement
    in the routine. '''
    symbol_table = SymbolTable()
    arg1 = symbol_table.new_symbol(symbol_type=DataSymbol, datatype=REAL_TYPE)
    zero = Literal("0.0", REAL_TYPE)
    assign1 = Assignment.create(Reference(arg1), zero)
    kschedule = KernelSchedule.create("work", symbol_table,
                                      [assign1, Return()])
    # Double-check that the tree is as we expect
    assert isinstance(kschedule[-1], Return)

    Profiler.set_options([Profiler.INVOKES])
    Profiler.add_profile_nodes(kschedule, Loop)
    # The Return should be a sibling of the ProfileNode rather than a child
    assert isinstance(kschedule[0], ProfileNode)
    assert isinstance(kschedule[0].children[0].children[0], Assignment)
    assert isinstance(kschedule[1], Return)
Exemplo n.º 9
0
def test_unique_region_names():
    '''Test that unique region names are created even when the kernel
    names are identical.'''

    Profiler.set_options([Profiler.KERNELS])
    _, invoke = get_invoke("single_invoke_two_identical_kernels.f90",
                           "gocean1.0", 0)
    Profiler.add_profile_nodes(invoke.schedule, Loop)

    # Convert the invoke to code, and remove all new lines, to make
    # regex matching easier

    code = str(invoke.gen()).replace("\n", "")

    # This regular expression puts the region names into groups.
    # Make sure that the created regions have different names, even
    # though the kernels have the same name.
    correct_re = ("subroutine invoke.*"
                  "use profile_psy_Data_mod, only: profile_PSyDataType.*"
                  r"TYPE\(profile_PSyDataType\), target, save :: "
                  "profile_psy_data.*"
                  r"TYPE\(profile_PSyDataType\), target, save :: "
                  "profile_psy_data.*"
                  r"call profile_psy_data.*%PreStart\(\"psy_single_invoke_two"
                  r"_kernels\", "
                  r"\"invoke_0:compute_cu_code:r0\", 0, 0\).*"
                  "do j.*"
                  "do i.*"
                  "call compute_cu_code.*"
                  "end.*"
                  "end.*"
                  r"call profile_psy_data.*%PostEnd.*"
                  r"call profile_psy_data.*%PreStart\(\"psy_single_invoke_two_"
                  r"kernels\", \"invoke_0:compute_cu_code:r1\", 0, 0\).*"
                  "do j.*"
                  "do i.*"
                  "call compute_cu_code.*"
                  "end.*"
                  "end.*"
                  r"call profile_psy_data.*%PostEnd")

    assert re.search(correct_re, code, re.I) is not None
Exemplo n.º 10
0
def test_lower_named_profile_node():
    ''' Test that the lower_to_language_level method behaves as expected when
    a ProfileNode has pre-set names for the module and region.

    '''
    Profiler.set_options([Profiler.INVOKES])
    symbol_table = SymbolTable()
    arg1 = symbol_table.new_symbol(symbol_type=DataSymbol, datatype=REAL_TYPE)
    assign1 = Assignment.create(Reference(arg1), Literal("0.0", REAL_TYPE))
    kschedule = KernelSchedule.create("work1", symbol_table,
                                      [assign1, Return()])
    Profiler.add_profile_nodes(kschedule, Loop)
    pnode = kschedule.walk(ProfileNode)[0]
    # Manually set the module and region names (to save using a transformation)
    pnode._module_name = "my_mod"
    pnode._region_name = "first"
    kschedule.lower_to_language_level()
    cblocks = kschedule.walk(CodeBlock)
    assert ("PreStart('my_mod', 'first', 0, 0)"
            in str(cblocks[0].get_ast_nodes[0]))
Exemplo n.º 11
0
def test_profile_nemo_loop_nests(parser):
    ''' Check that the automatic kernel-level profiling handles a
    tightly-nested loop containing a valid kernel. '''
    Profiler.set_options([Profiler.KERNELS])
    psy, schedule = get_nemo_schedule(
        parser, "program do_loop\n"
        "integer :: ji, jj\n"
        "integer, parameter :: jpi=4, jpj=8\n"
        "real :: sto_tmp(jpi,jpj)\n"
        "do jj = 1, jpj\n"
        "  do ji = 1,jpi\n"
        "    sto_tmp(ji,jj) = 1.0d0\n"
        "  end do\n"
        "end do\n"
        "end program do_loop\n")
    Profiler.add_profile_nodes(schedule, Loop)
    code = str(psy.gen).lower()
    # Check that it's the outer loop that's had profiling added
    assert ("  type(profile_psydatatype), target, save :: profile_psy_data0\n"
            "  call profile_psy_data0 % prestart('do_loop', 'r0', 0, 0)\n"
            "  do jj = 1, jpj" in code)
Exemplo n.º 12
0
def test_profile_nemo_openmp(parser):
    ''' Check that the automatic kernel-level profiling handles a
    tightly-nested loop that has been parallelised using OpenMP. '''
    omptrans = OMPParallelLoopTrans()
    Profiler.set_options([Profiler.KERNELS])
    psy, schedule = get_nemo_schedule(
        parser, "program do_loop\n"
        "integer, parameter :: jpi=5, jpj=5\n"
        "integer :: ji, jj\n"
        "real :: sto_tmp(jpi,jpj)\n"
        "do jj = 1, jpj\n"
        "  do ji = 1,jpi\n"
        "    sto_tmp(ji,jj) = 1.0d0\n"
        "  end do\n"
        "end do\n"
        "end program do_loop\n")
    omptrans.apply(schedule[0])
    Profiler.add_profile_nodes(schedule, Loop)
    code = str(psy.gen).lower()
    assert ("  type(profile_psydatatype), target, save :: profile_psy_data0\n"
            "  call profile_psy_data0 % prestart('do_loop', 'r0', 0, 0)\n"
            "  !$omp parallel do default(shared), private(ji,jj), "
            "schedule(static)\n"
            "  do jj = 1, jpj" in code)
Exemplo n.º 13
0
def test_profile_nemo_loop_imperfect_nest(parser):
    ''' Check that the automatic kernel-level profiling handles a
    tightly-nested loop within an imperfectly-nested loop. '''
    Profiler.set_options([Profiler.KERNELS])
    psy, schedule = get_nemo_schedule(
        parser, "program do_loop\n"
        "integer :: ji, jj\n"
        "integer, parameter :: jpi=4, jpj=5\n"
        "integer :: npt, jt\n"
        "logical :: ln_use_this\n"
        "real :: sto_tmp(jpi,jpj)\n"
        "if(ln_use_this)then\n"
        "  do jt = 1, npt\n"
        "    do jj = 1, jpj\n"
        "      do ji = 1,jpi\n"
        "        sto_tmp(ji,jj) = 1.0d0\n"
        "      end do\n"
        "    end do\n"
        "    do ji = 1, jpi\n"
        "      sto_tmp(ji,1) = 0.0d0\n"
        "    end do\n"
        "  end do\n"
        "end if\n"
        "end program do_loop\n")
    Profiler.add_profile_nodes(schedule, Loop)
    pnodes = schedule.walk(ProfileNode)
    assert len(pnodes) == 2
    tloop = schedule[0].if_body[0]
    assert isinstance(tloop.loop_body[0], ProfileNode)
    assert isinstance(tloop.loop_body[1], ProfileNode)
    code = str(psy.gen).lower()
    assert ("        end do\n"
            "      end do\n"
            "      call profile_psy_data0 % postend\n"
            "      call profile_psy_data1 % prestart('do_loop', 'r1', 0, 0)\n"
            "      do ji = 1, jpi" in code)
Exemplo n.º 14
0
def test_auto_invoke_no_return(capsys):
    ''' Check that using the auto-invoke profiling option does not add any
    profiling if the invoke contains a Return anywhere other than as the
    last statement. '''
    Profiler.set_options([Profiler.INVOKES])
    symbol_table = SymbolTable()
    arg1 = symbol_table.new_symbol(
        symbol_type=DataSymbol, datatype=REAL_TYPE)
    zero = Literal("0.0", REAL_TYPE)
    assign1 = Assignment.create(Reference(arg1), zero)
    assign2 = Assignment.create(Reference(arg1), zero.copy())

    # Create Schedule with Return at the start.
    kschedule = KernelSchedule.create(
        "work1", symbol_table, [Return(), assign1, assign2])
    Profiler.add_profile_nodes(kschedule, Loop)
    # No profiling should have been added
    assert not kschedule.walk(ProfileNode)
    _, err = capsys.readouterr()
    assert ("Not adding profiling to routine 'work1' because it contains one "
            "or more Return statements" in err)

    # Create Schedule with Return in the middle.
    kschedule = KernelSchedule.create(
        "work2", symbol_table, [assign1.copy(), Return(), assign2.copy()])
    Profiler.add_profile_nodes(kschedule, Loop)
    # No profiling should have been added
    assert not kschedule.walk(ProfileNode)
    _, err = capsys.readouterr()
    assert ("Not adding profiling to routine 'work2' because it contains one "
            "or more Return statements" in err)

    # Create Schedule with a Return at the end as well as in the middle.
    kschedule = KernelSchedule.create(
        "work3", symbol_table, [assign1.copy(), Return(), assign2.copy(),
                                Return()])
    Profiler.add_profile_nodes(kschedule, Loop)
    # No profiling should have been added
    assert not kschedule.walk(ProfileNode)
    _, err = capsys.readouterr()
    assert ("Not adding profiling to routine 'work3' because it contains one "
            "or more Return statements" in err)
Exemplo n.º 15
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,
                           dist_mem=False)
    # This test expects constant loop bounds
    invoke.schedule._const_loop_bounds = True
    Profiler.add_profile_nodes(invoke.schedule, Loop)

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

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

    gsched = colored("GOInvokeSchedule", GOInvokeSchedule._colour)
    sched = colored("Schedule", Schedule._colour)
    loop = Loop().coloured_name(True)
    profile = invoke.schedule[0].coloured_name(True)

    # 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"
                "    0: " + profile + "[]\n"
                "        " + sched + "[]\n"
                "            0: " + loop +
                "[type='outer', field_space='go_cv', "
                "it_space='go_internal_pts']\n")
    assert expected in out

    prt = ProfileTrans()

    # 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[0].profile_body[0].loop_body[0])

    new_sched_str = str(new_sched)
    correct = ("""GOInvokeSchedule[invoke='invoke_0', \
Constant loop bounds=True]:
ProfileStart[var=profile_psy_data]
GOLoop[id:'', variable:'j', loop_type:'outer']
Literal[value:'2', Scalar<INTEGER, UNDEFINED>]
Literal[value:'jstop-1', Scalar<INTEGER, UNDEFINED>]
Literal[value:'1', Scalar<INTEGER, UNDEFINED>]
Schedule:
ProfileStart[var=profile_psy_data_1]
GOLoop[id:'', variable:'i', loop_type:'inner']
Literal[value:'2', Scalar<INTEGER, UNDEFINED>]
Literal[value:'istop', Scalar<INTEGER, UNDEFINED>]
Literal[value:'1', Scalar<INTEGER, UNDEFINED>]
Schedule:
kern call: compute_cv_code
End Schedule
End GOLoop
ProfileEnd
End Schedule
End GOLoop
GOLoop[id:'', variable:'j', loop_type:'outer']
Literal[value:'1', Scalar<INTEGER, UNDEFINED>]
Literal[value:'jstop+1', Scalar<INTEGER, UNDEFINED>]
Literal[value:'1', Scalar<INTEGER, UNDEFINED>]
Schedule:
GOLoop[id:'', variable:'i', loop_type:'inner']
Literal[value:'1', Scalar<INTEGER, UNDEFINED>]
Literal[value:'istop+1', Scalar<INTEGER, UNDEFINED>]
Literal[value:'1', Scalar<INTEGER, UNDEFINED>]
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)
Exemplo n.º 16
0
def test_profile_invokes_dynamo0p3():
    '''Check that a Dynamo 0.3 invoke is instrumented correctly
    '''
    Profiler.set_options([Profiler.INVOKES])

    # First test for a single invoke with a single kernel work as expected:
    _, invoke = get_invoke("1_single_invoke.f90", "dynamo0.3", idx=0)
    Profiler.add_profile_nodes(invoke.schedule, Loop)

    # Convert the invoke to code, and remove all new lines, to make
    # regex matching easier
    code = str(invoke.gen()).replace("\n", "")

    correct_re = ("subroutine invoke.*"
                  "use profile_psy_data_mod, only: profile_PSyDataType.*"
                  r"TYPE\(profile_PSyDataType\), target, save :: "
                  "profile_psy_data.*"
                  r"call profile_psy_data%PreStart\(\"single_invoke_psy\", "
                  r"\"invoke_0_testkern_type:testkern_code:r0\", 0, 0\).*"
                  "do cell.*"
                  "call.*"
                  "end.*"
                  r"call profile_psy_data%PostEnd")
    assert re.search(correct_re, code, re.I) is not None

    # Next test two kernels in one invoke:
    _, invoke = get_invoke("1.2_multi_invoke.f90", "dynamo0.3", idx=0)
    Profiler.add_profile_nodes(invoke.schedule, Loop)
    # Convert the invoke to code, and remove all new lines, to make
    # regex matching easier
    code = str(invoke.gen()).replace("\n", "")

    # The .* after testkern_code is necessary since the name can be changed
    # by PSyclone to avoid name duplications.
    correct_re = ("subroutine invoke.*"
                  "use profile_psy_data_mod, only: profile_PSyDataType.*"
                  r"TYPE\(profile_PSyDataType\), target, save :: "
                  "profile_psy_data.*"
                  r"call profile_psy_data%PreStart\(\"multi_invoke_psy\", "
                  r"\"invoke_0:r0.*\", 0, 0\).*"
                  "do cell.*"
                  "call.*"
                  "end.*"
                  "do cell.*"
                  "call.*"
                  "end.*"
                  r"call profile_psy_data%PostEnd")
    assert re.search(correct_re, code, re.I) is not None

    # Lastly, test an invoke whose first kernel is a builtin
    _, invoke = get_invoke("15.1.1_X_plus_Y_builtin.f90", "dynamo0.3", idx=0)
    Profiler.add_profile_nodes(invoke.schedule, Loop)
    code = str(invoke.gen())
    assert "USE profile_psy_data_mod, ONLY: profile_PSyDataType" in code
    assert "TYPE(profile_PSyDataType), target, save :: profile_psy_data" \
        in code
    assert "CALL profile_psy_data%PreStart(\"single_invoke_psy\", "\
           "\"invoke_0:x_plus_y:r0\", 0, 0)" in code
    assert "CALL profile_psy_data%PostEnd" in code

    Profiler.set_options(None)
Exemplo n.º 17
0
def test_profile_invokes_gocean1p0():
    '''Check that an invoke is instrumented correctly
    '''
    Profiler.set_options([Profiler.INVOKES])
    _, invoke = get_invoke("test11_different_iterates_over_one_invoke.f90",
                           "gocean1.0",
                           idx=0)
    Profiler.add_profile_nodes(invoke.schedule, Loop)

    # Convert the invoke to code, and remove all new lines, to make
    # regex matching easier
    code = str(invoke.gen()).replace("\n", "")

    # First a simple test that the nesting is correct - the
    # profile regions include both loops. Note that indeed
    # the function 'compute_cv_code' is in the module file
    # kernel_ne_offset_mod.
    correct_re = ("subroutine invoke.*"
                  "use profile_psy_data_mod, ONLY: profile_PSyDataType.*"
                  r"TYPE\(profile_PsyDataType\), target, save :: profile_"
                  r"psy_data.*call profile_psy_data%PreStart\(\"psy_single_"
                  r"invoke_different_iterates_over\", \"invoke_0:r0\", 0, "
                  r"0\).*"
                  "do j.*"
                  "do i.*"
                  "call.*"
                  "end.*"
                  "end.*"
                  r"call profile_psy_data%PostEnd")
    assert re.search(correct_re, code, re.I) is not None

    # Check that if gen() is called more than once the same profile
    # variables and region names are created:
    code_again = str(invoke.gen()).replace("\n", "")
    assert code == code_again

    # Test that two kernels in one invoke get instrumented correctly.
    _, invoke = get_invoke("single_invoke_two_kernels.f90", "gocean1.0", 0)
    Profiler.add_profile_nodes(invoke.schedule, Loop)

    # Convert the invoke to code, and remove all new lines, to make
    # regex matching easier
    code = str(invoke.gen()).replace("\n", "")

    correct_re = ("subroutine invoke.*"
                  "use profile_psy_data_mod, only: profile_PSyDataType.*"
                  r"TYPE\(profile_PSyDataType\), target, save :: "
                  "profile_psy_data.*"
                  r"call profile_psy_data%PreStart\(\"psy_single_invoke_two"
                  r"_kernels\", \"invoke_0:r0\", 0, 0\).*"
                  "do j.*"
                  "do i.*"
                  "call.*"
                  "end.*"
                  "end.*"
                  "do j.*"
                  "do i.*"
                  "call.*"
                  "end.*"
                  "end.*"
                  r"call profile_psy_data%PostEnd")
    assert re.search(correct_re, code, re.I) is not None
    Profiler.set_options(None)
Exemplo n.º 18
0
def generate(filename,
             api="",
             kernel_path="",
             script_name=None,
             line_length=False,
             distributed_memory=None,
             kern_out_path="",
             kern_naming="multiple"):
    # pylint: disable=too-many-arguments
    '''Takes a PSyclone algorithm specification as input and outputs the
    associated generated algorithm and psy codes suitable for
    compiling with the specified kernel(s) and support
    infrastructure. Uses the :func:`parse.algorithm.parse` function to
    parse the algorithm specification, the :class:`psyGen.PSy` class
    to generate the PSy code and the :class:`alg_gen.Alg` class to
    generate the modified algorithm code.

    :param str filename: The file containing the algorithm specification.
    :param str kernel_path: The directory from which to recursively \
                            search for the files containing the kernel \
                            source (if different from the location of the \
                            algorithm specification).
    :param str script_name: A script file that can apply optimisations \
                            to the PSy layer (can be a path to a file or \
                            a filename that relies on the PYTHONPATH to \
                            find the module).
    :param bool line_length: A logical flag specifying whether we care \
                             about line lengths being longer than 132 \
                             characters. If so, the input (algorithm \
                             and kernel) code is checked to make sure \
                             that it conforms. The default is False.
    :param bool distributed_memory: A logical flag specifying whether to \
                                    generate distributed memory code. The \
                                    default is set in the config.py file.
    :param str kern_out_path: Directory to which to write transformed \
                              kernel code.
    :param bool kern_naming: the scheme to use when re-naming transformed \
                             kernels.
    :return: 2-tuple containing fparser1 ASTs for the algorithm code and \
             the psy code.
    :rtype: (:py:class:`fparser.one.block_statements.BeginSource`, \
             :py:class:`fparser.one.block_statements.Module`)

    :raises IOError: if the filename or search path do not exist
    :raises GenerationError: if an invalid API is specified.
    :raises GenerationError: if an invalid kernel-renaming scheme is specified.

    For example:

    >>> from psyclone.generator import generate
    >>> alg, psy = generate("algspec.f90")
    >>> alg, psy = generate("algspec.f90", kernel_path="src/kernels")
    >>> alg, psy = generate("algspec.f90", script_name="optimise.py")
    >>> alg, psy = generate("algspec.f90", line_length=True)
    >>> alg, psy = generate("algspec.f90", distributed_memory=False)

    '''

    if distributed_memory is None:
        distributed_memory = Config.get().distributed_memory

    # pylint: disable=too-many-statements, too-many-locals, too-many-branches
    if api == "":
        api = Config.get().default_api
    else:
        if api not in Config.get().supported_apis:
            raise GenerationError(
                "generate: Unsupported API '{0}' specified. Supported "
                "types are {1}.".format(api,
                                        Config.get().supported_apis))

    # Store Kernel-output options in our Configuration object
    Config.get().kernel_output_dir = kern_out_path
    try:
        Config.get().kernel_naming = kern_naming
    except ValueError as verr:
        raise GenerationError(
            "Invalid kernel-renaming scheme supplied: {0}".format(str(verr)))

    if not os.path.isfile(filename):
        raise IOError("file '{0}' not found".format(filename))
    if kernel_path and not os.access(kernel_path, os.R_OK):
        raise IOError("kernel search path '{0}' not found".format(kernel_path))
    try:
        from psyclone.alg_gen import Alg
        ast, invoke_info = parse(filename,
                                 api=api,
                                 invoke_name="invoke",
                                 kernel_path=kernel_path,
                                 line_length=line_length)
        psy = PSyFactory(api, distributed_memory=distributed_memory)\
            .create(invoke_info)
        if script_name is not None:
            handle_script(script_name, psy)

        # Add profiling nodes to schedule if automatic profiling has
        # been requested.
        from psyclone.psyir.nodes import Loop
        for invoke in psy.invokes.invoke_list:
            Profiler.add_profile_nodes(invoke.schedule, Loop)

        if api not in API_WITHOUT_ALGORITHM:
            alg_gen = Alg(ast, psy).gen
        else:
            alg_gen = None
    except Exception:
        raise

    return alg_gen, psy.gen