diff --git a/test/functional/tests/performance/test_performance_reads.py b/test/functional/tests/performance/test_performance_reads.py index 825b817..9c29e6b 100644 --- a/test/functional/tests/performance/test_performance_reads.py +++ b/test/functional/tests/performance/test_performance_reads.py @@ -1,11 +1,12 @@ # # Copyright(c) 2022 Intel Corporation +# Copyright(c) 2024 Huawei Technologies Co., Ltd. # SPDX-License-Identifier: BSD-3-Clause # -from datetime import timedelta import pytest +from datetime import timedelta from api.cas import casadm from api.cas.cache_config import CacheLineSize, CacheMode, SeqCutOffPolicy from core.test_run import TestRun @@ -41,7 +42,9 @@ def fill_cas_cache(target, bs): # TODO: for disks other than Intel Optane, fio ramp is needed before fio tests on raw disk @pytest.mark.require_disk("cache", DiskTypeSet([DiskType.optane])) @pytest.mark.require_disk("core", DiskTypeLowerThan("cache")) -@pytest.mark.parametrizex("block_size", [Size(1, Unit.Blocks4096), Size(8, Unit.Blocks4096)]) +@pytest.mark.parametrizex( + "block_size", [Size(1, Unit.Blocks4096), Size(8, Unit.Blocks4096)] +) @pytest.mark.parametrizex("queue_depth", [1, 16, 32]) @pytest.mark.parametrizex("cache_line_size", CacheLineSize) def test_performance_read_hit_wt(cache_line_size, block_size, queue_depth): @@ -54,6 +57,7 @@ def test_performance_read_hit_wt(cache_line_size, block_size, queue_depth): pass_criteria: - passes performance threshold """ + processors_number = get_number_of_processors_from_cpuinfo() num_jobs = [int(processors_number / 2), processors_number] data_size = Size(20, Unit.GibiByte) @@ -92,12 +96,16 @@ def test_performance_read_hit_wt(cache_line_size, block_size, queue_depth): TestRun.LOGGER.info(str(raw_disk_results[nj])) with TestRun.step("Start cache and add core device"): - cache = casadm.start_cache(cache_part, CacheMode.WT, cache_line_size, cache_id=1) + cache = casadm.start_cache( + cache_part, CacheMode.WT, cache_line_size, cache_id=1, force=True + ) cache.set_seq_cutoff_policy(SeqCutOffPolicy.never) core = cache.add_core(core_part, core_id=1) with TestRun.step("Ensure that I/O scheduler for CAS device is 'none'"): - TestRun.executor.run_expect_success("sudo echo none > /sys/block/cas1-1/queue/scheduler") + TestRun.executor.run_expect_success( + f"sudo echo none > /sys/block/{core.path.lstrip('/dev/')}/queue/scheduler" + ) with TestRun.step("Fill the cache with data via CAS device"): fill_cas_cache(core, cache_line_size) @@ -150,7 +158,9 @@ def test_performance_read_hit_wt(cache_line_size, block_size, queue_depth): # TODO: for disks other than Intel Optane, fio ramp is needed before fio tests on raw disk @pytest.mark.require_disk("cache", DiskTypeSet([DiskType.optane])) @pytest.mark.require_disk("core", DiskTypeLowerThan("cache")) -@pytest.mark.parametrizex("block_size", [Size(1, Unit.Blocks4096), Size(8, Unit.Blocks4096)]) +@pytest.mark.parametrizex( + "block_size", [Size(1, Unit.Blocks4096), Size(8, Unit.Blocks4096)] +) @pytest.mark.parametrizex("queue_depth", [1, 16, 32]) @pytest.mark.parametrizex("cache_line_size", CacheLineSize) def test_performance_read_hit_wb(cache_line_size, block_size, queue_depth): diff --git a/test/functional/tests/performance/test_performance_write.py b/test/functional/tests/performance/test_performance_write.py new file mode 100644 index 0000000..bf0925d --- /dev/null +++ b/test/functional/tests/performance/test_performance_write.py @@ -0,0 +1,165 @@ +# +# Copyright(c) 2022 Intel Corporation +# Copyright(c) 2024 Huawei Technologies Co., Ltd. +# SPDX-License-Identifier: BSD-3-Clause +# + +import pytest + +from datetime import timedelta +from api.cas import casadm +from api.cas.cache_config import CacheLineSize, CacheMode, SeqCutOffPolicy +from core.test_run import TestRun +from storage_devices.disk import DiskType, DiskTypeSet, DiskTypeLowerThan +from test_tools.fio.fio import Fio +from test_tools.fio.fio_param import ReadWrite, IoEngine, CpusAllowedPolicy +from test_utils.os_utils import ( + set_wbt_lat, + get_number_of_processors_from_cpuinfo, + get_dut_cpu_physical_cores, +) +from test_utils.size import Unit, Size + + +# TODO: for disks other than Intel Optane, fio ramp is needed before fio tests on raw disk +@pytest.mark.require_disk("cache", DiskTypeSet([DiskType.optane])) +@pytest.mark.require_disk("core", DiskTypeLowerThan("cache")) +@pytest.mark.parametrizex( + "block_size", [Size(1, Unit.Blocks4096), Size(8, Unit.Blocks4096)] +) +@pytest.mark.parametrizex("queue_depth", [1, 16, 32]) +def test_performance_write_insert_wb(block_size, queue_depth): + """ + title: Test Open CAS performance for 100% write inserts scenario in write-back mode. + description: | + Compare write hit performance (throughput and latency) for Open CAS vs raw device + for different start command options. Open CAS in Write-Back mode device should + provide comparable throughput to bare cache device. + pass_criteria: + - passes performance threshold + """ + + processors_number = get_number_of_processors_from_cpuinfo() + num_jobs = [int(processors_number / 2), processors_number] + data_size = Size(20, Unit.GibiByte) + cache_size = Size(24, Unit.GibiByte) + cache_line_size = CacheLineSize.LINE_4KiB + raw_disk_results = {} + cas_results = {} + + fio_command = ( + Fio() + .create_command() + .direct() + .read_write(ReadWrite.randrw) + .write_percentage(100) + .io_engine(IoEngine.libaio) + .cpus_allowed(get_dut_cpu_physical_cores()) + .cpus_allowed_policy(CpusAllowedPolicy.split) + .run_time(timedelta(seconds=450)) + .block_size(block_size) + .io_depth(queue_depth) + ) + + with TestRun.step("Prepare partitions for cache and core"): + cache_device = TestRun.disks["cache"] + cache_device.create_partitions([cache_size]) + cache_part = cache_device.partitions[0] + + core_device = TestRun.disks["core"] + core_device.create_partitions([data_size]) + core_part = core_device.partitions[0] + + with TestRun.step("Measure 4KiB write performance (throughput and latency) on raw disk."): + fio_command.target(cache_part) + for nj in num_jobs: + # jobs directed to specific offsets + offset = data_size.value // nj + # round down offset to multiplication of 512 blocks + offset = Size(offset // Unit.Blocks512.get_value() * Unit.Blocks512.get_value()) + fio_command.size(offset) + for i in range(nj): + job = fio_command.add_job(f"job{i + 1}") + job.file_size((i + 1) * offset) + job.offset(i * offset) + + raw_disk_results[nj] = fio_command.run().pop() + TestRun.LOGGER.info(str(raw_disk_results[nj])) + fio_command.clear_jobs() + + with TestRun.group("Measure read performance (throughput and latency) on CAS device"): + for nj in num_jobs: + TestRun.LOGGER.info(f"Measuring performance for num_jobs={nj}") + + with TestRun.step("Start cache and add core device"): + cache = casadm.start_cache( + cache_part, CacheMode.WB, cache_line_size, cache_id=1, force=True + ) + cache.set_seq_cutoff_policy(SeqCutOffPolicy.never) + core = cache.add_core(core_part, core_id=1) + + with TestRun.step("Ensure that I/O scheduler for CAS device is 'none'"): + TestRun.executor.run_expect_success( + f"sudo echo none > /sys/block/{core.path.lstrip('/dev/')}/queue/scheduler" + ) + + with TestRun.step("Run fio on CAS device"): + fio_command.target(core) + # jobs directed to specific offsets + offset = data_size.value // nj + # round down offset to multiplication of 512 blocks + offset = Size(offset // Unit.Blocks512.get_value() * Unit.Blocks512.get_value()) + fio_command.size(offset) + for i in range(nj): + job = fio_command.add_job(f"job{i + 1}") + job.file_size((i + 1) * offset) + job.offset(i * offset) + cas_results[nj] = fio_command.run().pop() + TestRun.LOGGER.info(str(cas_results[nj])) + fio_command.clear_jobs() + + with TestRun.step("Check if write hits is equal to 0"): + cache_stats = cache.get_statistics() + write_hits = cache_stats.request_stats.write.hits + if write_hits != 0: + TestRun.LOGGER.error(f"Write hits equal to: {write_hits}, expected: 0.") + + with TestRun.step("Stop cache"): + cache.stop() + + with TestRun.step("Compare fio results"): + for nj in num_jobs: + cas_read_latency = cas_results[nj].read_completion_latency_average().microseconds + cas_write_latency = cas_results[nj].write_completion_latency_average().microseconds + disk_read_latency = raw_disk_results[nj].read_completion_latency_average().microseconds + disk_write_latency = ( + raw_disk_results[nj].write_completion_latency_average().microseconds + ) + + cas_read_iops = cas_results[nj].read_iops() + disk_read_iops = raw_disk_results[nj].read_iops() + cas_write_iops = cas_results[nj].write_iops() + disk_write_iops = raw_disk_results[nj].write_iops() + write_iops_ratio = (100 * cas_write_iops) / disk_write_iops + + TestRun.LOGGER.info( + f"Results for num_jobs={nj}, queue_depth={queue_depth}," + f" block_size={block_size}, cache_line_size={cache_line_size}" + ) + TestRun.LOGGER.info("Average read/write latency (us):") + TestRun.LOGGER.info(f" - (disk) {disk_read_latency}/{disk_write_latency}") + TestRun.LOGGER.info(f" - (CAS) {cas_read_latency}/{cas_write_latency}") + TestRun.LOGGER.info("Average read/write throughput (IOPS):") + TestRun.LOGGER.info(f" - (disk) {disk_read_iops}/{raw_disk_results[nj].write_iops()}") + TestRun.LOGGER.info(f" - (CAS) {cas_read_iops}/{cas_results[nj].write_iops()}") + TestRun.LOGGER.info(f"Write ratio: {write_iops_ratio}") + + if write_iops_ratio < 50: + TestRun.LOGGER.error(f"The write iops ratio is below expected threshold (50%).") + + +@pytest.fixture(scope="session", autouse=True) +def disable_wbt_throttling(): + TestRun.LOGGER.info("Disabling write-back throttling for cache and core devices") + set_wbt_lat(TestRun.disks["cache"], 0) + set_wbt_lat(TestRun.disks["core"], 0)