From d8ae889aa25207660c50c99f67ccaf6ad30b1cf6 Mon Sep 17 00:00:00 2001 From: yukaribbba Date: Sun, 30 Jun 2024 11:32:36 +0800 Subject: [PATCH 01/33] Create performance_tests.rst --- doc/source/performance_tests.rst | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 doc/source/performance_tests.rst diff --git a/doc/source/performance_tests.rst b/doc/source/performance_tests.rst new file mode 100644 index 0000000000..ce388659a8 --- /dev/null +++ b/doc/source/performance_tests.rst @@ -0,0 +1,4 @@ +================= +Performance Tests +================= + From ca86340cf6798d0e60129b29e58a71a2efe9a88f Mon Sep 17 00:00:00 2001 From: yukaribbba Date: Sun, 30 Jun 2024 12:07:57 +0800 Subject: [PATCH 02/33] Update index.rst --- doc/source/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/index.rst b/doc/source/index.rst index 66a069fcda..0a52c03929 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -77,6 +77,7 @@ Documentation Satpy API faq + performance_tests Release Notes Security Policy From 6b3ad97de014374c64aebdf8ae38bc6ec1e0a819 Mon Sep 17 00:00:00 2001 From: yukaribbba Date: Sun, 30 Jun 2024 16:28:22 +0800 Subject: [PATCH 03/33] adding --- doc/source/index.rst | 10 ++- .../abi_l1b_tests.rst} | 2 +- doc/source/performance_tests/index.rst | 81 +++++++++++++++++++ 3 files changed, 91 insertions(+), 2 deletions(-) rename doc/source/{performance_tests.rst => performance_tests/abi_l1b_tests.rst} (67%) create mode 100644 doc/source/performance_tests/index.rst diff --git a/doc/source/index.rst b/doc/source/index.rst index 0a52c03929..e05aa34555 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -77,7 +77,15 @@ Documentation Satpy API faq - performance_tests + +.. toctree:: + :maxdepth: 2 + + performance_tests/index + +.. toctree:: + :maxdepth: 1 + Release Notes Security Policy diff --git a/doc/source/performance_tests.rst b/doc/source/performance_tests/abi_l1b_tests.rst similarity index 67% rename from doc/source/performance_tests.rst rename to doc/source/performance_tests/abi_l1b_tests.rst index ce388659a8..145f44a168 100644 --- a/doc/source/performance_tests.rst +++ b/doc/source/performance_tests/abi_l1b_tests.rst @@ -1,4 +1,4 @@ ================= -Performance Tests +Tests for abi_l1b ================= diff --git a/doc/source/performance_tests/index.rst b/doc/source/performance_tests/index.rst new file mode 100644 index 0000000000..e3f9163a9f --- /dev/null +++ b/doc/source/performance_tests/index.rst @@ -0,0 +1,81 @@ +================= +Performance Tests +================= + +For better performace tweaks on specific readers, here're the results from a series +of tests involving ``DASK_ARRAY__CHUNK_SIZE``, ``DASK_NUM_WORKERS`` and other options +mentioned in :doc:`FAQ <../faq>`. + +.. toctree:: + :maxdepth: 1 + + abi_l1b_tests + + +Test platform +------------- ++----------+--------------------------------------+ +| CPU | 1x 8-core, 8-thread i7-9700k @4.6GHz | ++----------+--------------------------------------+ +| Memory | 2x 32GB DDR4 | ++----------+--------------------------------------+ +| SSD | 1x Samsung 980 Pro PCI-E 2TB | ++----------+--------------------------------------+ +| OS | Windows 11 23H2 Workstation Pro | ++----------+--------------------------------------+ + + +Conda environment +----------------- ++------------+--------------------------------------+ +| Channel | conda-forge | ++------------+--------------------------------------+ +| Python | 3.12.3 | ++------------+--------------------------------------+ +| dask | 2024.6.2 | ++------------+--------------------------------------+ +| numpy | 2.0.0 | ++------------+--------------------------------------+ +| satpy | 0.49 | ++------------+--------------------------------------+ +| pyresample | 1.28.3 | ++------------+--------------------------------------+ +| pyspectral | 0.13.1 | ++------------+--------------------------------------+ +| psutil | 6.0.0 | ++------------+--------------------------------------+ + + +Test procedure +-------------- +- Each round will go through 5 scenes. + +- The composite will usually be the default ``true_color`` which requires heavy computation like atmospheric corrections. + +- A new monitor thread using ``psutil`` will record the CPU and memory usage synchronously. The sample rate is around 0.5 seconds. + +- When the current round finished, the machine will take a 2-min rest to let the CPU cool down. + +- After that, reboot will clear the system cache and prevent the test program from taking advantage of it. + + +Test conditions +--------------- ++------------------------------------+--------------------------------------------------------+ +| DASK_ARRAY__CHUNK_SIZE (in MiB) | 16, 32, 64, 96, 128 | ++------------------------------------+--------------------------------------------------------+ +| DASK_ARRAY__CHUNK_SIZE (in arrays) | 512x512, 1024x1024, 2048x2048, 3072x3072, 4096x4096 | ++------------------------------------+--------------------------------------------------------+ +| DASK_NUM_WORKERS | 8, 12, 16 | ++------------------------------------+--------------------------------------------------------+ +| OMP_NUM_THREADS | 8 | ++------------------------------------+--------------------------------------------------------+ +| generate=False | Used when the composite requires different resolutions | ++------------------------------------+--------------------------------------------------------+ +| nprocs=8 | Used on ``nearest`` or ``bilinear`` resampling | ++------------------------------------+--------------------------------------------------------+ +| resampling cache | Used on ``nearest`` or ``bilinear`` resampling | ++------------------------------------+--------------------------------------------------------+ + +General conclusions +------------------- \ No newline at end of file From 26b60a1886bb39591365dc8a858cb831b4417314 Mon Sep 17 00:00:00 2001 From: yukaribbba Date: Sun, 30 Jun 2024 23:55:18 +0800 Subject: [PATCH 04/33] Update abi_l1b_tests.rst --- .../performance_tests/abi_l1b_tests.rst | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/doc/source/performance_tests/abi_l1b_tests.rst b/doc/source/performance_tests/abi_l1b_tests.rst index 145f44a168..663bf7eaaf 100644 --- a/doc/source/performance_tests/abi_l1b_tests.rst +++ b/doc/source/performance_tests/abi_l1b_tests.rst @@ -2,3 +2,47 @@ Tests for abi_l1b ================= + +Recommended Settings +==================== + + +Result Table +============ + +Fulldisk - ``native`` resampling +-------------------------------- ++----------------------------+-------------------+----------------+----------+-----------------+-----------------+------------+--------+ +| Dask Array Chunk Size (MB) | Dask Num Workers | Omp Num Threads| Time (s) | Avg Memory (GB) | Max Memory (GB) | Avg CPU (%)| Errors | ++============================+===================+================+==========+=================+=================+============+========+ +| 16 | 8 | 8 | 151.01 | 4.96 | 9.36 | 59.54 | | ++----------------------------+-------------------+----------------+----------+-----------------+-----------------+------------+--------+ +| 16 | 12 | 8 | 151.22 | 6.33 | 11.96 | 69.05 | | ++----------------------------+-------------------+----------------+----------+-----------------+-----------------+------------+--------+ +| 16 | 16 | 8 | 152.99 | 7.88 | 14.46 | 68.87 | | ++----------------------------+-------------------+----------------+----------+-----------------+-----------------+------------+--------+ +| 32 | 8 | 8 | 152.23 | 8.75 | 18.61 | 60.45 | | ++----------------------------+-------------------+----------------+----------+-----------------+-----------------+------------+--------+ +| 32 | 12 | 8 | 153.49 | 11.95 | 23.23 | 64.36 | | ++----------------------------+-------------------+----------------+----------+-----------------+-----------------+------------+--------+ +| 32 | 16 | 8 | 155.00 | 15.03 | 34.72 | 64.39 | | ++----------------------------+-------------------+----------------+----------+-----------------+-----------------+------------+--------+ +| 64 | 8 | 8 | 165.17 | 18.23 | 42.97 | 53.77 | | ++----------------------------+-------------------+----------------+----------+-----------------+-----------------+------------+--------+ +| 64 | 12 | 8 | 165.79 | 25.83 | 60.45 | 51.67 | | ++----------------------------+-------------------+----------------+----------+-----------------+-----------------+------------+--------+ +| 64 | 16 | 8 | 166.13 | 33.48 | 78.64 | 58.23 | | ++----------------------------+-------------------+----------------+----------+-----------------+-----------------+------------+--------+ +| 96 | 8 | 8 | 169.79 | 26.62 | 74.84 | 51.73 | | ++----------------------------+-------------------+----------------+----------+-----------------+-----------------+------------+--------+ +| 96 | 12 | 8 | 178.59 | 37.50 | 97.69 | 53.16 | | ++----------------------------+-------------------+----------------+----------+-----------------+-----------------+------------+--------+ +| 96 | 16 | 8 | 197.83 | 45.96 | 122.95 | 50.77 | | ++----------------------------+-------------------+----------------+----------+-----------------+-----------------+------------+--------+ +| 128 | 8 | 8 | 165.92 | 27.69 | 68.20 | 54.77 | | ++----------------------------+-------------------+----------------+----------+-----------------+-----------------+------------+--------+ +| 128 | 12 | 8 | 177.71 | 37.22 | 98.93 | 54.49 | | ++----------------------------+-------------------+----------------+----------+-----------------+-----------------+------------+--------+ +| 128 | 16 | 8 | 205.90 | 49.01 | 124.45 | 50.47 | | ++----------------------------+-------------------+----------------+----------+-----------------+-----------------+------------+--------+ + From e5641d326de31a144300f4a262052185a7377b29 Mon Sep 17 00:00:00 2001 From: yukaribbba Date: Mon, 1 Jul 2024 00:28:10 +0800 Subject: [PATCH 05/33] Update abi_l1b_tests.rst --- .../performance_tests/abi_l1b_tests.rst | 149 ++++++++++++++---- 1 file changed, 114 insertions(+), 35 deletions(-) diff --git a/doc/source/performance_tests/abi_l1b_tests.rst b/doc/source/performance_tests/abi_l1b_tests.rst index 663bf7eaaf..85631f0f2a 100644 --- a/doc/source/performance_tests/abi_l1b_tests.rst +++ b/doc/source/performance_tests/abi_l1b_tests.rst @@ -10,39 +10,118 @@ Recommended Settings Result Table ============ -Fulldisk - ``native`` resampling --------------------------------- -+----------------------------+-------------------+----------------+----------+-----------------+-----------------+------------+--------+ -| Dask Array Chunk Size (MB) | Dask Num Workers | Omp Num Threads| Time (s) | Avg Memory (GB) | Max Memory (GB) | Avg CPU (%)| Errors | -+============================+===================+================+==========+=================+=================+============+========+ -| 16 | 8 | 8 | 151.01 | 4.96 | 9.36 | 59.54 | | -+----------------------------+-------------------+----------------+----------+-----------------+-----------------+------------+--------+ -| 16 | 12 | 8 | 151.22 | 6.33 | 11.96 | 69.05 | | -+----------------------------+-------------------+----------------+----------+-----------------+-----------------+------------+--------+ -| 16 | 16 | 8 | 152.99 | 7.88 | 14.46 | 68.87 | | -+----------------------------+-------------------+----------------+----------+-----------------+-----------------+------------+--------+ -| 32 | 8 | 8 | 152.23 | 8.75 | 18.61 | 60.45 | | -+----------------------------+-------------------+----------------+----------+-----------------+-----------------+------------+--------+ -| 32 | 12 | 8 | 153.49 | 11.95 | 23.23 | 64.36 | | -+----------------------------+-------------------+----------------+----------+-----------------+-----------------+------------+--------+ -| 32 | 16 | 8 | 155.00 | 15.03 | 34.72 | 64.39 | | -+----------------------------+-------------------+----------------+----------+-----------------+-----------------+------------+--------+ -| 64 | 8 | 8 | 165.17 | 18.23 | 42.97 | 53.77 | | -+----------------------------+-------------------+----------------+----------+-----------------+-----------------+------------+--------+ -| 64 | 12 | 8 | 165.79 | 25.83 | 60.45 | 51.67 | | -+----------------------------+-------------------+----------------+----------+-----------------+-----------------+------------+--------+ -| 64 | 16 | 8 | 166.13 | 33.48 | 78.64 | 58.23 | | -+----------------------------+-------------------+----------------+----------+-----------------+-----------------+------------+--------+ -| 96 | 8 | 8 | 169.79 | 26.62 | 74.84 | 51.73 | | -+----------------------------+-------------------+----------------+----------+-----------------+-----------------+------------+--------+ -| 96 | 12 | 8 | 178.59 | 37.50 | 97.69 | 53.16 | | -+----------------------------+-------------------+----------------+----------+-----------------+-----------------+------------+--------+ -| 96 | 16 | 8 | 197.83 | 45.96 | 122.95 | 50.77 | | -+----------------------------+-------------------+----------------+----------+-----------------+-----------------+------------+--------+ -| 128 | 8 | 8 | 165.92 | 27.69 | 68.20 | 54.77 | | -+----------------------------+-------------------+----------------+----------+-----------------+-----------------+------------+--------+ -| 128 | 12 | 8 | 177.71 | 37.22 | 98.93 | 54.49 | | -+----------------------------+-------------------+----------------+----------+-----------------+-----------------+------------+--------+ -| 128 | 16 | 8 | 205.90 | 49.01 | 124.45 | 50.47 | | -+----------------------------+-------------------+----------------+----------+-----------------+-----------------+------------+--------+ +Fulldisk - native resampling +---------------------------- ++------------+---------+--------+--------+--------+-------+--------+ +| Dask Array | Dask | Time | Avg | Max | Avg | Errors | +| Chunk Size | Num | (s) | Memory | Memory | CPU | | +| (MB) | Workers | | (GB) | (GB) | (%) | | ++------------+---------+--------+--------+--------+-------+--------+ +| 16 | 8 | 151.01 | 4.96 | 9.36 | 59.54 | N/A | ++------------+---------+--------+--------+--------+-------+--------+ +| 16 | 12 | 151.22 | 6.33 | 11.96 | 69.05 | N/A | ++------------+---------+--------+--------+--------+-------+--------+ +| 16 | 16 | 152.99 | 7.88 | 14.46 | 68.87 | N/A | ++------------+---------+--------+--------+--------+-------+--------+ +| 32 | 8 | 152.23 | 8.75 | 18.61 | 60.45 | N/A | ++------------+---------+--------+--------+--------+-------+--------+ +| 32 | 12 | 153.49 | 11.95 | 23.23 | 64.36 | N/A | ++------------+---------+--------+--------+--------+-------+--------+ +| 32 | 16 | 155.00 | 15.03 | 34.72 | 64.39 | N/A | ++------------+---------+--------+--------+--------+-------+--------+ +| 64 | 8 | 165.17 | 18.23 | 42.97 | 53.77 | N/A | ++------------+---------+--------+--------+--------+-------+--------+ +| 64 | 12 | 165.79 | 25.83 | 60.45 | 51.67 | N/A | ++------------+---------+--------+--------+--------+-------+--------+ +| 64 | 16 | 166.13 | 33.48 | 78.64 | 58.23 | N/A | ++------------+---------+--------+--------+--------+-------+--------+ +| 96 | 8 | 169.79 | 26.62 | 74.84 | 51.73 | N/A | ++------------+---------+--------+--------+--------+-------+--------+ +| 96 | 12 | 178.59 | 37.50 | 97.69 | 53.16 | N/A | ++------------+---------+--------+--------+--------+-------+--------+ +| 96 | 16 | 197.83 | 45.96 | 122.95 | 50.77 | N/A | ++------------+---------+--------+--------+--------+-------+--------+ +| 128 | 8 | 165.92 | 27.69 | 68.20 | 54.77 | N/A | ++------------+---------+--------+--------+--------+-------+--------+ +| 128 | 12 | 177.71 | 37.22 | 98.93 | 54.49 | N/A | ++------------+---------+--------+--------+--------+-------+--------+ +| 128 | 16 | 205.90 | 49.01 | 124.45 | 50.47 | N/A | ++------------+---------+--------+--------+--------+-------+--------+ + +Local area - nearest resampling (with cache) +-------------------------------------------- ++------------+---------+--------+--------+--------+-------+--------+ +| Dask Array | Dask | Time | Avg | Max | Avg | Errors | +| Chunk Size | Num | (s) | Memory | Memory | CPU | | +| (MB) | Workers | | (GB) | (GB) | (%) | | ++------------+---------+--------+--------+--------+-------+--------+ +| 16 | 8 | 41.94 | 4.34 | 7.8 | 32.98 | N/A | ++------------+---------+--------+--------+--------+-------+--------+ +| 16 | 12 | 41.79 | 4.7 | 9.51 | 38.32 | N/A | ++------------+---------+--------+--------+--------+-------+--------+ +| 16 | 16 | 41.99 | 5.05 | 10.4 | 39.35 | N/A | ++------------+---------+--------+--------+--------+-------+--------+ +| 32 | 8 | 39.94 | 4.94 | 12.01 | 34.29 | N/A | ++------------+---------+--------+--------+--------+-------+--------+ +| 32 | 12 | 39.81 | 5.46 | 16.26 | 37.66 | N/A | ++------------+---------+--------+--------+--------+-------+--------+ +| 32 | 16 | 39.99 | 6.21 | 20.67 | 36.37 | N/A | ++------------+---------+--------+--------+--------+-------+--------+ +| 64 | 8 | 41.38 | 6.17 | 19.36 | 33.5 | N/A | ++------------+---------+--------+--------+--------+-------+--------+ +| 64 | 12 | 40.29 | 7.03 | 23.65 | 37.03 | N/A | ++------------+---------+--------+--------+--------+-------+--------+ +| 64 | 16 | 39.91 | 7.44 | 25.28 | 38.13 | N/A | ++------------+---------+--------+--------+--------+-------+--------+ +| 96 | 8 | 40.43 | 7.15 | 23.24 | 34.66 | N/A | ++------------+---------+--------+--------+--------+-------+--------+ +| 96 | 12 | 40.31 | 7.09 | 22.12 | 35.33 | N/A | ++------------+---------+--------+--------+--------+-------+--------+ +| 96 | 16 | 39.94 | 7.31 | 23.11 | 36.4 | N/A | ++------------+---------+--------+--------+--------+-------+--------+ +| 128 | 8 | 42.9 | 7.85 | 26.48 | 31.27 | N/A | ++------------+---------+--------+--------+--------+-------+--------+ +| 128 | 12 | 43.78 | 7.94 | 27.72 | 30.78 | N/A | ++------------+---------+--------+--------+--------+-------+--------+ +| 128 | 16 | 42.17 | 8.05 | 28.56 | 31.87 | N/A | ++------------+---------+--------+--------+--------+-------+--------+ + + +Local area - bilinear resampling (with cache) +--------------------------------------------- ++------------+---------+--------+--------+--------+-------+--------+ +| Dask Array | Dask | Time | Avg | Max | Avg | Errors | +| Chunk Size | Num | (s) | Memory | Memory | CPU | | +| (MB) | Workers | | (GB) | (GB) | (%) | | ++------------+---------+--------+--------+--------+-------+--------+ +| 16 | 8 | 196.78 | 12.65 | 37.36 | 7.81 | N/A | ++------------+---------+--------+--------+--------+-------+--------+ +| 16 | 12 | 141.56 | 12.06 | 39.92 | 10.35 | N/A | ++------------+---------+--------+--------+--------+-------+--------+ +| 16 | 16 | 144.00 | 12.74 | 41.58 | 10.75 | N/A | ++------------+---------+--------+--------+--------+-------+--------+ +| 32 | 8 | 144.10 | 12.26 | 38.74 | 10.05 | N/A | ++------------+---------+--------+--------+--------+-------+--------+ +| 32 | 12 | 146.39 | 13.09 | 42.94 | 10.70 | N/A | ++------------+---------+--------+--------+--------+-------+--------+ +| 32 | 16 | 150.53 | 13.81 | 39.60 | 10.68 | N/A | ++------------+---------+--------+--------+--------+-------+--------+ +| 64 | 8 | 152.96 | 12.99 | 40.93 | 10.45 | N/A | ++------------+---------+--------+--------+--------+-------+--------+ +| 64 | 12 | 163.94 | 13.73 | 41.10 | 9.97 | N/A | ++------------+---------+--------+--------+--------+-------+--------+ +| 64 | 16 | 151.42 | 14.65 | 42.42 | 11.62 | N/A | ++------------+---------+--------+--------+--------+-------+--------+ +| 96 | 8 | 219.15 | 14.22 | 41.90 | 8.11 | N/A | ++------------+---------+--------+--------+--------+-------+--------+ +| 96 | 12 | 182.07 | 15.32 | 41.61 | 10.12 | N/A | ++------------+---------+--------+--------+--------+-------+--------+ +| 96 | 16 | 195.08 | 15.85 | 41.35 | 10.42 | N/A | ++------------+---------+--------+--------+--------+-------+--------+ +| 128 | 8 | 174.62 | 14.69 | 39.29 | 9.95 | N/A | ++------------+---------+--------+--------+--------+-------+--------+ +| 128 | 12 | 214.65 | 16.17 | 40.08 | 9.53 | N/A | ++------------+---------+--------+--------+--------+-------+--------+ +| 128 | 16 | 198.26 | 15.97 | 46.53 | 9.62 | N/A | ++------------+---------+--------+--------+--------+-------+--------+ From 3192e319c494c9e4971f2c0817852c5010c3d417 Mon Sep 17 00:00:00 2001 From: yukaribbba Date: Mon, 1 Jul 2024 00:29:47 +0800 Subject: [PATCH 06/33] Update index.rst --- doc/source/performance_tests/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/performance_tests/index.rst b/doc/source/performance_tests/index.rst index e3f9163a9f..b969375adf 100644 --- a/doc/source/performance_tests/index.rst +++ b/doc/source/performance_tests/index.rst @@ -48,7 +48,7 @@ Conda environment Test procedure -------------- -- Each round will go through 5 scenes. +- Each round will go through 5 scenes to calculate average. - The composite will usually be the default ``true_color`` which requires heavy computation like atmospheric corrections. From 5e4a344d1344561a45f3fbe489da884722552500 Mon Sep 17 00:00:00 2001 From: yukaribbba Date: Mon, 1 Jul 2024 00:30:39 +0800 Subject: [PATCH 07/33] Update index.rst --- doc/source/performance_tests/index.rst | 34 +++++++++++++------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/doc/source/performance_tests/index.rst b/doc/source/performance_tests/index.rst index b969375adf..9faf88b352 100644 --- a/doc/source/performance_tests/index.rst +++ b/doc/source/performance_tests/index.rst @@ -27,23 +27,23 @@ Test platform Conda environment ----------------- -+------------+--------------------------------------+ -| Channel | conda-forge | -+------------+--------------------------------------+ -| Python | 3.12.3 | -+------------+--------------------------------------+ -| dask | 2024.6.2 | -+------------+--------------------------------------+ -| numpy | 2.0.0 | -+------------+--------------------------------------+ -| satpy | 0.49 | -+------------+--------------------------------------+ -| pyresample | 1.28.3 | -+------------+--------------------------------------+ -| pyspectral | 0.13.1 | -+------------+--------------------------------------+ -| psutil | 6.0.0 | -+------------+--------------------------------------+ ++------------+-------------+ +| Channel | conda-forge | ++------------+-------------+ +| Python | 3.12.3 | ++------------+-------------+ +| dask | 2024.6.2 | ++------------+-------------+ +| numpy | 2.0.0 | ++------------+-------------+ +| satpy | 0.49 | ++------------+-------------+ +| pyresample | 1.28.3 | ++------------+-------------+ +| pyspectral | 0.13.1 | ++------------+-------------+ +| psutil | 6.0.0 | ++------------+-------------+ Test procedure From 1b942929f24e85d43ee5f4c714e5e4b33ef79621 Mon Sep 17 00:00:00 2001 From: yukaribbba Date: Mon, 1 Jul 2024 23:54:14 +0800 Subject: [PATCH 08/33] Update abi_l1b_tests.rst --- doc/source/performance_tests/abi_l1b_tests.rst | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/doc/source/performance_tests/abi_l1b_tests.rst b/doc/source/performance_tests/abi_l1b_tests.rst index 85631f0f2a..232d7375cf 100644 --- a/doc/source/performance_tests/abi_l1b_tests.rst +++ b/doc/source/performance_tests/abi_l1b_tests.rst @@ -1,11 +1,25 @@ ================= Tests for abi_l1b ================= ++----------+----------------------------------------------------------------------------------+ +| Datasets | 5 scenes by GOES-16 around solar noon (scan period: UTC 17:00 - 17:10) | +| | from 2024.06.17 to 2024.06.21 | ++----------+----------------------------------------------------------------------------------+ +| Area 1 | - 500m full disk (``scn.finest_area()``) | +| | - ``native`` resampling | ++----------+----------------------------------------------------------------------------------+ +| Area 2 | - 500m local area | +| | - width: 8008, height: 8008 | +| | - projection: +proj=lcc +lon_0=-96 +lat_1=20 +lat_2=60 +datum=WGS84 +ellps=WGS84 | +| | - area extent: (-106000, 2635000, 3898000, 6639000) | +| | - ``nearest`` and ``bilinear`` resampling | ++----------+----------------------------------------------------------------------------------+ Recommended Settings ==================== - +- ``DASK_ARRAY__CHUNK_SIZE``: **16MiB** +- ``DASK_NUM_WORKERS``: **8/12/16** are all worth considering Result Table ============ From 17a0aa50b744be2178a7f6601c73fc683e4f9be7 Mon Sep 17 00:00:00 2001 From: yukaribbba Date: Tue, 2 Jul 2024 00:14:45 +0800 Subject: [PATCH 09/33] Update abi_l1b_tests.rst --- .../performance_tests/abi_l1b_tests.rst | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/doc/source/performance_tests/abi_l1b_tests.rst b/doc/source/performance_tests/abi_l1b_tests.rst index 232d7375cf..0e1b12d6d7 100644 --- a/doc/source/performance_tests/abi_l1b_tests.rst +++ b/doc/source/performance_tests/abi_l1b_tests.rst @@ -1,19 +1,18 @@ ================= Tests for abi_l1b ================= -+----------+----------------------------------------------------------------------------------+ -| Datasets | 5 scenes by GOES-16 around solar noon (scan period: UTC 17:00 - 17:10) | -| | from 2024.06.17 to 2024.06.21 | -+----------+----------------------------------------------------------------------------------+ -| Area 1 | - 500m full disk (``scn.finest_area()``) | -| | - ``native`` resampling | -+----------+----------------------------------------------------------------------------------+ -| Area 2 | - 500m local area | -| | - width: 8008, height: 8008 | -| | - projection: +proj=lcc +lon_0=-96 +lat_1=20 +lat_2=60 +datum=WGS84 +ellps=WGS84 | -| | - area extent: (-106000, 2635000, 3898000, 6639000) | -| | - ``nearest`` and ``bilinear`` resampling | -+----------+----------------------------------------------------------------------------------+ +- Datasets: 5 scenes by GOES-16 around solar noon (scan period: UTC 17:00 - 17:10) from 2024.06.17 to 2024.06.21 +- Area and resampling: + ++-------------+------------+----------------------------------------------------------------------------------+--------------------------+ +| Description | Resolution | Area Definition | Resampler | ++=============+============+==================================================================================+==========================+ +| Full Disk | 500m | ``scn.finest_area()`` | ``native`` | ++-------------+------------+----------------------------------------------------------------------------------+--------------------------+ +| Local | 500m | - width: 8008, height: 8008 | ``nearest``/``bilinear`` | +| | | - projection: +proj=lcc +lon_0=-96 +lat_1=20 +lat_2=60 +datum=WGS84 +ellps=WGS84 | | +| | | - area extent: (-106000, 2635000, 3898000, 6639000) | | ++-------------+------------+----------------------------------------------------------------------------------+--------------------------+ Recommended Settings @@ -21,6 +20,7 @@ Recommended Settings - ``DASK_ARRAY__CHUNK_SIZE``: **16MiB** - ``DASK_NUM_WORKERS``: **8/12/16** are all worth considering + Result Table ============ @@ -30,7 +30,7 @@ Fulldisk - native resampling | Dask Array | Dask | Time | Avg | Max | Avg | Errors | | Chunk Size | Num | (s) | Memory | Memory | CPU | | | (MB) | Workers | | (GB) | (GB) | (%) | | -+------------+---------+--------+--------+--------+-------+--------+ ++============+=========+========+========+========+=======+========+ | 16 | 8 | 151.01 | 4.96 | 9.36 | 59.54 | N/A | +------------+---------+--------+--------+--------+-------+--------+ | 16 | 12 | 151.22 | 6.33 | 11.96 | 69.05 | N/A | From 2ef1e145a886255718ac40264f4956eeb54241cf Mon Sep 17 00:00:00 2001 From: yukaribbba Date: Sat, 6 Jul 2024 23:34:23 +0800 Subject: [PATCH 10/33] Create performance_test.py --- benchmarks/performance_test.py | 484 +++++++++++++++++++++++++++++++++ 1 file changed, 484 insertions(+) create mode 100644 benchmarks/performance_test.py diff --git a/benchmarks/performance_test.py b/benchmarks/performance_test.py new file mode 100644 index 0000000000..2b279035bd --- /dev/null +++ b/benchmarks/performance_test.py @@ -0,0 +1,484 @@ +# Copyright (c) 2015-2024 Satpy developers +# +# This file is part of satpy. +# +# satpy is free software: you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation, either version 3 of the License, or (at your option) any later +# version. +# +# satpy is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with +# satpy. If not, see . +"""Base class for performance test.""" +import csv +import glob +import os +import platform +import time +from datetime import datetime, UTC +from io import BytesIO +from itertools import zip_longest +from threading import Thread + +import cpuinfo +import numpy as np +import pandas as pd +import psutil +import matplotlib.pyplot as plt + + +class SatpyPerformanceTest: + """Test satpy performance by looping through conditions involving ``dask_array_chunk_size``and ``dask_num_workers``. + There are two tests: ``simple_test`` and ``resampler_test`` + + """ + def __init__(self, work_dir, folder_pattern, reader_name, composite, chunk_size_opts, worker_opts, + reader_kwargs=None): + """Initialize SatpyPerformanceTest with some basic arguments. + + Args: + work_dir (str): Absolute path to the directory that contains all your dataset folders. + folder_pattern (str): Naming scheme of the dataset folders, e.g. `G16_s*_e*_FLDK`. + reader_name (str): Reader you want to test. + composite (str): Composite for test. Usually this could be ``true_color`` which involves a + lot of computation like atmospheric correction. + chunk_size_opts (list): All the ``dask_array_chunk_size`` values you wish for, in `MiB`. + worker_opts (list): All the ``dask_num_workers`` values you wish for. + reader_kwargs (dict): Additional reader arguments for ``Scene``, + like `{'mask_saturated': False}` in modis_l1b. + + """ + super().__init__() + self.work_dir = work_dir + self.folder_pattern = folder_pattern + self.reader_name = reader_name + self.reader_kwargs = reader_kwargs + self.composite = composite + + self.folders = glob.glob(f"{self.work_dir}/{self.folder_pattern}") + + self.chunk_size_opts = chunk_size_opts + self.worker_opts = worker_opts + self.total_rounds = len(self.chunk_size_opts) * len(self.worker_opts) + + self.result = {} + self.running = True + + def monitor_system_usage(self, interval=0.5): + """Use psutil to record CPU and memory usage. Default sample rate is 0.5s.""" + os_type = platform.system() + + process = psutil.Process() + cpu_usage = [] + memory_usage = [] + timestamps = [] + + start_time = time.time() + while self.running: + cpu_usage.append(process.cpu_percent()) + if os_type == "Windows": + # In Windows, "vms" means "pagefile" + memory_usage.append((process.memory_full_info().rss + process.memory_full_info().vms)) + elif os_type == "Linux": + memory_usage.append((process.memory_full_info().rss + process.memory_full_info().swap)) + else: + memory_usage.append(process.memory_full_info().rss) + timestamps.append(time.time() - start_time) + time.sleep(interval) + + self.result["cpu_usage"] = cpu_usage + self.result["memory_usage"] = memory_usage + self.result["timestamps"] = timestamps + + def write_to_csv(self, file_name): + """Write the result of each round to a csv file.""" + with open(file_name, "w", newline="", encoding="utf-8") as csvfile: + csvwriter = csv.writer(csvfile) + csvwriter.writerow(["Timestamp (s)", "CPU Usage (%)", "Memory Usage (Byte)", "Process Time", "Scenes", + "Errors"]) + for ts, cpu, mem, pt, scn, er in zip_longest(self.result["timestamps"], self.result["cpu_usage"], + self.result["memory_usage"], self.result["process_time"], + self.result["scenes"], self.result["errors"], fillvalue="N/A"): + csvwriter.writerow([ts, cpu, mem, pt, scn, er]) + + def satpy_test(self, resampler, diff_res=False, area_def=None, resampler_kwargs=None): + """Call satpy to do the test.""" + from satpy import find_files_and_readers, Scene + + reader_kwargs = {} if self.reader_kwargs is None else self.reader_kwargs + resampler_kwargs = {} if resampler_kwargs is None else resampler_kwargs + for folder in self.folders: + files = find_files_and_readers(base_dir=folder, reader=self.reader_name) + scn = Scene(filenames=files, reader_kwargs=reader_kwargs) + scn.load([self.composite], generate=False if diff_res else True) + + if resampler == "none": + scn2 = scn + else: + scn2 = scn.resample(area_def, resampler=resampler, **resampler_kwargs) + + scn2.save_dataset(self.composite, writer="geotiff", filename="test.tif", base_dir=self.work_dir, + fill_value=0, compress=None) + + def single_loop(self, conditions, area, diff_res=False, area_def=None, resampler_kwargs=None): + """Single round of the test. """ + import dask.config + self.running = True + + chunk_size, num_worker, resampler = conditions + + dask.config.set({"array.chunk-size": f"{chunk_size}MiB"}) + dask.config.set(num_workers=num_worker) + + try: + num_thread = os.environ["OMP_NUM_THREADS"] + except KeyError: + num_thread = psutil.cpu_count(logical=True) + + # Start recording cpu/mem usage + monitor_thread = Thread(target=self.monitor_system_usage, args=(0.5,)) + monitor_thread.start() + + errors = [] + start = time.perf_counter() + try: + self.satpy_test(resampler, diff_res, area_def, resampler_kwargs) + except Exception as e: + errors.append(e) + + end = time.perf_counter() + # All of these must be list object + self.result["process_time"] = [end - start] + self.result["scenes"] = [len(self.folders)] + self.result["errors"] = errors + + # Stop recording + self.running = False + monitor_thread.join() + + csv_file = (f"{self.work_dir}/{self.reader_name.replace("_", "")}_" + f"chunk{chunk_size}_worker{num_worker}_thread{num_thread}_{area}_{resampler}.csv") + self.write_to_csv(csv_file) + + def simple_test(self, diff_res=False): + """Test in dataset's original projection. No resampling or the simplest ``native`` resampling. + + Args: + diff_res (bool): If the composite requires bands in different resolutions, this should be set to True + to let the native resampler match them to the ``scn.finest_area()``. + For example, ``true_color`` of ABI needs 500m C01 and 1000m C02 bands, so it's `True`. + This is not a test option and should be set properly according to the composite, + otherwise the test will end up in errors. + + """ + resampler = "native" if diff_res else "none" + area = "original" + + i = 0 + for chunk_size in self.chunk_size_opts: + for num_worker in self.worker_opts: + print(f"Start testing CHUNK_SIZE={chunk_size}MiB, NUM_WORKER={num_worker}, resampler is {resampler}.") + self.single_loop((chunk_size, num_worker, resampler), area, diff_res) + i = i + 1 + + if i == self.total_rounds: + print("All the tests finished. Generating HTML report.") + html_report(self.work_dir, self.reader_name) + else: + print(f"ROUND {i} / {self.total_rounds} Completed. Now take a 1-min rest.") + time.sleep(60) + + def resampler_test(self, resamplers, area_def, resampler_kwargs=None): + """Test involving resampling. See https://satpy.readthedocs.io/en/latest/resample.html#resampling-algorithms + for available resampler. + + Args: + resamplers (list): List of resampling algorithms you want to test. + area_def (AreaDefinition or DynamicAreaDefinition or str): Area definition or the name of an area stored + in YAML. + resampler_kwargs (dict): Additional arguments passed to the resampler, e.g. + {'cache_dir': '/path/to/my/cache'} for ``bilinear`` or ``nearest``. + + """ + resampler_kwargs = {} if resampler_kwargs is None else resampler_kwargs + area = "local" if len(area_def.area_id) == 0 else area_def.area_id.replace("_", "") + + i = 0 + for chunk_size in self.chunk_size_opts: + for num_worker in self.worker_opts: + for resampler in resamplers: + print( + f"Start testing CHUNK_SIZE={chunk_size}MiB, NUM_WORKER={num_worker}, resampler is {resampler}.") + self.single_loop((chunk_size, num_worker, resampler), area, area_def, resampler_kwargs) + i = i + 1 + + if i == self.total_rounds: + print("All the tests finished. Generating HTML report.") + html_report(self.work_dir, self.reader_name) + else: + print(f"ROUND {i} / {self.total_rounds} Completed. Now take a 1-min rest.") + time.sleep(60) + + +def process_csv(cvs_file): + """Process result csv and return a dataframe.""" + # Extract information from the filename + filename = os.path.basename(cvs_file) + filename = filename.split(".")[0] + filename_parts = filename.split("_") + dask_array_chunk_size = int(filename_parts[1].replace("chunk", "")) + dask_num_workers = int(filename_parts[2].replace("worker", "")) + omp_num_threads = int(filename_parts[3].replace("thread", "")) + area = filename_parts[4] + resampling_alg = filename_parts[5] + + data = pd.read_csv(cvs_file, keep_default_na=False) + scenes = int(data.loc[0, "Scenes"]) + cpu_thread = psutil.cpu_count(logical=True) + + # Prepare the row dictionary for the new CSV based on filename + new_row = { + "Dask Array Chunk Size (MB)": dask_array_chunk_size, + "Dask Num Workers": dask_num_workers, + "Omp Num Threads": omp_num_threads, + "Area": area, + "Resampling Algorithm": resampling_alg, + "Time (s)": round(float(data.loc[0, "Process Time"]) / scenes, 2), + "Avg Memory (GB)": round(data["Memory Usage (Byte)"].mean() / (1024 ** 3), 2), + "Max Memory (GB)": round(data["Memory Usage (Byte)"].max() / (1024 ** 3), 2), + "Avg CPU (%)": round(data["CPU Usage (%)"].mean() / cpu_thread, 2), + "Scenes": scenes, + "Errors": data.loc[0, "Errors"], + } + + df = pd.DataFrame([new_row]) + + return df + + +def combined_csv(work_dir, reader_name): + """Collect all the csv files under work_dir and merge them in to one dataframe.""" + all_dataframes = [] + csvs = glob.glob(f"{work_dir}/{reader_name.replace("_", "")}_chunk*_worker*_thread*_*_*.csv") + for file in csvs: + df = process_csv(file) + all_dataframes.append(df) + + if not all_dataframes: + return + + combined_df = pd.concat(all_dataframes, ignore_index=True) + + # Sort the DataFrame + # Make sure "original" area always comes first + combined_df["sort_priority"] = np.where(combined_df["Area"].str.contains("original"), 0, 1) + sorted_df = combined_df.sort_values(by=["sort_priority", "Area", "Resampling Algorithm", + "Dask Array Chunk Size (MB)", "Dask Num Workers", "Omp Num Threads"]) + + sorted_df.reset_index(drop=True, inplace=True) + + return sorted_df + + +def draw_hbar(dataframe, colors, title, key_x, key_y): + """Plot the bar chart by matplotlib.""" + dpi = 100 + fig_width = 1080 / dpi + num_bars = len(dataframe) + # Dynamic height + fig_height = max(600, 100 + 50 * num_bars) / dpi + fig, ax = plt.subplots(figsize=(fig_width, fig_height), dpi=dpi) + plt.subplots_adjust(left=0.15, right=0.85, top=0.9, bottom=0.1) + + dataframe.plot.barh(x=key_x, y=key_y, legend=True if isinstance(key_y, list) else False, + ax=ax, width=0.5, color=colors, stacked=True if isinstance(key_y, list) else False) + plt.title(title, fontsize=16) + plt.ylabel(key_x, fontsize=14) + plt.xlabel("Memory (GB)" if isinstance(key_y, list) else key_y, fontsize=14) + ax.tick_params(axis="both", labelsize=12) + if isinstance(key_y, list): + ax.legend(loc="upper right") + # Mark the position of physical memory limit + physical_memory = psutil.virtual_memory().total // (1024 ** 3) + ax.axvline(x=physical_memory, color="#808080", linestyle="--") + ax.text(physical_memory + 0.5, 1, "Physical\nMemory\nLimit", color="#808080") + if "CPU" in ax.get_xlabel(): + ax.set_xlim([0, 100]) + + # Data label right to the bar + cumulative_widths = [0] * len(dataframe) + for i, container in enumerate(ax.containers): + for j, bar in enumerate(container): + width = bar.get_width() + cumulative_widths[j] = cumulative_widths[j] + width if isinstance(key_y, list) else width + label_x_pos = cumulative_widths[j] + 0.3 + + if i == 0: + # For "Time", "CPU" and "Avg Memory" + label_text = str(round(width, 2)) + else: + # For "Max Memory" + # Because in the dataframe for graph it's actually the difference between Max and Avg + # so that we can draw the "stacked" bars correctly. + # Now we have to restore the value to the real Max when writing the label. + label_text = str(round(cumulative_widths[j], 2)) + + ax.text(label_x_pos, bar.get_y() + bar.get_height() / 2, label_text, va='center') + + svg = BytesIO() + plt.savefig(svg, format="svg") + svg = svg.getvalue().decode("utf-8") + plt.close() + + return svg + + +def html_report(work_dir, reader_name): + """Analyze the summary dataframe and produce an HTML report.""" + import dask + import xarray as xr + import satpy + import pyresample + import pyspectral + + # Get system info + cpu_core = psutil.cpu_count(logical=False) + cpu_thread = psutil.cpu_count(logical=True) + cpu_info = cpuinfo.get_cpu_info() + cpu_model = cpu_info["brand_raw"] + memory_info = psutil.virtual_memory().total // (1024 ** 3) + os_info = platform.platform() + + # Get Python env + python_version = platform.python_version() + numpy_version = np.__version__ + dask_version = dask.__version__ + xarray_version = xr.__version__ + satpy_version = satpy.__version__ + pyresample_version = pyresample.__version__ + pyspectral_version = pyspectral.__version__ + psutil_version = psutil.__version__ + + df = combined_csv(work_dir, reader_name) + if df is None: + print("Test CSV result not found! Or its filename doesn't fit [*_chunk*_worker*_thread*_*_*.csv]") + return + # Group the dataframe for report + df["Group"] = "Area: " + df["Area"] + " - " + "Resampler: " + df["Resampling Algorithm"] + groups = df["Group"].unique() + + # HTML head + html_content = f""" + + + + + Satpy Performance Test Report for {reader_name} + + + + +

