Package: crystaldiskmark-parser
CrystalDiskMark-ParserRead in the .txt files generated byCrystalDiskMarkin Python.
This library was created to enable visualizing the benchmark results ofCrystalDiskMarkin an automated way.
The following plot created withmatplotlibis an example.Installpipinstallcrystaldiskmark-parserSeehttps://pypi.org/project/crystaldiskmark-parser/for more information.OverviewThe library provides two Functions:parse(filepath): Returns parsed data as acrystaldiskmark_parser.BenchmarkResultparse_df(filepath): Returns parsed data as apandas.DataFrameUsingparse_df(filepath)is recommended.Example 0: Print DataFramefromcrystaldiskmark_parser.parserimportparse_dfdf=parse_df("../test/data/CrystalDiskMark_20210622162528 WD Blue 3D 1TB WDS100T2B0A.txt")print(df)Outputdate                              test  \
0  2021/06/22 17:19:21  1 GiB (x5) [E: 96% (894/932GiB)]   
1  2021/06/22 17:19:21  1 GiB (x5) [E: 96% (894/932GiB)]   
2  2021/06/22 17:19:21  1 GiB (x5) [E: 96% (894/932GiB)]   
3  2021/06/22 17:19:21  1 GiB (x5) [E: 96% (894/932GiB)]   
4  2021/06/22 17:19:21  1 GiB (x5) [E: 96% (894/932GiB)]   
5  2021/06/22 17:19:21  1 GiB (x5) [E: 96% (894/932GiB)]   
6  2021/06/22 17:19:21  1 GiB (x5) [E: 96% (894/932GiB)]   
7  2021/06/22 17:19:21  1 GiB (x5) [E: 96% (894/932GiB)]   

                             time                                    os  \
0  Measure 5 sec / Interval 5 sec  Windows 10  [10.0 Build 19042] (x64)   
1  Measure 5 sec / Interval 5 sec  Windows 10  [10.0 Build 19042] (x64)   
2  Measure 5 sec / Interval 5 sec  Windows 10  [10.0 Build 19042] (x64)   
3  Measure 5 sec / Interval 5 sec  Windows 10  [10.0 Build 19042] (x64)   
4  Measure 5 sec / Interval 5 sec  Windows 10  [10.0 Build 19042] (x64)   
5  Measure 5 sec / Interval 5 sec  Windows 10  [10.0 Build 19042] (x64)   
6  Measure 5 sec / Interval 5 sec  Windows 10  [10.0 Build 19042] (x64)   
7  Measure 5 sec / Interval 5 sec  Windows 10  [10.0 Build 19042] (x64)   

      mode  profile         comment read_write type  blocksize unit_blocksize  \
0  [Admin]  Default  WD Blue 3D 1TB       read  SEQ        1.0            MiB   
1  [Admin]  Default  WD Blue 3D 1TB       read  SEQ        1.0            MiB   
2  [Admin]  Default  WD Blue 3D 1TB       read  RND        4.0            KiB   
3  [Admin]  Default  WD Blue 3D 1TB       read  RND        4.0            KiB   
4  [Admin]  Default  WD Blue 3D 1TB      write  SEQ        1.0            MiB   
5  [Admin]  Default  WD Blue 3D 1TB      write  SEQ        1.0            MiB   
6  [Admin]  Default  WD Blue 3D 1TB      write  RND        4.0            KiB   
7  [Admin]  Default  WD Blue 3D 1TB      write  RND        4.0            KiB   

  queues threads     rate unit_rate     iops unit_iops   latency unit_latency  
