コード例 #1
0
ファイル: profile_node_test.py プロジェクト: hiker/PSyclone
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)
コード例 #2
0
def test_profile_errors2():
    '''Test various error handling.'''

    with pytest.raises(GenerationError) as gen_error:
        Profiler.set_options(["invalid"])
    assert ("options must be one of 'invokes', 'kernels'"
            in str(gen_error.value))
コード例 #3
0
ファイル: nemo_profile_test.py プロジェクト: mfkiwl/PSyclone
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)
コード例 #4
0
ファイル: profile_node_test.py プロジェクト: hiker/PSyclone
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 == []
コード例 #5
0
ファイル: nemo_profile_test.py プロジェクト: mfkiwl/PSyclone
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
コード例 #6
0
ファイル: profile_test.py プロジェクト: drewsilcock/PSyclone
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)

    # 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_mod, only: ProfileData.*"
                  r"TYPE\(ProfileData\), save :: profile.*"
                  r"call ProfileStart\(\"kernel_ne_offset_mod\", "
                  r"\"compute_cv_code\", profile\).*"
                  "do j.*"
                  "do i.*"
                  "call.*"
                  "end.*"
                  "end.*"
                  r"call ProfileEnd\(profile\)")
    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)

    # 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_mod, only: ProfileData.*"
                  r"TYPE\(ProfileData\), save :: profile.*"
                  r"call ProfileStart\(\"compute_cu_mod\", "
                  r"\"compute_cu_code\", profile\).*"
                  "do j.*"
                  "do i.*"
                  "call.*"
                  "end.*"
                  "end.*"
                  "do j.*"
                  "do i.*"
                  "call.*"
                  "end.*"
                  "end.*"
                  r"call ProfileEnd\(profile\)")
    assert re.search(correct_re, code, re.I) is not None
    Profiler.set_options(None)
コード例 #7
0
ファイル: profile_test.py プロジェクト: drewsilcock/PSyclone
def teardown_function():
    '''This function is called at the end of any test function. It disables
    any automatic profiling set. This is necessary in case of a test failure
    to make sure any further tests will not be ran with profiling enabled.
    It also creates a new NameSpace manager, which is responsible to create
    unique region names - this makes sure the test works if the order or
    number of tests run is changed, otherwise the created region names will
    change.
    '''
    Profiler.set_options([])
    Profiler._namespace = NameSpace()
コード例 #8
0
ファイル: profile_test.py プロジェクト: stfc/PSyclone
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)
コード例 #9
0
ファイル: profile_test.py プロジェクト: drewsilcock/PSyclone
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)

    # 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 even though the kernels have the same name, that
    # the created regions have different names. In order to be
    # flexible for future changes, we get the region names from
    # the ProfileStart calls using a regular expressions (\w*
    # being the group name enclosed in "") group. Python will store
    # those two groups and they can be accessed using the resulting
    # re object.group(n).
    correct_re = ("subroutine invoke.*"
                  "use profile_mod, only: ProfileData.*"
                  r"TYPE\(ProfileData\), save :: profile.*"
                  r"TYPE\(ProfileData\), save :: profile.*"
                  r"call ProfileStart\(\"compute_cu_mod\", \"(\w*)\", "
                  r"profile\).*"
                  "do j.*"
                  "do i.*"
                  "call compute_cu_code.*"
                  "end.*"
                  "end.*"
                  r"call ProfileEnd\(profile\).*"
                  r"call ProfileStart\(\"compute_cu_mod\", \"(\w*)\", "
                  r"profile_1\).*"
                  "do j.*"
                  "do i.*"
                  "call compute_cu_code.*"
                  "end.*"
                  "end.*"
                  r"call ProfileEnd\(profile_1\)")

    groups = re.search(correct_re, code, re.I)
    assert groups is not None

    # Check that the region names are indeed different: group(1)
    # is the first kernel region name crated by PSyclone, and
    # group(2) the name used in the second ProfileStart.
    # Those names must be different (otherwise the profiling tool
    # would likely combine the two different regions into one).
    assert groups.group(1) != groups.group(2)