Satpy Performance Test Report for {reader_name}

+

Generation Date: UTC {datetime.now(UTC).strftime("%Y-%m-%d %H:%M:%S")}

+

1. System Info

+

1.1 Platform

+

CPU: {cpu_model}, {cpu_core} cores / {cpu_thread} threads in total

+

Physical Memory: {memory_info} GB

+

OS: {os_info}

+

1.2 Python Environment

+

Python: {python_version}

+

Numpy: {numpy_version}

+

Dask: {dask_version}

+

Xarray: {xarray_version}

+

Satpy: {satpy_version}

+

Pyresample: {pyresample_version}

+

Pyspectral: {pyspectral_version}

+

Psutil: {psutil_version}

+

2. Test Results

+ """ + + figures = {"Process Time (single scene average)": {"key_y": "Time (s)", "colors": ["#4E79A7"]}, + "Average CPU Usage": {"key_y": "Avg CPU (%)", "colors": ["#F28E2B"]}, + "Memory Usage": {"key_y": ["Avg Memory (GB)", "Max Memory (GB)"], "colors": ["#59A14F", "#EDC948"]}} + + for group in groups: + group_df = df[df["Group"] == group] + # Drop unnecessary column + group_df_table = group_df.drop(["Group", "Area", "Resampling Algorithm", "sort_priority"], + axis=1, inplace=False) + + group_df_graph = group_df.copy() + # Build a new column containing value group to make it easier for plotting the chart + group_df_graph["Chunk size - Num workers - Num Threads"] = ( + group_df_graph["Dask Array Chunk Size (MB)"].astype(str) + " - " + + group_df_graph["Dask Num Workers"].astype(str) + " - " + + group_df_graph["Omp Num Threads"].astype(str)) + group_df_graph = group_df_graph.sort_values(by=["Dask Array Chunk Size (MB)", "Dask Num Workers", + "Omp Num Threads"], ascending=False) + # For stacked bar + group_df_graph["Max Memory (GB)"] = group_df_graph["Max Memory (GB)"] - group_df_graph["Avg Memory (GB)"] + # Replace all the error rows with 0 so the chart will be significant in these rows + group_df_graph.loc[group_df_graph["Errors"] != "N/A", ["Time (s)", "Avg CPU (%)", + "Avg Memory (GB)", "Max Memory (GB)"]] = 0 + + group_html = group_df_table.to_html(index=False) + html_content += f""" +

