Source code for CIME.tests.test_unit_baselines_performance

#!/usr/bin/env python3

import gzip
import tempfile
import unittest
import os
from unittest import mock
from pathlib import Path

from CIME.baselines import performance
from CIME.tests.test_unit_system_tests import CPLLOG


[docs] def create_mock_case(tempdir, get_latest_cpl_logs=None): caseroot = Path(tempdir, "0", "caseroot") rundir = caseroot / "run" if get_latest_cpl_logs is not None: get_latest_cpl_logs.return_value = (str(rundir / "cpl.log.gz"),) baseline_root = Path(tempdir, "baselines") baseline_root.mkdir(parents=True, exist_ok=False) case = mock.MagicMock() return case, caseroot, rundir, baseline_root
[docs] class TestUnitBaselinesPerformance(unittest.TestCase):
[docs] @mock.patch("CIME.baselines.performance._perf_get_memory") def test_perf_get_memory_default(self, _perf_get_memory): _perf_get_memory.return_value = ("1000", "a") case = mock.MagicMock() config = mock.MagicMock() config.perf_get_memory.side_effect = AttributeError mem = performance.perf_get_memory(case, config) assert mem == ("1000", "a")
[docs] def test_perf_get_memory(self): case = mock.MagicMock() config = mock.MagicMock() config.perf_get_memory.return_value = ("1000", "a") mem = performance.perf_get_memory(case, config) assert mem == ("1000", "a")
[docs] @mock.patch("CIME.baselines.performance._perf_get_throughput") def test_perf_get_throughput_default(self, _perf_get_throughput): _perf_get_throughput.return_value = ("100", "a") case = mock.MagicMock() config = mock.MagicMock() config.perf_get_throughput.side_effect = AttributeError tput = performance.perf_get_throughput(case, config) assert tput == ("100", "a")
[docs] def test_perf_get_throughput(self): case = mock.MagicMock() config = mock.MagicMock() config.perf_get_throughput.return_value = ("100", "a") tput = performance.perf_get_throughput(case, config) assert tput == ("100", "a")
[docs] def test_get_cpl_throughput_no_file(self): throughput = performance.get_cpl_throughput("/tmp/cpl.log") assert throughput is None
[docs] def test_get_cpl_throughput(self): with tempfile.TemporaryDirectory() as tempdir: cpl_log_path = Path(tempdir, "cpl.log.gz") with gzip.open(cpl_log_path, "w") as fd: fd.write(CPLLOG.encode("utf-8")) throughput = performance.get_cpl_throughput(str(cpl_log_path)) assert throughput == 719.635
[docs] def test_get_cpl_mem_usage_gz(self): with tempfile.TemporaryDirectory() as tempdir: cpl_log_path = Path(tempdir, "cpl.log.gz") with gzip.open(cpl_log_path, "w") as fd: fd.write(CPLLOG.encode("utf-8")) mem_usage = performance.get_cpl_mem_usage(str(cpl_log_path)) assert mem_usage == [ (10102.0, 1673.89), (10103.0, 1673.89), (10104.0, 1673.89), (10105.0, 1673.89), ]
[docs] @mock.patch("CIME.baselines.performance.os.path.isfile") def test_get_cpl_mem_usage(self, isfile): isfile.return_value = True with mock.patch( "builtins.open", mock.mock_open(read_data=CPLLOG.encode("utf-8")) ) as mock_file: mem_usage = performance.get_cpl_mem_usage("/tmp/cpl.log") assert mem_usage == [ (10102.0, 1673.89), (10103.0, 1673.89), (10104.0, 1673.89), (10105.0, 1673.89), ]
[docs] def test_read_baseline_file_multi_line(self): with mock.patch( "builtins.open", mock.mock_open( read_data="sha:1df0 date:2023 1000.0\nsha:3b05 date:2023 2000.0" ), ) as mock_file: baseline = performance.read_baseline_file("/tmp/cpl-mem.log") mock_file.assert_called_with("/tmp/cpl-mem.log") assert baseline == "sha:1df0 date:2023 1000.0\nsha:3b05 date:2023 2000.0"
[docs] def test_read_baseline_file_content(self): if not os.path.exists("/tmp/cpl-mem.log"): os.mknod("/tmp/cpl-mem.log") with mock.patch( "builtins.open", mock.mock_open(read_data="sha:1df0 date:2023 1000.0") ) as mock_file: baseline = performance.read_baseline_file("/tmp/cpl-mem.log") mock_file.assert_called_with("/tmp/cpl-mem.log") assert baseline == "sha:1df0 date:2023 1000.0"
[docs] def test_write_baseline_file(self): with mock.patch("builtins.open", mock.mock_open()) as mock_file: performance.write_baseline_file("/tmp/cpl-tput.log", "1000") mock_file.assert_called_with("/tmp/cpl-tput.log", "a")
[docs] @mock.patch("CIME.baselines.performance.get_cpl_throughput") @mock.patch("CIME.baselines.performance.get_latest_cpl_logs") def test__perf_get_throughput(self, get_latest_cpl_logs, get_cpl_throughput): get_cpl_throughput.side_effect = FileNotFoundError() with tempfile.TemporaryDirectory() as tempdir: case, _, _, baseline_root = create_mock_case(tempdir, get_latest_cpl_logs) with self.assertRaises(RuntimeError): performance._perf_get_throughput(case)
[docs] @mock.patch("CIME.baselines.performance.get_cpl_mem_usage") @mock.patch("CIME.baselines.performance.get_latest_cpl_logs") def test__perf_get_memory_override(self, get_latest_cpl_logs, get_cpl_mem_usage): get_cpl_mem_usage.side_effect = FileNotFoundError() with tempfile.TemporaryDirectory() as tempdir: case, _, _, baseline_root = create_mock_case(tempdir, get_latest_cpl_logs) with self.assertRaises(RuntimeError): performance._perf_get_memory(case, "/tmp/override")
[docs] @mock.patch("CIME.baselines.performance.get_cpl_mem_usage") @mock.patch("CIME.baselines.performance.get_latest_cpl_logs") def test__perf_get_memory(self, get_latest_cpl_logs, get_cpl_mem_usage): get_cpl_mem_usage.side_effect = FileNotFoundError() with tempfile.TemporaryDirectory() as tempdir: case, _, _, baseline_root = create_mock_case(tempdir, get_latest_cpl_logs) with self.assertRaises(RuntimeError): performance._perf_get_memory(case)
[docs] @mock.patch("CIME.baselines.performance.write_baseline_file") @mock.patch("CIME.baselines.performance.perf_get_memory") @mock.patch("CIME.baselines.performance.perf_get_throughput") def test_write_baseline_skip( self, perf_get_throughput, perf_get_memory, write_baseline_file ): perf_get_throughput.return_value = "100" perf_get_memory.return_value = "1000" with tempfile.TemporaryDirectory() as tempdir: case, _, _, baseline_root = create_mock_case(tempdir) performance.perf_write_baseline( case, baseline_root, False, False, ) perf_get_throughput.assert_not_called() perf_get_memory.assert_not_called() write_baseline_file.assert_not_called()
[docs] @mock.patch("CIME.baselines.performance.write_baseline_file") @mock.patch("CIME.baselines.performance.perf_get_memory") @mock.patch("CIME.baselines.performance.perf_get_throughput") def test_write_baseline_runtimeerror( self, perf_get_throughput, perf_get_memory, write_baseline_file ): perf_get_throughput.side_effect = RuntimeError perf_get_memory.side_effect = RuntimeError with tempfile.TemporaryDirectory() as tempdir: case, _, _, baseline_root = create_mock_case(tempdir) performance.perf_write_baseline(case, baseline_root) perf_get_throughput.assert_called() perf_get_memory.assert_called() write_baseline_file.assert_not_called()
[docs] @mock.patch("CIME.baselines.performance.write_baseline_file") @mock.patch("CIME.baselines.performance.perf_get_memory") @mock.patch("CIME.baselines.performance.perf_get_throughput") def test_perf_write_baseline( self, perf_get_throughput, perf_get_memory, write_baseline_file ): perf_get_throughput.return_value = ("100", "a") perf_get_memory.return_value = ("1000", "a") with tempfile.TemporaryDirectory() as tempdir: case, _, _, baseline_root = create_mock_case(tempdir) performance.perf_write_baseline(case, baseline_root) perf_get_throughput.assert_called() perf_get_memory.assert_called() write_baseline_file.assert_any_call( str(baseline_root / "cpl-tput.log"), "100", "a" ) write_baseline_file.assert_any_call( str(baseline_root / "cpl-mem.log"), "1000", "a" )
[docs] @mock.patch("CIME.baselines.performance._perf_get_throughput") @mock.patch("CIME.baselines.performance.read_baseline_file") @mock.patch("CIME.baselines.performance.get_latest_cpl_logs") def test_perf_compare_throughput_baseline_no_baseline_file( self, get_latest_cpl_logs, read_baseline_file, _perf_get_throughput ): read_baseline_file.side_effect = FileNotFoundError _perf_get_throughput.return_value = 504 with tempfile.TemporaryDirectory() as tempdir: case, _, _, baseline_root = create_mock_case(tempdir, get_latest_cpl_logs) case.get_value.side_effect = ( str(baseline_root), "master/ERIO.ne30_g16_rx1.A.docker_gnu", "/tmp/components/cpl", 0.05, ) with self.assertRaises(FileNotFoundError): performance.perf_compare_throughput_baseline(case)
[docs] @mock.patch("CIME.baselines.performance._perf_get_throughput") @mock.patch("CIME.baselines.performance.read_baseline_file") @mock.patch("CIME.baselines.performance.get_latest_cpl_logs") def test_perf_compare_throughput_baseline_no_baseline( self, get_latest_cpl_logs, read_baseline_file, _perf_get_throughput ): read_baseline_file.return_value = "" _perf_get_throughput.return_value = ("504", "a") with tempfile.TemporaryDirectory() as tempdir: case, _, _, baseline_root = create_mock_case(tempdir, get_latest_cpl_logs) case.get_baseline_dir.return_value = str( baseline_root / "master" / "ERIO.ne30_g16_rx1.A.docker_gnu" ) case.get_value.side_effect = ( "/tmp/components/cpl", 0.05, ) (below_tolerance, comment) = performance.perf_compare_throughput_baseline( case ) assert below_tolerance is None assert ( comment == "Could not compare throughput to baseline, as baseline had no value." )
[docs] @mock.patch("CIME.baselines.performance._perf_get_throughput") @mock.patch("CIME.baselines.performance.read_baseline_file") @mock.patch("CIME.baselines.performance.get_latest_cpl_logs") def test_perf_compare_throughput_baseline_no_tolerance( self, get_latest_cpl_logs, read_baseline_file, _perf_get_throughput ): read_baseline_file.return_value = "500" _perf_get_throughput.return_value = ("504", "a") with tempfile.TemporaryDirectory() as tempdir: case, _, _, baseline_root = create_mock_case(tempdir, get_latest_cpl_logs) case.get_baseline_dir.return_value = str( baseline_root / "master" / "ERIO.ne30_g16_rx1.A.docker_gnu" ) case.get_value.side_effect = ( "/tmp/components/cpl", None, ) (below_tolerance, comment) = performance.perf_compare_throughput_baseline( case ) assert below_tolerance assert ( comment == "TPUTCOMP: Throughput changed by -0.80%: baseline=500.000 sypd, tolerance=10%, current=504.000 sypd" )
[docs] @mock.patch("CIME.baselines.performance._perf_get_throughput") @mock.patch("CIME.baselines.performance.read_baseline_file") @mock.patch("CIME.baselines.performance.get_latest_cpl_logs") def test_perf_compare_throughput_baseline_above_threshold( self, get_latest_cpl_logs, read_baseline_file, _perf_get_throughput ): read_baseline_file.return_value = "1000" _perf_get_throughput.return_value = ("504", "a") with tempfile.TemporaryDirectory() as tempdir: case, _, _, baseline_root = create_mock_case(tempdir, get_latest_cpl_logs) case.get_baseline_dir.return_value = str( baseline_root / "master" / "ERIO.ne30_g16_rx1.A.docker_gnu" ) case.get_value.side_effect = ( "/tmp/components/cpl", 0.05, ) (below_tolerance, comment) = performance.perf_compare_throughput_baseline( case ) assert not below_tolerance assert ( comment == "Error: TPUTCOMP: Throughput changed by 49.60%: baseline=1000.000 sypd, tolerance=5%, current=504.000 sypd" )
[docs] @mock.patch("CIME.baselines.performance._perf_get_throughput") @mock.patch("CIME.baselines.performance.read_baseline_file") @mock.patch("CIME.baselines.performance.get_latest_cpl_logs") def test_perf_compare_throughput_baseline( self, get_latest_cpl_logs, read_baseline_file, _perf_get_throughput ): read_baseline_file.return_value = "500" _perf_get_throughput.return_value = ("504", "a") with tempfile.TemporaryDirectory() as tempdir: case, _, _, baseline_root = create_mock_case(tempdir, get_latest_cpl_logs) case.get_baseline_dir.return_value = str( baseline_root / "master" / "ERIO.ne30_g16_rx1.A.docker_gnu" ) case.get_value.side_effect = ( "/tmp/components/cpl", 0.05, ) (below_tolerance, comment) = performance.perf_compare_throughput_baseline( case ) assert below_tolerance assert ( comment == "TPUTCOMP: Throughput changed by -0.80%: baseline=500.000 sypd, tolerance=5%, current=504.000 sypd" )
[docs] @mock.patch("CIME.baselines.performance.get_cpl_mem_usage") @mock.patch("CIME.baselines.performance.read_baseline_file") @mock.patch("CIME.baselines.performance.get_latest_cpl_logs") def test_perf_compare_memory_baseline_no_baseline( self, get_latest_cpl_logs, read_baseline_file, get_cpl_mem_usage ): read_baseline_file.return_value = "" get_cpl_mem_usage.return_value = [ (1, 1000.0), (2, 1001.0), (3, 1002.0), (4, 1003.0), ] with tempfile.TemporaryDirectory() as tempdir: case, _, _, baseline_root = create_mock_case(tempdir, get_latest_cpl_logs) case.get_baseline_dir.return_value = str( baseline_root / "master" / "ERIO.ne30_g16_rx1.A.docker_gnu" ) case.get_value.side_effect = ( "/tmp/components/cpl", 0.05, ) (below_tolerance, comment) = performance.perf_compare_memory_baseline(case) assert below_tolerance assert ( comment == "MEMCOMP: Memory usage highwater changed by 0.00%: baseline=0.000 MB, tolerance=5%, current=1003.000 MB" )
[docs] @mock.patch("CIME.baselines.performance.get_cpl_mem_usage") @mock.patch("CIME.baselines.performance.read_baseline_file") @mock.patch("CIME.baselines.performance.get_latest_cpl_logs") def test_perf_compare_memory_baseline_not_enough_samples( self, get_latest_cpl_logs, read_baseline_file, get_cpl_mem_usage ): read_baseline_file.return_value = ["1000.0"] get_cpl_mem_usage.return_value = [ (1, 1000.0), (2, 1001.0), ] with tempfile.TemporaryDirectory() as tempdir: case, _, _, baseline_root = create_mock_case(tempdir, get_latest_cpl_logs) case.get_value.side_effect = ( str(baseline_root), "master/ERIO.ne30_g16_rx1.A.docker_gnu", "/tmp/components/cpl", 0.05, ) (below_tolerance, comment) = performance.perf_compare_memory_baseline(case) assert below_tolerance is None assert comment == "Found 2 memory usage samples, need atleast 4"
[docs] @mock.patch("CIME.baselines.performance.get_cpl_mem_usage") @mock.patch("CIME.baselines.performance.read_baseline_file") @mock.patch("CIME.baselines.performance.get_latest_cpl_logs") def test_perf_compare_memory_baseline_no_baseline_file( self, get_latest_cpl_logs, read_baseline_file, get_cpl_mem_usage ): read_baseline_file.side_effect = FileNotFoundError get_cpl_mem_usage.return_value = [ (1, 1000.0), (2, 1001.0), (3, 1002.0), (4, 1003.0), ] with tempfile.TemporaryDirectory() as tempdir: case, _, _, baseline_root = create_mock_case(tempdir, get_latest_cpl_logs) case.get_value.side_effect = ( str(baseline_root), "master/ERIO.ne30_g16_rx1.A.docker_gnu", "/tmp/components/cpl", 0.05, ) with self.assertRaises(FileNotFoundError): performance.perf_compare_memory_baseline(case)
[docs] @mock.patch("CIME.baselines.performance.get_cpl_mem_usage") @mock.patch("CIME.baselines.performance.read_baseline_file") @mock.patch("CIME.baselines.performance.get_latest_cpl_logs") def test_perf_compare_memory_baseline_no_tolerance( self, get_latest_cpl_logs, read_baseline_file, get_cpl_mem_usage ): read_baseline_file.return_value = "1000.0" get_cpl_mem_usage.return_value = [ (1, 1000.0), (2, 1001.0), (3, 1002.0), (4, 1003.0), ] with tempfile.TemporaryDirectory() as tempdir: case, _, _, baseline_root = create_mock_case(tempdir, get_latest_cpl_logs) case.get_baseline_dir.return_value = str( baseline_root / "master" / "ERIO.ne30_g16_rx1.A.docker_gnu" ) case.get_value.side_effect = ( "/tmp/components/cpl", None, ) (below_tolerance, comment) = performance.perf_compare_memory_baseline(case) assert below_tolerance assert ( comment == "MEMCOMP: Memory usage highwater changed by 0.30%: baseline=1000.000 MB, tolerance=10%, current=1003.000 MB" )
[docs] @mock.patch("CIME.baselines.performance.get_cpl_mem_usage") @mock.patch("CIME.baselines.performance.read_baseline_file") @mock.patch("CIME.baselines.performance.get_latest_cpl_logs") def test_perf_compare_memory_baseline_above_threshold( self, get_latest_cpl_logs, read_baseline_file, get_cpl_mem_usage ): read_baseline_file.return_value = "1000.0" get_cpl_mem_usage.return_value = [ (1, 2000.0), (2, 2001.0), (3, 2002.0), (4, 2003.0), ] with tempfile.TemporaryDirectory() as tempdir: case, _, _, baseline_root = create_mock_case(tempdir, get_latest_cpl_logs) case.get_baseline_dir.return_value = str( baseline_root / "master" / "ERIO.ne30_g16_rx1.A.docker_gnu" ) case.get_value.side_effect = ( "/tmp/components/cpl", 0.05, ) (below_tolerance, comment) = performance.perf_compare_memory_baseline(case) assert not below_tolerance assert ( comment == "Error: MEMCOMP: Memory usage highwater changed by 100.30%: baseline=1000.000 MB, tolerance=5%, current=2003.000 MB" )
[docs] @mock.patch("CIME.baselines.performance.get_cpl_mem_usage") @mock.patch("CIME.baselines.performance.read_baseline_file") @mock.patch("CIME.baselines.performance.get_latest_cpl_logs") def test_perf_compare_memory_baseline( self, get_latest_cpl_logs, read_baseline_file, get_cpl_mem_usage ): read_baseline_file.return_value = "1000.0" get_cpl_mem_usage.return_value = [ (1, 1000.0), (2, 1001.0), (3, 1002.0), (4, 1003.0), ] with tempfile.TemporaryDirectory() as tempdir: case, _, _, baseline_root = create_mock_case(tempdir, get_latest_cpl_logs) case.get_baseline_dir.return_value = str( baseline_root / "master" / "ERIO.ne30_g16_rx1.A.docker_gnu" ) case.get_value.side_effect = ( "/tmp/components/cpl", 0.05, ) (below_tolerance, comment) = performance.perf_compare_memory_baseline(case) assert below_tolerance assert ( comment == "MEMCOMP: Memory usage highwater changed by 0.30%: baseline=1000.000 MB, tolerance=5%, current=1003.000 MB" )
[docs] def test_get_latest_cpl_logs_found_multiple(self): with tempfile.TemporaryDirectory() as tempdir: run_dir = Path(tempdir) / "run" run_dir.mkdir(parents=True, exist_ok=False) cpl_log_path = run_dir / "cpl.log.gz" cpl_log_path.touch() cpl_log_2_path = run_dir / "cpl-2023-01-01.log.gz" cpl_log_2_path.touch() case = mock.MagicMock() case.get_value.side_effect = ( str(run_dir), "mct", ) latest_cpl_logs = performance.get_latest_cpl_logs(case) assert len(latest_cpl_logs) == 2 assert sorted(latest_cpl_logs) == sorted( [str(cpl_log_path), str(cpl_log_2_path)] )
[docs] def test_get_latest_cpl_logs_found_single(self): with tempfile.TemporaryDirectory() as tempdir: run_dir = Path(tempdir) / "run" run_dir.mkdir(parents=True, exist_ok=False) cpl_log_path = run_dir / "cpl.log.gz" cpl_log_path.touch() case = mock.MagicMock() case.get_value.side_effect = ( str(run_dir), "mct", ) latest_cpl_logs = performance.get_latest_cpl_logs(case) assert len(latest_cpl_logs) == 1 assert latest_cpl_logs[0] == str(cpl_log_path)
[docs] def test_get_latest_cpl_logs(self): case = mock.MagicMock() case.get_value.side_effect = ( f"/tmp/run", "mct", ) latest_cpl_logs = performance.get_latest_cpl_logs(case) assert len(latest_cpl_logs) == 0