コード例 #10
0
ファイル: profile_test.py プロジェクト: drewsilcock/PSyclone
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)

    # 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_mod, only: ProfileData, ProfileStart, "
                  "ProfileEnd.*"
                  r"TYPE\(ProfileData\), save :: profile.*"
                  r"call ProfileStart\(\"testkern_mod\", \"testkern_code.*\", "
                  r"profile\).*"
                  "do cell.*"
                  "call.*"
                  "end.*"
                  r"call ProfileEnd\(profile\)")
    assert re.search(correct_re, code, re.I) is not None

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

    # 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_mod, only: ProfileData, ProfileStart, "
                  "ProfileEnd.*"
                  r"TYPE\(ProfileData\), save :: profile.*"
                  r"TYPE\(ProfileData\), save :: profile.*"
                  r"call ProfileStart\(\"testkern_mod\", \"testkern_code.*\", "
                  r"(?P<profile1>\w*)\).*"
                  "do cell.*"
                  "call.*"
                  "end.*"
                  r"call ProfileEnd\((?P=profile1)\).*"
                  r"call ProfileStart\(.*, (?P<profile2>\w*)\).*"
                  "do cell.*"
                  "call.*"
                  "end.*"
                  r"call ProfileEnd\((?P=profile2)\).*")
    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)
コード例 #11
0
ファイル: profile_test.py プロジェクト: drewsilcock/PSyclone
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)

    # 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_mod, only: ProfileData.*"
                  r"TYPE\(ProfileData\), save :: profile.*"
                  r"call ProfileStart\(\"testkern_mod\", \"testkern_code\", "
                  r"profile\).*"
                  "do cell.*"
                  "call.*"
                  "end.*"
                  r"call ProfileEnd\(profile\)")
    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)

    # 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_mod, only: ProfileData.*"
                  r"TYPE\(ProfileData\), save :: profile.*"
                  r"call ProfileStart\(\"testkern_mod\", \"testkern_code.*\","
                  r" profile\).*"
                  "do cell.*"
                  "call.*"
                  "end.*"
                  "do cell.*"
                  "call.*"
                  "end.*"
                  r"call ProfileEnd\(profile\)")
    assert re.search(correct_re, code, re.I) is not None
    Profiler.set_options(None)
コード例 #12
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)
コード例 #13
0
def test_main_profile(capsys):
    '''Tests that the profiling command line flags are working as expected.
    '''
    filename = os.path.join(os.path.dirname(os.path.abspath(__file__)),
                            "test_files", "gocean1p0", "test27_loop_swap.f90")

    from psyclone.profiler import Profiler
    options = ["-api", "gocean1.0"]

    # Check for invokes only parameter:
    main(options + ["--profile", "invokes", filename])
    assert not Profiler.profile_kernels()
    assert Profiler.profile_invokes()

    # Check for kernels only parameter:
    main(options + ["--profile", "kernels", filename])
    assert Profiler.profile_kernels()
    assert not Profiler.profile_invokes()

    # Check for invokes + kernels
    main(options + ["--profile", "kernels", '--profile', 'invokes', filename])
    assert Profiler.profile_kernels()
    assert Profiler.profile_invokes()

    # Check for missing parameter (argparse then
    # takes the filename as parameter for profiler):
    with pytest.raises(SystemExit):
        main(options + ["--profile", filename])
    _, outerr = capsys.readouterr()

    correct_re = "invalid choice.*choose from 'invokes', 'kernels'"
    assert re.search(correct_re, outerr) is not None

    # Check for invalid parameter
    with pytest.raises(SystemExit):
        main(options + ["--profile", "invalid", filename])
    _, outerr = capsys.readouterr()

    assert re.search(correct_re, outerr) is not None

    # Check for warning in case of script with profiling"
    with pytest.raises(SystemExit):
        main(options + ["--profile", "kernels", "-s", "somescript", filename])
    _, out = capsys.readouterr()
    out = out.replace("\n", " ")

    warning = ("Error: use of automatic profiling in combination with an "
               "optimisation script is not recommended since it may not work "
               "as expected.")

    assert warning in out

    # Reset profile flags to avoid further failures in other tests
    Profiler.set_options(None)