2.{groups.tolist().index(group) + 1} {group}

+

2.{groups.tolist().index(group) + 1}.1 Table

+ {group_html} +

2.{groups.tolist().index(group) + 1}.2 Charts

+ """ + + # Plot three charts: time, cpu and mem + for figure in figures.keys(): + svg_bar = draw_hbar(group_df_graph, figures[figure]["colors"], figure, + "Chunk size - Num workers - Num Threads", figures[figure]["key_y"]) + html_content += f""" +
+ {svg_bar} +
+ """ + + # Finish HTML report + html_content += """ + + + """ + + # Save it to disk + with open(f"{work_dir}/satpy_performance_report.html", "w", encoding="utf-8") as f: + f.write(html_content) \ No newline at end of file From 5aa6fa854402d44191ee05c8f32d4a866e674114 Mon Sep 17 00:00:00 2001 From: yukaribbba Date: Sun, 7 Jul 2024 09:26:14 +0800 Subject: [PATCH 11/33] Update performance_test.py --- benchmarks/performance_test.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/benchmarks/performance_test.py b/benchmarks/performance_test.py index 2b279035bd..d678b136a1 100644 --- a/benchmarks/performance_test.py +++ b/benchmarks/performance_test.py @@ -49,7 +49,7 @@ def __init__(self, work_dir, folder_pattern, reader_name, composite, chunk_size_ chunk_size_opts (list): All the ``dask_array_chunk_size`` values you wish for, in `MiB`. worker_opts (list): All the ``dask_num_workers`` values you wish for. reader_kwargs (dict): Additional reader arguments for ``Scene``, - like `{'mask_saturated': False}` in modis_l1b. + e.g. `{'mask_saturated': False}` in modis_l1b. """ super().__init__() @@ -165,11 +165,11 @@ def single_loop(self, conditions, area, diff_res=False, area_def=None, resampler self.write_to_csv(csv_file) def simple_test(self, diff_res=False): - """Test in dataset's original projection. No resampling or the simplest ``native`` resampling. + """Test readers in dataset's original projection. No resampling involved or the simplest ``native`` resampling. Args: diff_res (bool): If the composite requires bands in different resolutions, this should be set to True - to let the native resampler match them to the ``scn.finest_area()``. + so the native resampler will match them to the ``scn.finest_area()``. For example, ``true_color`` of ABI needs 500m C01 and 1000m C02 bands, so it's `True`. This is not a test option and should be set properly according to the composite, otherwise the test will end up in errors. @@ -181,19 +181,19 @@ def simple_test(self, diff_res=False): i = 0 for chunk_size in self.chunk_size_opts: for num_worker in self.worker_opts: - print(f"Start testing CHUNK_SIZE={chunk_size}MiB, NUM_WORKER={num_worker}, resampler is {resampler}.") + print(f"Start testing CHUNK_SIZE={chunk_size}MiB, NUM_WORKER={num_worker}, resampler is {resampler}.") # noqa self.single_loop((chunk_size, num_worker, resampler), area, diff_res) i = i + 1 if i == self.total_rounds: - print("All the tests finished. Generating HTML report.") + print("All the tests finished. Generating HTML report.") # noqa html_report(self.work_dir, self.reader_name) else: - print(f"ROUND {i} / {self.total_rounds} Completed. Now take a 1-min rest.") + print(f"ROUND {i} / {self.total_rounds} Completed. Now take a 1-min rest.") # noqa time.sleep(60) def resampler_test(self, resamplers, area_def, resampler_kwargs=None): - """Test involving resampling. See https://satpy.readthedocs.io/en/latest/resample.html#resampling-algorithms + """Test readers with resampling. See https://satpy.readthedocs.io/en/latest/resample.html#resampling-algorithms for available resampler. Args: @@ -212,15 +212,15 @@ def resampler_test(self, resamplers, area_def, resampler_kwargs=None): for num_worker in self.worker_opts: for resampler in resamplers: print( - f"Start testing CHUNK_SIZE={chunk_size}MiB, NUM_WORKER={num_worker}, resampler is {resampler}.") + f"Start testing CHUNK_SIZE={chunk_size}MiB, NUM_WORKER={num_worker}, resampler is {resampler}.") # noqa self.single_loop((chunk_size, num_worker, resampler), area, area_def, resampler_kwargs) i = i + 1 if i == self.total_rounds: - print("All the tests finished. Generating HTML report.") + print("All the tests finished. Generating HTML report.") # noqa html_report(self.work_dir, self.reader_name) else: - print(f"ROUND {i} / {self.total_rounds} Completed. Now take a 1-min rest.") + print(f"ROUND {i} / {self.total_rounds} Completed. Now take a 1-min rest.") # noqa time.sleep(60) From 570c77b7c60d4d02af00ce13733eb12019abe2c7 Mon Sep 17 00:00:00 2001 From: yukaribbba Date: Sun, 7 Jul 2024 09:36:28 +0800 Subject: [PATCH 12/33] Update performance_test.py --- benchmarks/performance_test.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/benchmarks/performance_test.py b/benchmarks/performance_test.py index d678b136a1..98df31857e 100644 --- a/benchmarks/performance_test.py +++ b/benchmarks/performance_test.py @@ -124,7 +124,7 @@ def satpy_test(self, resampler, diff_res=False, area_def=None, resampler_kwargs= scn2.save_dataset(self.composite, writer="geotiff", filename="test.tif", base_dir=self.work_dir, fill_value=0, compress=None) - def single_loop(self, conditions, area, diff_res=False, area_def=None, resampler_kwargs=None): + def single_loop(self, conditions, diff_res=False, area_def=None, resampler_kwargs=None): """Single round of the test. """ import dask.config self.running = True @@ -160,6 +160,11 @@ def single_loop(self, conditions, area, diff_res=False, area_def=None, resampler self.running = False monitor_thread.join() + if area_def is None: + area = "original" + else: + area = "local" if len(area_def.area_id) == 0 else area_def.area_id.replace("_", "") + csv_file = (f"{self.work_dir}/{self.reader_name.replace("_", "")}_" f"chunk{chunk_size}_worker{num_worker}_thread{num_thread}_{area}_{resampler}.csv") self.write_to_csv(csv_file) @@ -176,13 +181,12 @@ def simple_test(self, diff_res=False): """ resampler = "native" if diff_res else "none" - area = "original" i = 0 for chunk_size in self.chunk_size_opts: for num_worker in self.worker_opts: print(f"Start testing CHUNK_SIZE={chunk_size}MiB, NUM_WORKER={num_worker}, resampler is {resampler}.") # noqa - self.single_loop((chunk_size, num_worker, resampler), area, diff_res) + self.single_loop((chunk_size, num_worker, resampler), diff_res) i = i + 1 if i == self.total_rounds: @@ -205,7 +209,6 @@ def resampler_test(self, resamplers, area_def, resampler_kwargs=None): """ resampler_kwargs = {} if resampler_kwargs is None else resampler_kwargs - area = "local" if len(area_def.area_id) == 0 else area_def.area_id.replace("_", "") i = 0 for chunk_size in self.chunk_size_opts: @@ -213,7 +216,7 @@ def resampler_test(self, resamplers, area_def, resampler_kwargs=None): for resampler in resamplers: print( f"Start testing CHUNK_SIZE={chunk_size}MiB, NUM_WORKER={num_worker}, resampler is {resampler}.") # noqa - self.single_loop((chunk_size, num_worker, resampler), area, area_def, resampler_kwargs) + self.single_loop((chunk_size, num_worker, resampler), area_def, resampler_kwargs) i = i + 1 if i == self.total_rounds: From c643997f1c680ccc3e5e84670feb61a52155e976 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 7 Jul 2024 01:37:51 +0000 Subject: [PATCH 13/33] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- benchmarks/performance_test.py | 15 ++++++++------- doc/source/index.rst | 2 +- doc/source/performance_tests/abi_l1b_tests.rst | 6 +++--- doc/source/performance_tests/index.rst | 2 +- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/benchmarks/performance_test.py b/benchmarks/performance_test.py index 98df31857e..52736ca4c8 100644 --- a/benchmarks/performance_test.py +++ b/benchmarks/performance_test.py @@ -19,16 +19,16 @@ import os import platform import time -from datetime import datetime, UTC +from datetime import UTC, datetime from io import BytesIO from itertools import zip_longest from threading import Thread import cpuinfo +import matplotlib.pyplot as plt import numpy as np import pandas as pd import psutil -import matplotlib.pyplot as plt class SatpyPerformanceTest: @@ -107,7 +107,7 @@ def write_to_csv(self, file_name): def satpy_test(self, resampler, diff_res=False, area_def=None, resampler_kwargs=None): """Call satpy to do the test.""" - from satpy import find_files_and_readers, Scene + from satpy import Scene, find_files_and_readers reader_kwargs = {} if self.reader_kwargs is None else self.reader_kwargs resampler_kwargs = {} if resampler_kwargs is None else resampler_kwargs @@ -343,10 +343,11 @@ def draw_hbar(dataframe, colors, title, key_x, key_y): def html_report(work_dir, reader_name): """Analyze the summary dataframe and produce an HTML report.""" import dask - import xarray as xr - import satpy import pyresample import pyspectral + import xarray as xr + + import satpy # Get system info cpu_core = psutil.cpu_count(logical=False) @@ -403,7 +404,7 @@ def html_report(work_dir, reader_name): background-color: #f2f2f2; }} tr:nth-child(even) {{background-color: #f9f9f9}} - tr:hover {{background-color: #f1f1f1}} + tr:hover {{background-color: #f1f1f1}} + + + +

Satpy Performance Test Report for {reader_name}

+

Generation Date: UTC {datetime.now(UTC).strftime("%Y-%m-%d %H:%M:%S")}

+

1. System Info

+

1.1 Platform

+

CPU: {cpu_model}, {cpu_core} cores / {cpu_thread} threads in total

+

Physical Memory: {memory_info} GB

+

OS: {os_info}

+

1.2 Python Environment

+

Python: {python_version}

+

Numpy: {numpy_version}

+

Dask: {dask_version}

+

Xarray: {xarray_version}

+

Satpy: {satpy_version}

+

Pyresample: {pyresample_version}

+

Pyspectral: {pyspectral_version}

+

Psutil: {psutil_version}

+

2. Test Results

+ """ + + return html_content + + +def html_report(work_dir, reader_name): + """Analyze the summary dataframe and produce an HTML report.""" df = combined_csv(work_dir, reader_name) if df is None: - print("Test CSV result not found! Or its filename doesn't fit [*_chunk*_worker*_thread*_*_*.csv]") + print("Test CSV result not found! Or its filename doesn't fit [*_chunk*_worker*_thread*_*_*.csv]") # noqa return # Group the dataframe for report df["Group"] = "Area: " + df["Area"] + " - " + "Resampler: " + df["Resampling Algorithm"] groups = df["Group"].unique() - # HTML head - html_content = f""" - - - - - Satpy Performance Test Report for {reader_name} - - - - -

Satpy Performance Test Report for {reader_name}

-

Generation Date: UTC {datetime.now(UTC).strftime("%Y-%m-%d %H:%M:%S")}

-

1. System Info

-

1.1 Platform

-

CPU: {cpu_model}, {cpu_core} cores / {cpu_thread} threads in total

-

Physical Memory: {memory_info} GB

-

OS: {os_info}

-

1.2 Python Environment

-

Python: {python_version}

-

Numpy: {numpy_version}

-

Dask: {dask_version}

-

Xarray: {xarray_version}

-

Satpy: {satpy_version}

-

Pyresample: {pyresample_version}

-

Pyspectral: {pyspectral_version}

-

Psutil: {psutil_version}

-

2. Test Results

- """ - - figures = {"Process Time (single scene average)": {"key_y": "Time (s)", "colors": ["#4E79A7"]}, - "Average CPU Usage": {"key_y": "Avg CPU (%)", "colors": ["#F28E2B"]}, - "Memory Usage": {"key_y": ["Avg Memory (GB)", "Max Memory (GB)"], "colors": ["#59A14F", "#EDC948"]}} + html_content = html_head(reader_name) for group in groups: group_df = df[df["Group"] == group] @@ -467,12 +476,11 @@ def html_report(work_dir, reader_name): """ # Plot three charts: time, cpu and mem - for figure in figures.keys(): - svg_bar = draw_hbar(group_df_graph, figures[figure]["colors"], figure, - "Chunk size - Num workers - Num Threads", figures[figure]["key_y"]) + titles = ["Process Time (single scene average)", "Average CPU Usage", "Memory Usage"] + for title in titles: + svg_bar = draw_hbar(group_df_graph, title) html_content += f""" -
+
{svg_bar}
""" @@ -484,5 +492,5 @@ def html_report(work_dir, reader_name): """ # Save it to disk - with open(f"{work_dir}/satpy_performance_report.html", "w", encoding="utf-8") as f: + with open(f"{work_dir}/{reader_name}_test_report.html", "w", encoding="utf-8") as f: f.write(html_content) From 1ac633b758e203cef29611b41620eafac0723889 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 7 Jul 2024 03:37:37 +0000 Subject: [PATCH 15/33] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- benchmarks/performance_test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/benchmarks/performance_test.py b/benchmarks/performance_test.py index 63a605bb62..624e358c3d 100644 --- a/benchmarks/performance_test.py +++ b/benchmarks/performance_test.py @@ -354,6 +354,7 @@ def html_head(reader_name): import pyresample import pyspectral import xarray as xr + import satpy # Get system info From 2f46364356514c73c14aad12004c1d8ab3df7809 Mon Sep 17 00:00:00 2001 From: yukaribbba Date: Sun, 7 Jul 2024 11:41:48 +0800 Subject: [PATCH 16/33] Update performance_test.py --- benchmarks/performance_test.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/benchmarks/performance_test.py b/benchmarks/performance_test.py index 624e358c3d..a2739aef20 100644 --- a/benchmarks/performance_test.py +++ b/benchmarks/performance_test.py @@ -19,7 +19,7 @@ import os import platform import time -from datetime import UTC, datetime +from datetime import datetime, timezone from io import BytesIO from itertools import zip_longest from threading import Thread @@ -338,7 +338,7 @@ def draw_hbar(dataframe, title): # Now we have to restore the value to the real Max when writing the label. label_text = str(round(cumulative_widths[j], 2)) - ax.text(label_x_pos, bar.get_y() + bar.get_height() / 2, label_text, va='center') + ax.text(label_x_pos, bar.get_y() + bar.get_height() / 2, label_text, va="center") svg = BytesIO() plt.savefig(svg, format="svg") @@ -415,7 +415,7 @@ def html_head(reader_name):

Satpy Performance Test Report for {reader_name}

-

Generation Date: UTC {datetime.now(UTC).strftime("%Y-%m-%d %H:%M:%S")}

+

Generation Date: UTC {datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S")}

1. System Info

1.1 Platform

CPU: {cpu_model}, {cpu_core} cores / {cpu_thread} threads in total

From d2b256a25afa2f0f5a1814c2df76fb85e0f21f3e Mon Sep 17 00:00:00 2001 From: yukaribbba Date: Sun, 7 Jul 2024 11:45:28 +0800 Subject: [PATCH 17/33] Update performance_test.py --- benchmarks/performance_test.py | 38 ++++++++++++---------------------- 1 file changed, 13 insertions(+), 25 deletions(-) diff --git a/benchmarks/performance_test.py b/benchmarks/performance_test.py index a2739aef20..97f65adee0 100644 --- a/benchmarks/performance_test.py +++ b/benchmarks/performance_test.py @@ -382,35 +382,23 @@ def html_head(reader_name): Satpy Performance Test Report for {reader_name} From d611f6a8b3cb83f14a957afc6abb69d1cbe707a8 Mon Sep 17 00:00:00 2001 From: yukaribbba Date: Sun, 7 Jul 2024 14:58:40 +0800 Subject: [PATCH 18/33] Update performance_test.py --- benchmarks/performance_test.py | 45 ++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/benchmarks/performance_test.py b/benchmarks/performance_test.py index 97f65adee0..0e5c7838db 100644 --- a/benchmarks/performance_test.py +++ b/benchmarks/performance_test.py @@ -63,8 +63,7 @@ def __init__(self, work_dir, folder_pattern, reader_name, composite, chunk_size_ self.folders = glob.glob(f"{self.work_dir}/{self.folder_pattern}") self.chunk_size_opts = chunk_size_opts - self.worker_opts = worker_opts - self.total_rounds = len(self.chunk_size_opts) * len(self.worker_opts) + self.worker_opts = worker_opts self.result = {} self.running = True @@ -106,7 +105,7 @@ def write_to_csv(self, file_name): self.result["scenes"], self.result["errors"], fillvalue="N/A"): csvwriter.writerow([ts, cpu, mem, pt, scn, er]) - def satpy_test(self, resampler, diff_res=False, area_def=None, resampler_kwargs=None): + def satpy_test(self, resampler, generate=False, area_def=None, resampler_kwargs=None): """Call satpy to do the test.""" from satpy import Scene, find_files_and_readers @@ -115,7 +114,7 @@ def satpy_test(self, resampler, diff_res=False, area_def=None, resampler_kwargs= for folder in self.folders: files = find_files_and_readers(base_dir=folder, reader=self.reader_name) scn = Scene(filenames=files, reader_kwargs=reader_kwargs) - scn.load([self.composite], generate=False if diff_res else True) + scn.load([self.composite], generate=generate) if resampler == "none": scn2 = scn @@ -125,7 +124,7 @@ def satpy_test(self, resampler, diff_res=False, area_def=None, resampler_kwargs= scn2.save_dataset(self.composite, writer="geotiff", filename="test.tif", base_dir=self.work_dir, fill_value=0, compress=None) - def single_loop(self, conditions, diff_res=False, area_def=None, resampler_kwargs=None): + def single_loop(self, conditions, generate=False, area_def=None, resampler_kwargs=None): """Single round of the test.""" import dask.config self.running = True @@ -147,7 +146,7 @@ def single_loop(self, conditions, diff_res=False, area_def=None, resampler_kwarg errors = [] start = time.perf_counter() try: - self.satpy_test(resampler, diff_res, area_def, resampler_kwargs) + self.satpy_test(resampler, generate, area_def, resampler_kwargs) except Exception as e: errors.append(e) @@ -182,48 +181,62 @@ def simple_test(self, diff_res=False): """ resampler = "native" if diff_res else "none" + generate = not diff_res + total_rounds = len(self.chunk_size_opts) * len(self.worker_opts) i = 0 for chunk_size in self.chunk_size_opts: for num_worker in self.worker_opts: print(f"Start testing CHUNK_SIZE={chunk_size}MiB, NUM_WORKER={num_worker}, resampler is {resampler}.") # noqa - self.single_loop((chunk_size, num_worker, resampler), diff_res) + self.single_loop((chunk_size, num_worker, resampler), generate=generate) i = i + 1 - if i == self.total_rounds: - print(f"ROUND {i} / {self.total_rounds} Completed. Generating HTML report.") # noqa + if i == total_rounds: + print(f"ROUND {i} / {total_rounds} Completed. Generating HTML report.") # noqa html_report(self.work_dir, self.reader_name) else: - print(f"ROUND {i} / {self.total_rounds} Completed. Now take a 1-min rest.") # noqa + print(f"ROUND {i} / {total_rounds} Completed. Now take a 1-min rest.\n") # noqa time.sleep(60) def resampler_test(self, resamplers, area_def, resampler_kwargs=None): """Test readers with resampling. Args: - resamplers (list): List of resampling algorithms you want to test. + resamplers (list or str): A single resampling algorithm or a list of resampling algorithms you want to test. area_def (AreaDefinition or DynamicAreaDefinition or str): Area definition or the name of an area stored in YAML. - resampler_kwargs (dict): Additional arguments passed to the resampler, e.g. - {'cache_dir': '/path/to/my/cache'} for ``bilinear`` or ``nearest``. + resampler_kwargs (dict): Additional arguments passed to the resampler. You can specify the separate + kwargs for each resampler, e.g. + {'bilinear': {'cache_dir': '/path/to/my/cache'}, + 'ewa': {'weight_delta_max': 40, 'weight_distance_max': 2}}. + Or you can just give general kwargs like {'cache_dir': '/path/to/my/cache'} for + both ``nearest`` and ``bilinear``. """ + resamplers = [resamplers] if not isinstance(resamplers, list) else resamplers resampler_kwargs = {} if resampler_kwargs is None else resampler_kwargs + total_rounds = len(self.chunk_size_opts) * len(self.worker_opts) * len(resamplers) i = 0 for chunk_size in self.chunk_size_opts: for num_worker in self.worker_opts: for resampler in resamplers: + try: + single_resampler_kwargs = resampler_kwargs[resampler] + except KeyError: + single_resampler_kwargs = resampler_kwargs + print( # noqa f"Start testing CHUNK_SIZE={chunk_size}MiB, NUM_WORKER={num_worker}, resampler is {resampler}.") - self.single_loop((chunk_size, num_worker, resampler), area_def, resampler_kwargs) + self.single_loop((chunk_size, num_worker, resampler), + area_def=area_def, resampler_kwargs=single_resampler_kwargs) i = i + 1 - if i == self.total_rounds: + if i == total_rounds: print("All the tests finished. Generating HTML report.") # noqa html_report(self.work_dir, self.reader_name) else: - print(f"ROUND {i} / {self.total_rounds} Completed. Now take a 1-min rest.") # noqa + print(f"ROUND {i} / {total_rounds} Completed. Now take a 1-min rest.") # noqa time.sleep(60) From 6fb22366c5163af7761eb633d89002547c689d3a Mon Sep 17 00:00:00 2001 From: yukaribbba Date: Sun, 7 Jul 2024 15:01:08 +0800 Subject: [PATCH 19/33] Update performance_test.py --- benchmarks/performance_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/performance_test.py b/benchmarks/performance_test.py index 0e5c7838db..d9769e1b3c 100644 --- a/benchmarks/performance_test.py +++ b/benchmarks/performance_test.py @@ -63,7 +63,7 @@ def __init__(self, work_dir, folder_pattern, reader_name, composite, chunk_size_ self.folders = glob.glob(f"{self.work_dir}/{self.folder_pattern}") self.chunk_size_opts = chunk_size_opts - self.worker_opts = worker_opts + self.worker_opts = worker_opts self.result = {} self.running = True From 8244042fd6543478ada52037ec1e601b4726480e Mon Sep 17 00:00:00 2001 From: yukaribbba Date: Sun, 7 Jul 2024 18:25:15 +0800 Subject: [PATCH 20/33] docs --- doc/source/index.rst | 11 +- doc/source/performance_test.rst | 86 +++++++++++ .../performance_tests/abi_l1b_tests.rst | 141 ------------------ doc/source/performance_tests/index.rst | 81 ---------- 4 files changed, 88 insertions(+), 231 deletions(-) create mode 100644 doc/source/performance_test.rst delete mode 100644 doc/source/performance_tests/abi_l1b_tests.rst delete mode 100644 doc/source/performance_tests/index.rst diff --git a/doc/source/index.rst b/doc/source/index.rst index e50bc2ffc3..296b931d9c 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -72,20 +72,13 @@ Documentation multiscene dev_guide/index + .. toctree:: :maxdepth: 1 Satpy API faq - -.. toctree:: - :maxdepth: 2 - - performance_tests/index - -.. toctree:: - :maxdepth: 1 - + performance_test Release Notes Security Policy diff --git a/doc/source/performance_test.rst b/doc/source/performance_test.rst new file mode 100644 index 0000000000..b303519256 --- /dev/null +++ b/doc/source/performance_test.rst @@ -0,0 +1,86 @@ +================= +Performance Tests +================= + +For better performace tweaks on specific readers, a tool ``performance_test`` under ``benchmarks`` is introduced here. +It involves ``DASK_ARRAY__CHUNK_SIZE``, ``DASK_NUM_WORKERS``, ``OMP_NUM_THREADS`` and other options mentioned in +:doc:`FAQ <../faq>`. This tool can loop through conditions defined by these values finally give a report in HTML. +The report contains tables and charts for better understanding. It has two types of tests: ``simple_test`` +and ``resampler_test``. + + +How it works? +============= +- The core is just a regular satpy script -- find datasets, load the composite, resample it if needed and + save it as geotiff. +- A monitor thread using ``psutil`` records the CPU and memory usage synchronously. The sample rate is around + 0.5 seconds. Any errors during the test will also be recorded. +- Each round will have one single condition tested. The result is stored in a csv file. After that, the machine will + take a 1-min rest to let the CPU cool down. +- After all the tests finished, it collects all the result csv files, visualizing and summarizing theme into the HTML + report. + + +Preparations +============ +1. Additional packages required +------------------------------- +- **psutil:** Record CPU/memory usage +- **pandas:** Analyze test result +- **matplotlib**: Plot the charts for report. +- **py-cpuinfo**: Get the CPU model for report. + + +2. Choose the composite and get corresponding datasets +------------------------------------------------------ +Although one scene is enough to run the test, 3-5 scenes would be better to get the average. + +- For geostationary satellites, it is recommended to get those around **solar noon** under **full-disk** scan mode. +- For polar satellites, scenes should be around the **same area** so the intensities of the computation are similar. + + +3. Organize the datasets +------------------------ +One scene per folder. All the dataset folders should have the same naming patterns, e.g.: + +.. code-block:: batch + + 2024/06/29 09:06 G16_s20241691700214_e20241691709522_FLDK + 2024/06/29 09:06 G16_s20241701700215_e20241701709523_FLDK + 2024/06/29 09:06 G16_s20241711700217_e20241711709525_FLDK + 2024/06/29 09:06 G16_s20241721700219_e20241721709527_FLDK + 2024/06/29 09:06 G16_s20241731700220_e20241731709528_FLDK + + +4. Do I have enough swap memory? +-------------------------------- +Some conditions or resamplers will consume a hell of memory and will need swap. When both are at their limits, +the OS may just kill the test process without any warnings or errors recorded. + + +5. Arrange your time and work +----------------------------- +The whole test progress may last hours long depending on the conditions. Keep the machine free during this period. +Turn off all the unnecessary background jobs such as software update. + + +Usage +===== +Initialize +---------- + +.. code-block:: python + + from performance_test import SatpyPerformanceTest + tester = SatpyPerformanceTest(work_dir="C:/Users/ABC/Downloads/Sat/Geo/ABI pef test", + folder_pattern="G16_s*_e*_FLDK", + reader_name="abi_l1b", + composite="true_color", + chunk_size_opts=[16, 64], + worker_opts=[8, 16]) + + +``simple_test`` +--------------- +This will test the reader in dataset's original projection. + diff --git a/doc/source/performance_tests/abi_l1b_tests.rst b/doc/source/performance_tests/abi_l1b_tests.rst deleted file mode 100644 index dea224c3ef..0000000000 --- a/doc/source/performance_tests/abi_l1b_tests.rst +++ /dev/null @@ -1,141 +0,0 @@ -================= -Tests for abi_l1b -================= -- Datasets: 5 scenes by GOES-16 around solar noon (scan period: UTC 17:00 - 17:10) from 2024.06.17 to 2024.06.21 -- Area and resampling: - -+-------------+------------+----------------------------------------------------------------------------------+--------------------------+ -| Description | Resolution | Area Definition | Resampler | -+=============+============+==================================================================================+==========================+ -| Full Disk | 500m | ``scn.finest_area()`` | ``native`` | -+-------------+------------+----------------------------------------------------------------------------------+--------------------------+ -| Local | 500m | - width: 8008, height: 8008 | ``nearest``/``bilinear`` | -| | | - projection: +proj=lcc +lon_0=-96 +lat_1=20 +lat_2=60 +datum=WGS84 +ellps=WGS84 | | -| | | - area extent: (-106000, 2635000, 3898000, 6639000) | | -+-------------+------------+----------------------------------------------------------------------------------+--------------------------+ - - -Recommended Settings -==================== -- ``DASK_ARRAY__CHUNK_SIZE``: **16MiB** -- ``DASK_NUM_WORKERS``: **8/12/16** are all worth considering - - -Result Table -============ - -Fulldisk - native resampling ----------------------------- -+------------+---------+--------+--------+--------+-------+--------+ -| Dask Array | Dask | Time | Avg | Max | Avg | Errors | -| Chunk Size | Num | (s) | Memory | Memory | CPU | | -| (MB) | Workers | | (GB) | (GB) | (%) | | -+============+=========+========+========+========+=======+========+ -| 16 | 8 | 151.01 | 4.96 | 9.36 | 59.54 | N/A | -+------------+---------+--------+--------+--------+-------+--------+ -| 16 | 12 | 151.22 | 6.33 | 11.96 | 69.05 | N/A | -+------------+---------+--------+--------+--------+-------+--------+ -| 16 | 16 | 152.99 | 7.88 | 14.46 | 68.87 | N/A | -+------------+---------+--------+--------+--------+-------+--------+ -| 32 | 8 | 152.23 | 8.75 | 18.61 | 60.45 | N/A | -+------------+---------+--------+--------+--------+-------+--------+ -| 32 | 12 | 153.49 | 11.95 | 23.23 | 64.36 | N/A | -+------------+---------+--------+--------+--------+-------+--------+ -| 32 | 16 | 155.00 | 15.03 | 34.72 | 64.39 | N/A | -+------------+---------+--------+--------+--------+-------+--------+ -| 64 | 8 | 165.17 | 18.23 | 42.97 | 53.77 | N/A | -+------------+---------+--------+--------+--------+-------+--------+ -| 64 | 12 | 165.79 | 25.83 | 60.45 | 51.67 | N/A | -+------------+---------+--------+--------+--------+-------+--------+ -| 64 | 16 | 166.13 | 33.48 | 78.64 | 58.23 | N/A | -+------------+---------+--------+--------+--------+-------+--------+ -| 96 | 8 | 169.79 | 26.62 | 74.84 | 51.73 | N/A | -+------------+---------+--------+--------+--------+-------+--------+ -| 96 | 12 | 178.59 | 37.50 | 97.69 | 53.16 | N/A | -+------------+---------+--------+--------+--------+-------+--------+ -| 96 | 16 | 197.83 | 45.96 | 122.95 | 50.77 | N/A | -+------------+---------+--------+--------+--------+-------+--------+ -| 128 | 8 | 165.92 | 27.69 | 68.20 | 54.77 | N/A | -+------------+---------+--------+--------+--------+-------+--------+ -| 128 | 12 | 177.71 | 37.22 | 98.93 | 54.49 | N/A | -+------------+---------+--------+--------+--------+-------+--------+ -| 128 | 16 | 205.90 | 49.01 | 124.45 | 50.47 | N/A | -+------------+---------+--------+--------+--------+-------+--------+ - - -Local area - nearest resampling (with cache) --------------------------------------------- -+------------+---------+--------+--------+--------+-------+--------+ -| Dask Array | Dask | Time | Avg | Max | Avg | Errors | -| Chunk Size | Num | (s) | Memory | Memory | CPU | | -| (MB) | Workers | | (GB) | (GB) | (%) | | -+------------+---------+--------+--------+--------+-------+--------+ -| 16 | 8 | 41.94 | 4.34 | 7.8 | 32.98 | N/A | -+------------+---------+--------+--------+--------+-------+--------+ -| 16 | 12 | 41.79 | 4.7 | 9.51 | 38.32 | N/A | -+------------+---------+--------+--------+--------+-------+--------+ -| 16 | 16 | 41.99 | 5.05 | 10.4 | 39.35 | N/A | -+------------+---------+--------+--------+--------+-------+--------+ -| 32 | 8 | 39.94 | 4.94 | 12.01 | 34.29 | N/A | -+------------+---------+--------+--------+--------+-------+--------+ -| 32 | 12 | 39.81 | 5.46 | 16.26 | 37.66 | N/A | -+------------+---------+--------+--------+--------+-------+--------+ -| 32 | 16 | 39.99 | 6.21 | 20.67 | 36.37 | N/A | -+------------+---------+--------+--------+--------+-------+--------+ -| 64 | 8 | 41.38 | 6.17 | 19.36 | 33.5 | N/A | -+------------+---------+--------+--------+--------+-------+--------+ -| 64 | 12 | 40.29 | 7.03 | 23.65 | 37.03 | N/A | -+------------+---------+--------+--------+--------+-------+--------+ -| 64 | 16 | 39.91 | 7.44 | 25.28 | 38.13 | N/A | -+------------+---------+--------+--------+--------+-------+--------+ -| 96 | 8 | 40.43 | 7.15 | 23.24 | 34.66 | N/A | -+------------+---------+--------+--------+--------+-------+--------+ -| 96 | 12 | 40.31 | 7.09 | 22.12 | 35.33 | N/A | -+------------+---------+--------+--------+--------+-------+--------+ -| 96 | 16 | 39.94 | 7.31 | 23.11 | 36.4 | N/A | -+------------+---------+--------+--------+--------+-------+--------+ -| 128 | 8 | 42.9 | 7.85 | 26.48 | 31.27 | N/A | -+------------+---------+--------+--------+--------+-------+--------+ -| 128 | 12 | 43.78 | 7.94 | 27.72 | 30.78 | N/A | -+------------+---------+--------+--------+--------+-------+--------+ -| 128 | 16 | 42.17 | 8.05 | 28.56 | 31.87 | N/A | -+------------+---------+--------+--------+--------+-------+--------+ - - -Local area - bilinear resampling (with cache) ---------------------------------------------- -+------------+---------+--------+--------+--------+-------+--------+ -| Dask Array | Dask | Time | Avg | Max | Avg | Errors | -| Chunk Size | Num | (s) | Memory | Memory | CPU | | -| (MB) | Workers | | (GB) | (GB) | (%) | | -+------------+---------+--------+--------+--------+-------+--------+ -| 16 | 8 | 196.78 | 12.65 | 37.36 | 7.81 | N/A | -+------------+---------+--------+--------+--------+-------+--------+ -| 16 | 12 | 141.56 | 12.06 | 39.92 | 10.35 | N/A | -+------------+---------+--------+--------+--------+-------+--------+ -| 16 | 16 | 144.00 | 12.74 | 41.58 | 10.75 | N/A | -+------------+---------+--------+--------+--------+-------+--------+ -| 32 | 8 | 144.10 | 12.26 | 38.74 | 10.05 | N/A | -+------------+---------+--------+--------+--------+-------+--------+ -| 32 | 12 | 146.39 | 13.09 | 42.94 | 10.70 | N/A | -+------------+---------+--------+--------+--------+-------+--------+ -| 32 | 16 | 150.53 | 13.81 | 39.60 | 10.68 | N/A | -+------------+---------+--------+--------+--------+-------+--------+ -| 64 | 8 | 152.96 | 12.99 | 40.93 | 10.45 | N/A | -+------------+---------+--------+--------+--------+-------+--------+ -| 64 | 12 | 163.94 | 13.73 | 41.10 | 9.97 | N/A | -+------------+---------+--------+--------+--------+-------+--------+ -| 64 | 16 | 151.42 | 14.65 | 42.42 | 11.62 | N/A | -+------------+---------+--------+--------+--------+-------+--------+ -| 96 | 8 | 219.15 | 14.22 | 41.90 | 8.11 | N/A | -+------------+---------+--------+--------+--------+-------+--------+ -| 96 | 12 | 182.07 | 15.32 | 41.61 | 10.12 | N/A | -+------------+---------+--------+--------+--------+-------+--------+ -| 96 | 16 | 195.08 | 15.85 | 41.35 | 10.42 | N/A | -+------------+---------+--------+--------+--------+-------+--------+ -| 128 | 8 | 174.62 | 14.69 | 39.29 | 9.95 | N/A | -+------------+---------+--------+--------+--------+-------+--------+ -| 128 | 12 | 214.65 | 16.17 | 40.08 | 9.53 | N/A | -+------------+---------+--------+--------+--------+-------+--------+ -| 128 | 16 | 198.26 | 15.97 | 46.53 | 9.62 | N/A | -+------------+---------+--------+--------+--------+-------+--------+ diff --git a/doc/source/performance_tests/index.rst b/doc/source/performance_tests/index.rst deleted file mode 100644 index aee50031fb..0000000000 --- a/doc/source/performance_tests/index.rst +++ /dev/null @@ -1,81 +0,0 @@ -================= -Performance Tests -================= - -For better performace tweaks on specific readers, here're the results from a series -of tests involving ``DASK_ARRAY__CHUNK_SIZE``, ``DASK_NUM_WORKERS`` and other options -mentioned in :doc:`FAQ <../faq>`. - -.. toctree:: - :maxdepth: 1 - - abi_l1b_tests - - -Test platform -------------- -+----------+--------------------------------------+ -| CPU | 1x 8-core, 8-thread i7-9700k @4.6GHz | -+----------+--------------------------------------+ -| Memory | 2x 32GB DDR4 | -+----------+--------------------------------------+ -| SSD | 1x Samsung 980 Pro PCI-E 2TB | -+----------+--------------------------------------+ -| OS | Windows 11 23H2 Workstation Pro | -+----------+--------------------------------------+ - - -Conda environment ------------------ -+------------+-------------+ -| Channel | conda-forge | -+------------+-------------+ -| Python | 3.12.3 | -+------------+-------------+ -| dask | 2024.6.2 | -+------------+-------------+ -| numpy | 2.0.0 | -+------------+-------------+ -| satpy | 0.49 | -+------------+-------------+ -| pyresample | 1.28.3 | -+------------+-------------+ -| pyspectral | 0.13.1 | -+------------+-------------+ -| psutil | 6.0.0 | -+------------+-------------+ - - -Test procedure --------------- -- Each round will go through 5 scenes to calculate average. - -- The composite will usually be the default ``true_color`` which requires heavy computation like atmospheric corrections. - -- A new monitor thread using ``psutil`` will record the CPU and memory usage synchronously. The sample rate is around 0.5 seconds. - -- When the current round finished, the machine will take a 2-min rest to let the CPU cool down. - -- After that, reboot will clear the system cache and prevent the test program from taking advantage of it. - - -Test conditions ---------------- -+------------------------------------+--------------------------------------------------------+ -| DASK_ARRAY__CHUNK_SIZE (in MiB) | 16, 32, 64, 96, 128 | -+------------------------------------+--------------------------------------------------------+ -| DASK_ARRAY__CHUNK_SIZE (in arrays) | 512x512, 1024x1024, 2048x2048, 3072x3072, 4096x4096 | -+------------------------------------+--------------------------------------------------------+ -| DASK_NUM_WORKERS | 8, 12, 16 | -+------------------------------------+--------------------------------------------------------+ -| OMP_NUM_THREADS | 8 | -+------------------------------------+--------------------------------------------------------+ -| generate=False | Used when the composite requires different resolutions | -+------------------------------------+--------------------------------------------------------+ -| nprocs=8 | Used on ``nearest`` or ``bilinear`` resampling | -+------------------------------------+--------------------------------------------------------+ -| resampling cache | Used on ``nearest`` or ``bilinear`` resampling | -+------------------------------------+--------------------------------------------------------+ - -General conclusions -------------------- From 15f67f8063d9a5f065f88e911335238c5b77d748 Mon Sep 17 00:00:00 2001 From: yukaribbba Date: Sun, 7 Jul 2024 20:42:19 +0800 Subject: [PATCH 21/33] docs --- benchmarks/performance_test.py | 23 +++++++++-------- doc/source/conf.py | 3 +++ doc/source/performance_test.rst | 45 ++++++++++++++++++++++++++++----- 3 files changed, 54 insertions(+), 17 deletions(-) diff --git a/benchmarks/performance_test.py b/benchmarks/performance_test.py index d9769e1b3c..4113036fae 100644 --- a/benchmarks/performance_test.py +++ b/benchmarks/performance_test.py @@ -29,6 +29,7 @@ import numpy as np import pandas as pd import psutil +from pyresample.geometry import AreaDefinition, DynamicAreaDefinition class SatpyPerformanceTest: @@ -42,15 +43,15 @@ def __init__(self, work_dir, folder_pattern, reader_name, composite, chunk_size_ """Initialize SatpyPerformanceTest with some basic arguments. Args: - work_dir (str): Absolute path to the directory that contains all your dataset folders. + work_dir (str): Absolute path to the base directory that contains all your dataset folders. folder_pattern (str): Naming scheme of the dataset folders, e.g. `G16_s*_e*_FLDK`. + This will be used for ``glob.glob`` to search the datasets. reader_name (str): Reader you want to test. - composite (str): Composite for test. Usually this could be ``true_color`` which involves a - lot of computation like atmospheric correction. + composite (str): Composite for test. chunk_size_opts (list): All the ``dask_array_chunk_size`` values you wish for, in `MiB`. worker_opts (list): All the ``dask_num_workers`` values you wish for. reader_kwargs (dict): Additional reader arguments for ``Scene``, - e.g. `{'mask_saturated': False}` in modis_l1b. + like ``{'mask_saturated': False}`` in modis_l1b. """ super().__init__() @@ -170,13 +171,13 @@ def single_loop(self, conditions, generate=False, area_def=None, resampler_kwarg self.write_to_csv(csv_file) def simple_test(self, diff_res=False): - """Test readers in dataset's original projection. No resampling involved or the simplest ``native`` resampling. + """Test the reader in dataset's original projection, with no resampling or the simplest ``native`` resampling. Args: - diff_res (bool): If the composite requires bands in different resolutions, this should be set to True + diff_res (bool): If the composite requires bands in different resolutions, this should be set to `True` so the native resampler will match them to the ``scn.finest_area()``. For example, ``true_color`` of ABI needs 500m C01 and 1000m C02 bands, so it's `True`. - This is not a test option and should be set properly according to the composite, + **This is not a test option and should be set properly according to the composite,** otherwise the test will end up in errors. """ @@ -199,7 +200,7 @@ def simple_test(self, diff_res=False): time.sleep(60) def resampler_test(self, resamplers, area_def, resampler_kwargs=None): - """Test readers with resampling. + """Test the reader with resampling. Args: resamplers (list or str): A single resampling algorithm or a list of resampling algorithms you want to test. @@ -207,9 +208,9 @@ def resampler_test(self, resamplers, area_def, resampler_kwargs=None): in YAML. resampler_kwargs (dict): Additional arguments passed to the resampler. You can specify the separate kwargs for each resampler, e.g. - {'bilinear': {'cache_dir': '/path/to/my/cache'}, - 'ewa': {'weight_delta_max': 40, 'weight_distance_max': 2}}. - Or you can just give general kwargs like {'cache_dir': '/path/to/my/cache'} for + ``{'bilinear': {'cache_dir': '/path/to/my/cache'}, + 'ewa': {'weight_delta_max': 40, 'weight_distance_max': 2}}``. + Or you can just give general kwargs like ``{'cache_dir': '/path/to/my/cache'}`` for both ``nearest`` and ``bilinear``. """ diff --git a/doc/source/conf.py b/doc/source/conf.py index 62156e9760..34d02a918a 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -129,6 +129,9 @@ def __getattr__(cls, name): "--private", ] +# Additional api for 'performance_test' +sys.path.insert(0, os.path.abspath('../../benchmarks/')) + # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] diff --git a/doc/source/performance_test.rst b/doc/source/performance_test.rst index b303519256..e2a3f94a3a 100644 --- a/doc/source/performance_test.rst +++ b/doc/source/performance_test.rst @@ -25,14 +25,17 @@ Preparations ============ 1. Additional packages required ------------------------------- -- **psutil:** Record CPU/memory usage -- **pandas:** Analyze test result +- **psutil:** Record CPU/memory usage. +- **pandas:** Analyze test result. - **matplotlib**: Plot the charts for report. - **py-cpuinfo**: Get the CPU model for report. 2. Choose the composite and get corresponding datasets ------------------------------------------------------ +Usually the composite should be the one involving a lot of computation like atmospheric correction. For most of the +earth observing satellites, this could be ``true_color`` or something like that. + Although one scene is enough to run the test, 3-5 scenes would be better to get the average. - For geostationary satellites, it is recommended to get those around **solar noon** under **full-disk** scan mode. @@ -61,13 +64,14 @@ the OS may just kill the test process without any warnings or errors recorded. 5. Arrange your time and work ----------------------------- The whole test progress may last hours long depending on the conditions. Keep the machine free during this period. -Turn off all the unnecessary background jobs such as software update. +Avoid any unnecessary background jobs like software update. Usage ===== Initialize ---------- +.. autofunction:: performance_test.SatpyPerformanceTest.__init__ .. code-block:: python @@ -79,8 +83,37 @@ Initialize chunk_size_opts=[16, 64], worker_opts=[8, 16]) +simple_test +----------- +.. autofunction:: performance_test.SatpyPerformanceTest.simple_test + +.. code-block:: python + + # You can set some system environments related to satpy before running the test. + os.environ["PSP_CONFIG_FILE"] = "D:/satpy_config/pyspectral/pyspectral.yaml" + + tester.simple_test(diff_res=True) + +resampler_test +-------------- +.. autofunction:: performance_test.SatpyPerformanceTest.resampler_test + +.. code-block:: python -``simple_test`` ---------------- -This will test the reader in dataset's original projection. + from pyresample.geometry import AreaDefinition + + proj = "+proj=lcc +lon_0=-96 +lat_1=20 +lat_2=60 +datum=WGS84 +ellps=WGS84" + width = 8008 + height = 8008 + area_extent = (-106000, 2635000, 3898000, 6639000) + nprocs=8 + + area_def = AreaDefinition(area_id="NorthAmerica", proj_id="lcc", description="na", + projection=proj, width=width, height=height, area_extent=area_extent, nprocs=nprocs) + new_tester.resampler_test(resamplers=["bilinear", "ewa"], + area_def=area_def, + resampler_kwargs={ + "bilinear": {"cache_dir": "C:/Users/45107/Downloads/Sat/Geo/ABI pef test/cache"}, + "ewa": {"weight_delta_max": 40, "weight_distance_max": 2}, + }) From d2a34e0ea10374425c3fed5b48dbd5e0ed2a9f33 Mon Sep 17 00:00:00 2001 From: yukaribbba Date: Sun, 7 Jul 2024 21:21:20 +0800 Subject: [PATCH 22/33] fix --- benchmarks/performance_test.py | 1 - doc/source/conf.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/benchmarks/performance_test.py b/benchmarks/performance_test.py index 4113036fae..43d1f849df 100644 --- a/benchmarks/performance_test.py +++ b/benchmarks/performance_test.py @@ -29,7 +29,6 @@ import numpy as np import pandas as pd import psutil -from pyresample.geometry import AreaDefinition, DynamicAreaDefinition class SatpyPerformanceTest: diff --git a/doc/source/conf.py b/doc/source/conf.py index 34d02a918a..e808c8bcfa 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -130,7 +130,7 @@ def __getattr__(cls, name): ] # Additional api for 'performance_test' -sys.path.insert(0, os.path.abspath('../../benchmarks/')) +sys.path.insert(0, os.path.abspath("../../benchmarks/")) # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] From 46daf1f52bd6db10723e6b1f4dca4f17a0e4b721 Mon Sep 17 00:00:00 2001 From: yukaribbba Date: Sun, 7 Jul 2024 21:36:33 +0800 Subject: [PATCH 23/33] Update performance_test.rst --- doc/source/performance_test.rst | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/doc/source/performance_test.rst b/doc/source/performance_test.rst index e2a3f94a3a..f98e5cbcea 100644 --- a/doc/source/performance_test.rst +++ b/doc/source/performance_test.rst @@ -39,7 +39,7 @@ earth observing satellites, this could be ``true_color`` or something like that. Although one scene is enough to run the test, 3-5 scenes would be better to get the average. - For geostationary satellites, it is recommended to get those around **solar noon** under **full-disk** scan mode. -- For polar satellites, scenes should be around the **same area** so the intensities of the computation are similar. +- For polar orbit satellites, scenes should be around the **same area** so the intensities of the computation are similar. 3. Organize the datasets @@ -57,7 +57,7 @@ One scene per folder. All the dataset folders should have the same naming patter 4. Do I have enough swap memory? -------------------------------- -Some conditions or resamplers will consume a hell of memory and will need swap. When both are at their limits, +Some conditions or resamplers will consume a hell of physical memory and then swap. When both are at their limits, the OS may just kill the test process without any warnings or errors recorded. @@ -117,3 +117,19 @@ resampler_test "ewa": {"weight_delta_max": 40, "weight_distance_max": 2}, }) +How to test ``OMP_NUM_THREADS``? +-------------------------------- +``OMP_NUM_THREADS`` should be set outside the python script. In **Linux**, you can set it temporarily by + +.. code-block:: shell + + OMP_NUM_THREADS=4 python your_test_script.py + +In **Windows**: + +.. code-block:: batch + + set OMP_NUM_THREADS=4 && python your_test_script.py + +You can also choose not to set it. Normally the program will use as many logic cores as available. Either way, the test +will pick up the correct value and pass it to the report. From 91f3b0b9e99842b40607823445115200dc2b2d03 Mon Sep 17 00:00:00 2001 From: yukaribbba Date: Mon, 8 Jul 2024 00:03:00 +0800 Subject: [PATCH 24/33] Update performance_test.rst --- doc/source/performance_test.rst | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/doc/source/performance_test.rst b/doc/source/performance_test.rst index f98e5cbcea..aa945dc464 100644 --- a/doc/source/performance_test.rst +++ b/doc/source/performance_test.rst @@ -15,9 +15,9 @@ How it works? save it as geotiff. - A monitor thread using ``psutil`` records the CPU and memory usage synchronously. The sample rate is around 0.5 seconds. Any errors during the test will also be recorded. -- Each round will have one single condition tested. The result is stored in a csv file. After that, the machine will +- Each round has one single condition tested. The result is stored in a csv file. After that, the machine will take a 1-min rest to let the CPU cool down. -- After all the tests finished, it collects all the result csv files, visualizing and summarizing theme into the HTML +- After all the tests finished, it collects all the result csv files, summarizing and visualizing them into the HTML report. @@ -57,18 +57,24 @@ One scene per folder. All the dataset folders should have the same naming patter 4. Do I have enough swap memory? -------------------------------- -Some conditions or resamplers will consume a hell of physical memory and then swap. When both are at their limits, +Some conditions or resamplers may consume a hell of physical memory and then swap. When both are at their limits, the OS may just kill the test process without any warnings or errors recorded. 5. Arrange your time and work ----------------------------- -The whole test progress may last hours long depending on the conditions. Keep the machine free during this period. +The whole test progress could last hours long depending on the conditions. Keep the machine free during this period. Avoid any unnecessary background jobs like software update. Usage ===== +.. note:: + + Both ``simple_test`` and ``resampler_test`` collect all the results under ``work_dir`` and produce the report + in the same format. So if you already have some previous tests, just keep them in the same directory and the + test will merge them into one automatically. + Initialize ---------- .. autofunction:: performance_test.SatpyPerformanceTest.__init__ @@ -116,10 +122,16 @@ resampler_test "bilinear": {"cache_dir": "C:/Users/45107/Downloads/Sat/Geo/ABI pef test/cache"}, "ewa": {"weight_delta_max": 40, "weight_distance_max": 2}, }) +.. note:: + + When you test ``bilinear`` or ``nearest`` resampler on geostationary datasets and want to both accelerate the test + and exclude the impact of resampling cache, it is recommended to pre-build the cache with just one scene and + one condition. And by that, you can also have a chance to tell how big the difference is between with and + without cache (Sometimes, it's VERY, especially for ``bilinear``). How to test ``OMP_NUM_THREADS``? -------------------------------- -``OMP_NUM_THREADS`` should be set outside the python script. In **Linux**, you can set it temporarily by +``OMP_NUM_THREADS`` should be set outside the python script. In **Linux**, you can do it temporarily by .. code-block:: shell From 27607002d6bd5182a6f8f81999cd8e6501f81f75 Mon Sep 17 00:00:00 2001 From: yukaribbba Date: Mon, 8 Jul 2024 00:06:34 +0800 Subject: [PATCH 25/33] Update performance_test.rst --- doc/source/performance_test.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/performance_test.rst b/doc/source/performance_test.rst index aa945dc464..257c4fd0fd 100644 --- a/doc/source/performance_test.rst +++ b/doc/source/performance_test.rst @@ -73,7 +73,7 @@ Usage Both ``simple_test`` and ``resampler_test`` collect all the results under ``work_dir`` and produce the report in the same format. So if you already have some previous tests, just keep them in the same directory and the - test will merge them into one automatically. + test will merge them into one and refresh the report automatically. Initialize ---------- From e1ebfaf88ee6352677f23c46c873084472a4ca76 Mon Sep 17 00:00:00 2001 From: yukaribbba Date: Mon, 8 Jul 2024 00:08:02 +0800 Subject: [PATCH 26/33] Update performance_test.py --- benchmarks/performance_test.py | 49 ++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/benchmarks/performance_test.py b/benchmarks/performance_test.py index 43d1f849df..e24993b608 100644 --- a/benchmarks/performance_test.py +++ b/benchmarks/performance_test.py @@ -30,6 +30,8 @@ import pandas as pd import psutil +OS_TYPE = platform.system() + class SatpyPerformanceTest: """Test satpy performance by looping through conditions involving ``dask_array_chunk_size``and ``dask_num_workers``. @@ -70,8 +72,6 @@ def __init__(self, work_dir, folder_pattern, reader_name, composite, chunk_size_ def monitor_system_usage(self, interval=0.5): """Use psutil to record CPU and memory usage. Default sample rate is 0.5s.""" - os_type = platform.system() - process = psutil.Process() cpu_usage = [] memory_usage = [] @@ -80,10 +80,10 @@ def monitor_system_usage(self, interval=0.5): start_time = time.time() while self.running: cpu_usage.append(process.cpu_percent()) - if os_type == "Windows": + if OS_TYPE == "Windows": # In Windows, "vms" means "pagefile" memory_usage.append((process.memory_full_info().rss + process.memory_full_info().vms)) - elif os_type == "Linux": + elif OS_TYPE == "Linux": memory_usage.append((process.memory_full_info().rss + process.memory_full_info().swap)) else: memory_usage.append(process.memory_full_info().rss) @@ -139,6 +139,9 @@ def single_loop(self, conditions, generate=False, area_def=None, resampler_kwarg except KeyError: num_thread = psutil.cpu_count(logical=True) + print(f"Start testing CHUNK_SIZE={chunk_size}MiB, NUM_WORKER={num_worker}, NUM_THREADS={num_thread}, " # noqa + f"resampler is \"{resampler}\".") + # Start recording cpu/mem usage monitor_thread = Thread(target=self.monitor_system_usage, args=(0.5,)) monitor_thread.start() @@ -184,10 +187,10 @@ def simple_test(self, diff_res=False): generate = not diff_res total_rounds = len(self.chunk_size_opts) * len(self.worker_opts) + print(f"{self.reader_name} test started. Composite is \"{self.composite}\".\n") # noqa i = 0 for chunk_size in self.chunk_size_opts: for num_worker in self.worker_opts: - print(f"Start testing CHUNK_SIZE={chunk_size}MiB, NUM_WORKER={num_worker}, resampler is {resampler}.") # noqa self.single_loop((chunk_size, num_worker, resampler), generate=generate) i = i + 1 @@ -217,6 +220,7 @@ def resampler_test(self, resamplers, area_def, resampler_kwargs=None): resampler_kwargs = {} if resampler_kwargs is None else resampler_kwargs total_rounds = len(self.chunk_size_opts) * len(self.worker_opts) * len(resamplers) + print(f"{self.reader_name} test started. Composite is \"{self.composite}\".\n") # noqa i = 0 for chunk_size in self.chunk_size_opts: for num_worker in self.worker_opts: @@ -226,17 +230,15 @@ def resampler_test(self, resamplers, area_def, resampler_kwargs=None): except KeyError: single_resampler_kwargs = resampler_kwargs - print( # noqa - f"Start testing CHUNK_SIZE={chunk_size}MiB, NUM_WORKER={num_worker}, resampler is {resampler}.") self.single_loop((chunk_size, num_worker, resampler), area_def=area_def, resampler_kwargs=single_resampler_kwargs) i = i + 1 if i == total_rounds: - print("All the tests finished. Generating HTML report.") # noqa + print(f"ROUND {i} / {total_rounds} Completed. Generating HTML report.") # noqa html_report(self.work_dir, self.reader_name) else: - print(f"ROUND {i} / {total_rounds} Completed. Now take a 1-min rest.") # noqa + print(f"ROUND {i} / {total_rounds} Completed. Now take a 1-min rest.\n") # noqa time.sleep(60) @@ -302,9 +304,16 @@ def combined_csv(work_dir, reader_name): def draw_hbar(dataframe, title): """Plot the bar chart by matplotlib.""" + if OS_TYPE == "Windows": + memory_key = "Memory Usage (Physical + PageFile)" + elif OS_TYPE == "Linux": + memory_key = "Memory Usage (Physical + Swap)" + else: + memory_key = "Memory Usage" + figures = {"Process Time (single scene average)": {"key_y": "Time (s)", "colors": ["#4E79A7"]}, "Average CPU Usage": {"key_y": "Avg CPU (%)", "colors": ["#F28E2B"]}, - "Memory Usage": {"key_y": ["Avg Memory (GB)", "Max Memory (GB)"], "colors": ["#59A14F", "#EDC948"]}} + memory_key: {"key_y": ["Avg Memory (GB)", "Max Memory (GB)"], "colors": ["#59A14F", "#EDC948"]}} colors = figures[title]["colors"] key_x = "Chunk size - Num workers - Num Threads" @@ -318,19 +327,19 @@ def draw_hbar(dataframe, title): fig, ax = plt.subplots(figsize=(fig_width, fig_height), dpi=dpi) plt.subplots_adjust(left=0.15, right=0.85, top=0.9, bottom=0.1) - dataframe.plot.barh(x=key_x, y=key_y, legend=True if title == "Memory Usage" else False, - ax=ax, width=0.5, color=colors, stacked=True if title == "Memory Usage" else False) + dataframe.plot.barh(x=key_x, y=key_y, legend=True if "Memory" in title else False, + ax=ax, width=0.5, color=colors, stacked=True if "Memory" in title else False) plt.title(title, fontsize=16) plt.ylabel(key_x, fontsize=14) - plt.xlabel("Memory (GB)" if title == "Memory Usage" else key_y, fontsize=14) + plt.xlabel("Memory (GB)" if "Memory" in title else key_y, fontsize=14) ax.tick_params(axis="both", labelsize=12) - if title == "Memory Usage": + if "Memory" in title: ax.legend(loc="upper right") # Mark the position of physical memory limit physical_memory = psutil.virtual_memory().total // (1024 ** 3) ax.axvline(x=physical_memory, color="#808080", linestyle="--") ax.text(physical_memory + 0.5, 1, "Physical\nMemory\nLimit", color="#808080") - if title == "Average CPU Usage": + if "CPU" in title: ax.set_xlim([0, 100]) # Data label right to the bar @@ -338,7 +347,7 @@ def draw_hbar(dataframe, title): for i, container in enumerate(ax.containers): for j, bar in enumerate(container): width = bar.get_width() - cumulative_widths[j] = cumulative_widths[j] + width if title == "Memory Usage" else width + cumulative_widths[j] = cumulative_widths[j] + width if "Memory" in title else width label_x_pos = cumulative_widths[j] + 0.3 if i == 0: @@ -478,7 +487,13 @@ def html_report(work_dir, reader_name): """ # Plot three charts: time, cpu and mem - titles = ["Process Time (single scene average)", "Average CPU Usage", "Memory Usage"] + if OS_TYPE == "Windows": + memory_title = "Memory Usage (Physical + PageFile)" + elif OS_TYPE == "Linux": + memory_title = "Memory Usage (Physical + Swap)" + else: + memory_title = "Memory Usage" + titles = ["Process Time (single scene average)", "Average CPU Usage", memory_title] for title in titles: svg_bar = draw_hbar(group_df_graph, title) html_content += f""" From bf82704fd103b480d686e678c04be5f5e840c456 Mon Sep 17 00:00:00 2001 From: yukaribbba Date: Mon, 8 Jul 2024 00:20:36 +0800 Subject: [PATCH 27/33] Update performance_test.py --- benchmarks/performance_test.py | 50 ++++++++++++++-------------------- 1 file changed, 21 insertions(+), 29 deletions(-) diff --git a/benchmarks/performance_test.py b/benchmarks/performance_test.py index e24993b608..01b69aa856 100644 --- a/benchmarks/performance_test.py +++ b/benchmarks/performance_test.py @@ -31,6 +31,15 @@ import psutil OS_TYPE = platform.system() +OS_MEMORY_KEY_MAP = { + "Windows": "Memory Usage (Physical + PageFile)", + "Linux": "Memory Usage (Physical + Swap)" +} + +MEMORY_KEY = OS_MEMORY_KEY_MAP.get(OS_TYPE, "Memory Usage") +FIGURES = {"Process Time (single scene average)": {"key_y": "Time (s)", "colors": ["#4E79A7"]}, + "Average CPU Usage": {"key_y": "Avg CPU (%)", "colors": ["#F28E2B"]}, + MEMORY_KEY: {"key_y": ["Avg Memory (GB)", "Max Memory (GB)"], "colors": ["#59A14F", "#EDC948"]}} class SatpyPerformanceTest: @@ -139,8 +148,8 @@ def single_loop(self, conditions, generate=False, area_def=None, resampler_kwarg except KeyError: num_thread = psutil.cpu_count(logical=True) - print(f"Start testing CHUNK_SIZE={chunk_size}MiB, NUM_WORKER={num_worker}, NUM_THREADS={num_thread}, " # noqa - f"resampler is \"{resampler}\".") + print(f"Start testing CHUNK_SIZE={chunk_size}MiB, NUM_WORKER={num_worker}, NUM_THREADS={num_thread}, " # noqa + f"""resampler is "{resampler}".""") # Start recording cpu/mem usage monitor_thread = Thread(target=self.monitor_system_usage, args=(0.5,)) @@ -187,7 +196,7 @@ def simple_test(self, diff_res=False): generate = not diff_res total_rounds = len(self.chunk_size_opts) * len(self.worker_opts) - print(f"{self.reader_name} test started. Composite is \"{self.composite}\".\n") # noqa + print(f"{self.reader_name} test started. Composite is \"{self.composite}\".\n") # noqa i = 0 for chunk_size in self.chunk_size_opts: for num_worker in self.worker_opts: @@ -195,10 +204,10 @@ def simple_test(self, diff_res=False): i = i + 1 if i == total_rounds: - print(f"ROUND {i} / {total_rounds} Completed. Generating HTML report.") # noqa + print(f"ROUND {i} / {total_rounds} Completed. Generating HTML report.") # noqa html_report(self.work_dir, self.reader_name) else: - print(f"ROUND {i} / {total_rounds} Completed. Now take a 1-min rest.\n") # noqa + print(f"ROUND {i} / {total_rounds} Completed. Now take a 1-min rest.\n") # noqa time.sleep(60) def resampler_test(self, resamplers, area_def, resampler_kwargs=None): @@ -220,7 +229,7 @@ def resampler_test(self, resamplers, area_def, resampler_kwargs=None): resampler_kwargs = {} if resampler_kwargs is None else resampler_kwargs total_rounds = len(self.chunk_size_opts) * len(self.worker_opts) * len(resamplers) - print(f"{self.reader_name} test started. Composite is \"{self.composite}\".\n") # noqa + print(f"{self.reader_name} test started. Composite is \"{self.composite}\".\n") # noqa i = 0 for chunk_size in self.chunk_size_opts: for num_worker in self.worker_opts: @@ -304,20 +313,9 @@ def combined_csv(work_dir, reader_name): def draw_hbar(dataframe, title): """Plot the bar chart by matplotlib.""" - if OS_TYPE == "Windows": - memory_key = "Memory Usage (Physical + PageFile)" - elif OS_TYPE == "Linux": - memory_key = "Memory Usage (Physical + Swap)" - else: - memory_key = "Memory Usage" - - figures = {"Process Time (single scene average)": {"key_y": "Time (s)", "colors": ["#4E79A7"]}, - "Average CPU Usage": {"key_y": "Avg CPU (%)", "colors": ["#F28E2B"]}, - memory_key: {"key_y": ["Avg Memory (GB)", "Max Memory (GB)"], "colors": ["#59A14F", "#EDC948"]}} - - colors = figures[title]["colors"] + colors = FIGURES[title]["colors"] key_x = "Chunk size - Num workers - Num Threads" - key_y = figures[title]["key_y"] + key_y = FIGURES[title]["key_y"] dpi = 100 fig_width = 1080 / dpi @@ -450,7 +448,7 @@ def html_report(work_dir, reader_name): """Analyze the summary dataframe and produce an HTML report.""" df = combined_csv(work_dir, reader_name) if df is None: - print("Test CSV result not found! Or its filename doesn't fit [*_chunk*_worker*_thread*_*_*.csv]") # noqa + print("Test CSV result not found! Or its filename doesn't fit [*_chunk*_worker*_thread*_*_*.csv]") # noqa return # Group the dataframe for report df["Group"] = "Area: " + df["Area"] + " - " + "Resampler: " + df["Resampling Algorithm"] @@ -487,17 +485,11 @@ def html_report(work_dir, reader_name): """ # Plot three charts: time, cpu and mem - if OS_TYPE == "Windows": - memory_title = "Memory Usage (Physical + PageFile)" - elif OS_TYPE == "Linux": - memory_title = "Memory Usage (Physical + Swap)" - else: - memory_title = "Memory Usage" - titles = ["Process Time (single scene average)", "Average CPU Usage", memory_title] - for title in titles: + for title in FIGURES.keys(): svg_bar = draw_hbar(group_df_graph, title) html_content += f""" -
+
{svg_bar}
""" From 13f8f2a8772bac8a41f41f2ea87d55eb44d673dd Mon Sep 17 00:00:00 2001 From: yukaribbba Date: Mon, 8 Jul 2024 00:21:42 +0800 Subject: [PATCH 28/33] Update performance_test.py --- benchmarks/performance_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/performance_test.py b/benchmarks/performance_test.py index 01b69aa856..b9ea8d4649 100644 --- a/benchmarks/performance_test.py +++ b/benchmarks/performance_test.py @@ -488,7 +488,7 @@ def html_report(work_dir, reader_name): for title in FIGURES.keys(): svg_bar = draw_hbar(group_df_graph, title) html_content += f""" -
{svg_bar}
From e2d656a705b19d294441630dac910d61fe61d269 Mon Sep 17 00:00:00 2001 From: yukaribbba Date: Mon, 8 Jul 2024 08:53:29 +0800 Subject: [PATCH 29/33] Update performance_test.py --- benchmarks/performance_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/performance_test.py b/benchmarks/performance_test.py index b9ea8d4649..ee51070d96 100644 --- a/benchmarks/performance_test.py +++ b/benchmarks/performance_test.py @@ -177,7 +177,7 @@ def single_loop(self, conditions, generate=False, area_def=None, resampler_kwarg else: area = "local" if len(area_def.area_id) == 0 else area_def.area_id.replace("_", "") - csv_file = (f"{self.work_dir}/{self.reader_name.replace("_", "")}_" + csv_file = (f"{self.work_dir}/{self.reader_name.replace('_', '')}_" f"chunk{chunk_size}_worker{num_worker}_thread{num_thread}_{area}_{resampler}.csv") self.write_to_csv(csv_file) From 1cc737ecc9da90ce1eff41eebd898cd526a5c3f4 Mon Sep 17 00:00:00 2001 From: yukaribbba Date: Mon, 8 Jul 2024 09:08:15 +0800 Subject: [PATCH 30/33] typos --- benchmarks/performance_test.py | 2 +- doc/source/performance_test.rst | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/benchmarks/performance_test.py b/benchmarks/performance_test.py index ee51070d96..31a027505b 100644 --- a/benchmarks/performance_test.py +++ b/benchmarks/performance_test.py @@ -290,7 +290,7 @@ def process_csv(cvs_file): def combined_csv(work_dir, reader_name): """Collect all the csv files under ``work_dir`` and merge them in to one dataframe.""" all_dataframes = [] - csvs = glob.glob(f"{work_dir}/{reader_name.replace("_", "")}_chunk*_worker*_thread*_*_*.csv") + csvs = glob.glob(f"{work_dir}/{reader_name.replace('_', '')}_chunk*_worker*_thread*_*_*.csv") for file in csvs: df = process_csv(file) all_dataframes.append(df) diff --git a/doc/source/performance_test.rst b/doc/source/performance_test.rst index 257c4fd0fd..afb9afd6e8 100644 --- a/doc/source/performance_test.rst +++ b/doc/source/performance_test.rst @@ -63,8 +63,8 @@ the OS may just kill the test process without any warnings or errors recorded. 5. Arrange your time and work ----------------------------- -The whole test progress could last hours long depending on the conditions. Keep the machine free during this period. -Avoid any unnecessary background jobs like software update. +The whole test progress could last hours long depending on the reader, the datasets and the conditions. +Keep the machine free during this period. Avoid any unnecessary background jobs like software update. Usage @@ -81,7 +81,9 @@ Initialize .. code-block:: python + import os from performance_test import SatpyPerformanceTest + tester = SatpyPerformanceTest(work_dir="C:/Users/ABC/Downloads/Sat/Geo/ABI pef test", folder_pattern="G16_s*_e*_FLDK", reader_name="abi_l1b", From fdc26efe942673b10d994bf15cd9638a6b036d47 Mon Sep 17 00:00:00 2001 From: yukaribbba Date: Mon, 8 Jul 2024 11:16:15 +0800 Subject: [PATCH 31/33] Update rtd_environment.yml --- doc/rtd_environment.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/rtd_environment.yml b/doc/rtd_environment.yml index abd8add616..94c5567ae3 100644 --- a/doc/rtd_environment.yml +++ b/doc/rtd_environment.yml @@ -12,14 +12,18 @@ dependencies: # 2.19.1 seems to cause library linking issues - eccodes>=2.20 - graphviz + - matplotlib-base - numba - numpy + - pandas - pillow - pooch + - psutil - pyresample - pytest - python-eccodes - python-geotiepoints + - py-cpuinfo - rasterio - rioxarray - setuptools From ce559df6b54acca4f3eaa57293a4fdc7a6249332 Mon Sep 17 00:00:00 2001 From: yukaribbba Date: Mon, 8 Jul 2024 11:36:43 +0800 Subject: [PATCH 32/33] Update performance_test.rst --- doc/source/performance_test.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/source/performance_test.rst b/doc/source/performance_test.rst index afb9afd6e8..1ccf511102 100644 --- a/doc/source/performance_test.rst +++ b/doc/source/performance_test.rst @@ -1,6 +1,6 @@ -================= -Performance Tests -================= +================ +Performance Test +================ For better performace tweaks on specific readers, a tool ``performance_test`` under ``benchmarks`` is introduced here. It involves ``DASK_ARRAY__CHUNK_SIZE``, ``DASK_NUM_WORKERS``, ``OMP_NUM_THREADS`` and other options mentioned in From 195bcb74cecbd05ebc22e105a01e46083742fbf2 Mon Sep 17 00:00:00 2001 From: yukaribbba Date: Mon, 8 Jul 2024 11:56:58 +0800 Subject: [PATCH 33/33] Update performance_test.rst --- doc/source/performance_test.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/performance_test.rst b/doc/source/performance_test.rst index 1ccf511102..9104620de2 100644 --- a/doc/source/performance_test.rst +++ b/doc/source/performance_test.rst @@ -27,7 +27,7 @@ Preparations ------------------------------- - **psutil:** Record CPU/memory usage. - **pandas:** Analyze test result. -- **matplotlib**: Plot the charts for report. +- **matplotlib**: (Either ``matplotlib`` or ``matplotlib-base``) Plot the charts for report. - **py-cpuinfo**: Get the CPU model for report.