0      8       1  531.458      MB/s    506.8      IOPS  15726.77           us  
1      1       1  489.838      MB/s    467.1      IOPS   2139.41           us  
2     32       1  269.406      MB/s  65772.9      IOPS    470.58           us  
3      1       1   41.375      MB/s  10101.3      IOPS     98.75           us  
4      8       1  498.656      MB/s    475.6      IOPS  16750.30           us  
5      1       1  485.163      MB/s    462.7      IOPS   2159.18           us  
6     32       1  222.691      MB/s  54367.9      IOPS    583.02           us  
7      1       1   99.900      MB/s  24389.6      IOPS     40.73           usExample 1: Visualize benchmark resultsfromcrystaldiskmark_parser.parserimportparse_dfimportmatplotlib.pyplotasplt#  Read to one big DataFramedf=parse_df("../test/data/CrystalDiskMark_20210622162528 WD Blue 3D 1TB WDS100T2B0A.txt")df=df.append(parse_df("../test/data/CrystalDiskMark_20210622163451 SAMSUNG 840 EVO 120GB.txt"))df=df.append(parse_df("../test/data/CrystalDiskMark_20210626154221 Crucial P2 2TB on H87M.txt"))df=df.append(parse_df("../test/data/CrystalDiskMark_20210627172807 Crucial P2 2TB on X370.txt"))print(df.info())# Extract relevant rowsdata=df.loc[(df['read_write']=='read')&(df['type']=="SEQ")&(df['queues']==8)&(df['blocksize']==1)&(df['unit_blocksize']=="MiB")]# Extract relevant columnsrate=data["rate"]name=data["comment"]# Plotfig,ax=plt.subplots(figsize=(10,6))con=ax.bar(name,rate)ax.bar_label(con)ax.set_title("Sequential Read Rate of SSDs")ax.set_ylabel("Rate [MB/s]")plt.show()Example 2: Visualize benchmark results (extended)fromcrystaldiskmark_parser.parserimportparse_dfimportmatplotlib.pyplotasplt#%%# Read to one big DataFramedf=parse_df("../test/data/CrystalDiskMark_20210622162528 WD Blue 3D 1TB WDS100T2B0A.txt")df=df.append(parse_df("../test/data/CrystalDiskMark_20210622163451 SAMSUNG 840 EVO 120GB.txt"))df=df.append(parse_df("../test/data/CrystalDiskMark_20210626154221 Crucial P2 2TB on H87M.txt"))df=df.append(parse_df("../test/data/CrystalDiskMark_20210627172807 Crucial P2 2TB on X370.txt"))# Extract relevant rowsdata=df.loc[(df['read_write']=='read')&(df['type']=="SEQ")&(df['queues']==8)&(df['blocksize']==1)&(df['unit_blocksize']=="MiB")]# Extract relevant columnsread_rate=data["rate"]read_rate=[round(x)forxinread_rate]name=data["comment"]# Extract relevant rowsdata=df.loc[(df['read_write']=='write')&(df['type']=="SEQ")&(df['queues']==8)&(df['blocksize']==1)&(df['unit_blocksize']=="MiB")]# Extract relevant columnswrite_rate=data["rate"]write_rate=[round(x)forxinwrite_rate]# Plotfig,ax=plt.subplots(figsize=(12,6))con=ax.bar([float(x)+.25forxinrange(len(read_rate))],read_rate,tick_label=name,width=0.25)ax.bar_label(con)con=ax.bar([float(x)+.5forxinrange(len(write_rate))],write_rate,width=0.25)ax.bar_label(con)ax.set_title("Sequential Read & Write Rate of SSDs")ax.set_ylabel("Rate [MB/s]")plt.legend(["read","write"])plt.show()Example 3: Visualize benchmark results (without DataFrame)fromcrystaldiskmark_parser.parserimportparseimportmatplotlib.pyplotaspltresult_wd=parse("data/CrystalDiskMark_20210622162528 WD Blue 3D 1TB WDS100T2B0A.txt")result_samsung=parse("data/CrystalDiskMark_20210622163451 SAMSUNG 840 EVO 120GB.txt")data=[result_wd.read_results[0].rate,result_samsung.read_results[0].rate]fig,ax=plt.subplots(figsize=(10,6))con=ax.bar(["WD Blue 3D 1TB","Samsung 840 EVO 120GB"],data)ax.bar_label(con)ax.set_title("Read Rate of SSDs")ax.set_ylabel(f"Rate [{result_wd.read_results[0].unit_rate}]")plt.show()Example 4: Print BenchmarkResultfromcrystaldiskmark_parser.parserimportparsebenchmark_result=parse("data/CrystalDiskMark_20210622162528 WD Blue 3D 1TB WDS100T2B0A.txt")print(benchmark_result)OutputBenchmarkResult({'test': '1 GiB (x5) [E: 96% (894/932GiB)]', 'date': '2021/06/22 17:19:21', 'os': 'Windows 10  [10.0 Build 19042] (x64)', 'profile': 'Default', 'time': 'Measure 5 sec / Interval 5 sec', 'mode': '[Admin]', 'comment': 'WD Blue 3D 1TB WDS100T2B0A', 'write_results': [TestResult({'test_type': 'SEQ', 'block_size': 1.0, 'unit_block_size': 'MiB', 'queues': 8, 'threads': 1, 'rate': 498.656, 'unit_rate': 'MB/s', 'iops': 475.6, 'unit_iops': 'IOPS', 'latency': 16750.3, 'unit_latency': 'us'}), TestResult({'test_type': 'SEQ', 'block_size': 1.0, 'unit_block_size': 'MiB', 'queues': 1, 'threads': 1, 'rate': 485.163, 'unit_rate': 'MB/s', 'iops': 462.7, 'unit_iops': 'IOPS', 'latency': 2159.18, 'unit_latency': 'us'}), TestResult({'test_type': 'RND', 'block_size': 4.0, 'unit_block_size': 'KiB', 'queues': 32, 'threads': 1, 'rate': 222.691, 'unit_rate': 'MB/s', 'iops': 54367.9, 'unit_iops': 'IOPS', 'latency': 583.02, 'unit_latency': 'us'}), TestResult({'test_type': 'RND', 'block_size': 4.0, 'unit_block_size': 'KiB', 'queues': 1, 'threads': 1, 'rate': 99.9, 'unit_rate': 'MB/s', 'iops': 24389.6, 'unit_iops': 'IOPS', 'latency': 40.73, 'unit_latency': 'us'})], 'read_results': [TestResult({'test_type': 'SEQ', 'block_size': 1.0, 'unit_block_size': 'MiB', 'queues': 8, 'threads': 1, 'rate': 531.458, 'unit_rate': 'MB/s', 'iops': 506.8, 'unit_iops': 'IOPS', 'latency': 15726.77, 'unit_latency': 'us'}), TestResult({'test_type': 'SEQ', 'block_size': 1.0, 'unit_block_size': 'MiB', 'queues': 1, 'threads': 1, 'rate': 489.838, 'unit_rate': 'MB/s', 'iops': 467.1, 'unit_iops': 'IOPS', 'latency': 2139.41, 'unit_latency': 'us'}), TestResult({'test_type': 'RND', 'block_size': 4.0, 'unit_block_size': 'KiB', 'queues': 32, 'threads': 1, 'rate': 269.406, 'unit_rate': 'MB/s', 'iops': 65772.9, 'unit_iops': 'IOPS', 'latency': 470.58, 'unit_latency': 'us'}), TestResult({'test_type': 'RND', 'block_size': 4.0, 'unit_block_size': 'KiB', 'queues': 1, 'threads': 1, 'rate': 41.375, 'unit_rate': 'MB/s', 'iops': 10101.3, 'unit_iops': 'IOPS', 'latency': 98.75, 'unit_latency': 'us'})]})DevelopmentBuild and Publish a New ReleaseThis repository has a CI/CD pipeline that handle building and publishing the package to PyPI.
It is triggered by a commit or pull request onmainbranch.Steps to publish a new release:Check if the version number is correctly set insetup.pyand__init__.pyCreate a new pull request fromdevbranch tomainbranch. Accept this pull requestWait until the pipeline endsCheck if package is available onPyPICreate a newReleasein GitHub. Set the version number as the name