コード例 #14
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)

    # 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_mod, only: ProfileData.*"
                  r"TYPE\(ProfileData\), save :: profile.*"
                  r"TYPE\(ProfileData\), save :: profile.*"
                  r"call ProfileStart\(\"compute_cu_mod\", "
                  r"\"compute_cu_code.*\", (?P<profile1>\w*)\).*"
                  "do j.*"
                  "do i.*"
                  "call.*"
                  "end.*"
                  "end.*"
                  r"call ProfileEnd\((?P=profile1)\).*"
                  r"call ProfileStart\(\"time_smooth_mod\", "
                  r"\"time_smooth_code\", (?P<profile2>\w*)\).*"
                  "do j.*"
                  "do i.*"
                  "call.*"
                  "end.*"
                  "end.*"
                  r"call ProfileEnd\((?P=profile2)\)")
    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)
コード例 #15
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)
コード例 #16
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
コード例 #17
0
ファイル: profile_node_test.py プロジェクト: hiker/PSyclone
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]))
コード例 #18
0
ファイル: nemo_profile_test.py プロジェクト: mfkiwl/PSyclone
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)
コード例 #19
0
ファイル: nemo_profile_test.py プロジェクト: mfkiwl/PSyclone
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)
コード例 #20
0
ファイル: nemo_profile_test.py プロジェクト: mfkiwl/PSyclone
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)
コード例 #21
0
def test_main_profile(capsys):
    '''Tests that the profiling command line flags are working as expected.
    '''
    filename = os.path.join(os.path.dirname(os.path.abspath(__file__)),
                            "test_files", "gocean1p0",
                            "test27_loop_swap.f90")

    options = ["-api", "gocean1.0"]

    # Check for invokes only parameter:
    main(options+["--profile", "invokes", filename])
    assert not Profiler.profile_kernels()
    assert Profiler.profile_invokes()

    # Check for kernels only parameter:
    main(options+["--profile", "kernels", filename])
    assert Profiler.profile_kernels()
    assert not Profiler.profile_invokes()

    # Check for invokes + kernels
    main(options+["--profile", "kernels",
                  '--profile', 'invokes', filename])
    assert Profiler.profile_kernels()
    assert Profiler.profile_invokes()

    # Check for missing parameter (argparse then
    # takes the filename as parameter for profiler):
    with pytest.raises(SystemExit):
        main(options+["--profile", filename])
    _, outerr = capsys.readouterr()

    correct_re = "invalid choice.*choose from 'invokes', 'kernels'"
    assert re.search(correct_re, outerr) is not None

    # Check for invalid parameter
    with pytest.raises(SystemExit):
        main(options+["--profile", "invalid", filename])
    _, outerr = capsys.readouterr()

    assert re.search(correct_re, outerr) is not None

    # Reset profile flags to avoid further failures in other tests
    Profiler.set_options(None)
コード例 #22
0
ファイル: profile_test.py プロジェクト: stfc/PSyclone
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)
コード例 #23
0
ファイル: profile_test.py プロジェクト: stfc/PSyclone
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)
コード例 #24
0
ファイル: generator.py プロジェクト: muhdfirdaus373/PSyclone
def main(args):
    '''
    Parses and checks the command line arguments, calls the generate
    function if all is well, catches any errors and outputs the
    results.
    :param list args: the list of command-line arguments that PSyclone has \
                      been invoked with.
    '''
    # pylint: disable=too-many-statements,too-many-branches

    # Make sure we have the supported APIs defined in the Config singleton,
    # but postpone loading the config file till the command line was parsed
    # in case that the user specifies a different config file.
    Config.get(do_not_load_file=True)

    parser = argparse.ArgumentParser(
        description='Run the PSyclone code generator on a particular file')
    parser.add_argument('-oalg', help='filename of transformed algorithm code')
    parser.add_argument('-opsy', help='filename of generated PSy code')
    parser.add_argument('-okern',
                        help='directory in which to put transformed kernels, '
                        'default is the current working directory.')
    parser.add_argument('-api',
                        help='choose a particular api from {0}, '
                        'default \'{1}\'.'.format(
                            str(Config.get().supported_apis),
                            Config.get().default_api))
    parser.add_argument('filename', help='algorithm-layer source code')
    parser.add_argument('-s',
                        '--script',
                        help='filename of a PSyclone'
                        ' optimisation script')
    parser.add_argument('-d',
                        '--directory',
                        default="",
                        help='path to root of directory '
                        'structure containing kernel source code')
    # Make the default an empty list so that we can check whether the
    # user has supplied a value(s) later
    parser.add_argument('-I',
                        '--include',
                        default=[],
                        action="append",
                        help='path to Fortran INCLUDE files (nemo API only)')
    parser.add_argument('-l',
                        '--limit',
                        dest='limit',
                        action='store_true',
                        default=False,
                        help='limit the fortran line length to 132 characters')
    parser.add_argument('-dm',
                        '--dist_mem',
                        dest='dist_mem',
                        action='store_true',
                        help='generate distributed memory code')
    parser.add_argument('-nodm',
                        '--no_dist_mem',
                        dest='dist_mem',
                        action='store_false',
                        help='do not generate distributed memory code')
    parser.add_argument(
        '--kernel-renaming',
        default="multiple",
        choices=configuration.VALID_KERNEL_NAMING_SCHEMES,
        help="Naming scheme to use when re-naming transformed kernels")
    parser.add_argument(
        '--profile',
        '-p',
        action="append",
        choices=Profiler.SUPPORTED_OPTIONS,
        help="Add profiling hooks for either 'kernels' or 'invokes'")
    parser.add_argument(
        '--force-profile',
        action="append",
        choices=Profiler.SUPPORTED_OPTIONS,
        help="Add profiling hooks for either 'kernels' or 'invokes' even if a "
        "transformation script is used. Use at your own risk.")
    parser.set_defaults(dist_mem=Config.get().distributed_memory)

    parser.add_argument("--config",
                        help="Config file with "
                        "PSyclone specific options.")
    parser.add_argument(
        '-v',
        '--version',
        dest='version',
        action="store_true",
        help='Display version information ({0})'.format(__VERSION__))

    args = parser.parse_args(args)

    if args.version:
        print("PSyclone version: {0}".format(__VERSION__))

    if args.script is not None and args.profile is not None:
        print(
            "Error: use of automatic profiling in combination with an\n"
            "optimisation script is not recommended since it may not work\n"
            "as expected.\n"
            "You can use --force-profile instead of --profile if you \n"
            "really want to use both options at the same time.",
            file=sys.stderr)
        exit(1)

    if args.profile is not None and args.force_profile is not None:
        print("Specify only one of --profile and --force-profile.",
              file=sys.stderr)
        exit(1)

    if args.profile:
        Profiler.set_options(args.profile)
    elif args.force_profile:
        Profiler.set_options(args.force_profile)

    # If an output directory has been specified for transformed kernels
    # then check that it is valid
    if args.okern:
        if not os.path.exists(args.okern):
            print("Specified kernel output directory ({0}) does not exist.".
                  format(args.okern),
                  file=sys.stderr)
            exit(1)
        if not os.access(args.okern, os.W_OK):
            print("Cannot write to specified kernel output directory ({0}).".
                  format(args.okern),
                  file=sys.stderr)
            exit(1)
        kern_out_path = args.okern
    else:
        # We write any transformed kernels to the current working directory
        kern_out_path = os.getcwd()

    # If no config file name is specified, args.config is none
    # and config will load the default config file.
    Config.get().load(args.config)

    # Check API, if none is specified, take the setting from the config file
    if args.api is None:
        # No command line option, use the one specified in Config - which
        # is either based on a parameter in the config file, or otherwise
        # the default:
        api = Config.get().api
    elif args.api not in Config.get().supported_apis:
        print("Unsupported API '{0}' specified. Supported API's are "
              "{1}.".format(args.api,
                            Config.get().supported_apis),
              file=sys.stderr)
        exit(1)
    else:
        # There is a valid API specified on the command line. Set it
        # as API in the config object as well.
        api = args.api
        Config.get().api = api

    # Store the search path(s) for include files
    if args.include and api != 'nemo':
        # We only support passing include paths to fparser2 and it's
        # only the NEMO API that uses fparser2 currently.
        print(
            "Setting the search path for Fortran include files "
            "(-I/--include) is only supported for the 'nemo' API.",
            file=sys.stderr)
        exit(1)

    # The Configuration manager checks that the supplied path(s) is/are
    # valid so protect with a try
    try:
        if args.include:
            Config.get().include_paths = args.include
        else:
            # Default is to instruct fparser2 to look in the directory
            # containing the file being parsed
            Config.get().include_paths = ["./"]
    except ConfigurationError as err:
        print(str(err), file=sys.stderr)
        exit(1)

    try:
        alg, psy = generate(args.filename,
                            api=api,
                            kernel_path=args.directory,
                            script_name=args.script,
                            line_length=args.limit,
                            distributed_memory=args.dist_mem,
                            kern_out_path=kern_out_path,
                            kern_naming=args.kernel_renaming)
    except NoInvokesError:
        _, exc_value, _ = sys.exc_info()
        print("Warning: {0}".format(exc_value))
        # no invoke calls were found in the algorithm file so we need
        # not need to process it, or generate any psy layer code so
        # output the original algorithm file and set the psy file to
        # be empty
        alg_file = open(args.filename)
        alg = alg_file.read()
        psy = ""
    except (OSError, IOError, ParseError, GenerationError, RuntimeError):
        _, exc_value, _ = sys.exc_info()
        print(exc_value, file=sys.stderr)
        exit(1)
    except Exception:  # pylint: disable=broad-except
        print("Error, unexpected exception, please report to the authors:",
              file=sys.stderr)
        exc_type, exc_value, exc_tb = sys.exc_info()
        print("Description ...", file=sys.stderr)
        print(exc_value, file=sys.stderr)
        print("Type ...", file=sys.stderr)
        print(exc_type, file=sys.stderr)
        print("Stacktrace ...", file=sys.stderr)
        traceback.print_tb(exc_tb, limit=20, file=sys.stderr)
        exit(1)
    if args.limit:
        fll = FortLineLength()
        psy_str = fll.process(str(psy))
        alg_str = fll.process(str(alg))
    else:
        psy_str = str(psy)
        alg_str = str(alg)
    if args.oalg is not None:
        my_file = open(args.oalg, "w")
        my_file.write(alg_str)
        my_file.close()
    else:
        print("Transformed algorithm code:\n%s" % alg_str)

    if not psy_str:
        # empty file so do not output anything
        pass
    elif args.opsy is not None:
        my_file = open(args.opsy, "w")
        my_file.write(psy_str)
        my_file.close()
    else:
        print("Generated psy layer code:\n", psy_str)
コード例 #25
0
ファイル: profile_test.py プロジェクト: drewsilcock/PSyclone
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)
コード例 #26
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
コード例 #27
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)
コード例 #28
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)
コード例 #29
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)
コード例 #30
0
def teardown_function():
    '''This function is called at the end of any test function. It disables
    any automatic profiling set. This is necessary in case of a test failure
    to make sure any further tests will not be ran with profiling enabled.
    '''
    Profiler.set_options([])