Skip to content

inspector

sc_crawler.inspector #

Helpers to load the hardware discovery and benchmarking data from the sc-inspector-data repository.

Functions:

Name Description
inspector_data_path

Return a subpath under the cached inspector data root.

inspect_server_benchmarks

Generate a list of BenchmarkScore-like dicts for the Server.

inspect_database_benchmarks

Generate a list of BenchmarkScore-like dicts for the Database.

inspect_update_server_dict

Update a Server-like dict based on inspector data.

inspector_data_path #

inspector_data_path(dir_name='data')

Return a subpath under the cached inspector data root.

Source code in sc_crawler/inspector.py
def inspector_data_path(dir_name: str = "data") -> str | PathLike:
    """Return a subpath under the cached inspector data root."""
    return path.join(_inspector_data_root(), dir_name)

inspect_server_benchmarks #

inspect_server_benchmarks(server)

Generate a list of BenchmarkScore-like dicts for the Server.

Source code in sc_crawler/inspector.py
 473
 474
 475
 476
 477
 478
 479
 480
 481
 482
 483
 484
 485
 486
 487
 488
 489
 490
 491
 492
 493
 494
 495
 496
 497
 498
 499
 500
 501
 502
 503
 504
 505
 506
 507
 508
 509
 510
 511
 512
 513
 514
 515
 516
 517
 518
 519
 520
 521
 522
 523
 524
 525
 526
 527
 528
 529
 530
 531
 532
 533
 534
 535
 536
 537
 538
 539
 540
 541
 542
 543
 544
 545
 546
 547
 548
 549
 550
 551
 552
 553
 554
 555
 556
 557
 558
 559
 560
 561
 562
 563
 564
 565
 566
 567
 568
 569
 570
 571
 572
 573
 574
 575
 576
 577
 578
 579
 580
 581
 582
 583
 584
 585
 586
 587
 588
 589
 590
 591
 592
 593
 594
 595
 596
 597
 598
 599
 600
 601
 602
 603
 604
 605
 606
 607
 608
 609
 610
 611
 612
 613
 614
 615
 616
 617
 618
 619
 620
 621
 622
 623
 624
 625
 626
 627
 628
 629
 630
 631
 632
 633
 634
 635
 636
 637
 638
 639
 640
 641
 642
 643
 644
 645
 646
 647
 648
 649
 650
 651
 652
 653
 654
 655
 656
 657
 658
 659
 660
 661
 662
 663
 664
 665
 666
 667
 668
 669
 670
 671
 672
 673
 674
 675
 676
 677
 678
 679
 680
 681
 682
 683
 684
 685
 686
 687
 688
 689
 690
 691
 692
 693
 694
 695
 696
 697
 698
 699
 700
 701
 702
 703
 704
 705
 706
 707
 708
 709
 710
 711
 712
 713
 714
 715
 716
 717
 718
 719
 720
 721
 722
 723
 724
 725
 726
 727
 728
 729
 730
 731
 732
 733
 734
 735
 736
 737
 738
 739
 740
 741
 742
 743
 744
 745
 746
 747
 748
 749
 750
 751
 752
 753
 754
 755
 756
 757
 758
 759
 760
 761
 762
 763
 764
 765
 766
 767
 768
 769
 770
 771
 772
 773
 774
 775
 776
 777
 778
 779
 780
 781
 782
 783
 784
 785
 786
 787
 788
 789
 790
 791
 792
 793
 794
 795
 796
 797
 798
 799
 800
 801
 802
 803
 804
 805
 806
 807
 808
 809
 810
 811
 812
 813
 814
 815
 816
 817
 818
 819
 820
 821
 822
 823
 824
 825
 826
 827
 828
 829
 830
 831
 832
 833
 834
 835
 836
 837
 838
 839
 840
 841
 842
 843
 844
 845
 846
 847
 848
 849
 850
 851
 852
 853
 854
 855
 856
 857
 858
 859
 860
 861
 862
 863
 864
 865
 866
 867
 868
 869
 870
 871
 872
 873
 874
 875
 876
 877
 878
 879
 880
 881
 882
 883
 884
 885
 886
 887
 888
 889
 890
 891
 892
 893
 894
 895
 896
 897
 898
 899
 900
 901
 902
 903
 904
 905
 906
 907
 908
 909
 910
 911
 912
 913
 914
 915
 916
 917
 918
 919
 920
 921
 922
 923
 924
 925
 926
 927
 928
 929
 930
 931
 932
 933
 934
 935
 936
 937
 938
 939
 940
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
def inspect_server_benchmarks(server: "Server") -> List[dict]:
    """Generate a list of BenchmarkScore-like dicts for the Server."""
    benchmarks = []

    framework = "bogomips"
    try:
        benchmarks.append(
            {
                **_benchmark_metafields(
                    server, framework="lscpu", benchmark_id=framework
                ),
                "score": round(float(_server_lscpu_field(server, "BogoMIPS:"))),
            }
        )
    except Exception as e:
        _log_cannot_load_benchmarks(server, framework, e)

    framework = "bw_mem"
    try:
        with open(_server_framework_stdout_path(server, framework), "r") as lines:
            for line in lines:
                # filter out error messages
                if match(r"^(rd|wr|rdwr) \d+(\.\d+) \d+(\.\d+)$", line):
                    row = line.strip().split()
                    benchmarks.append(
                        {
                            **_benchmark_metafields(server, framework=framework),
                            "config": {"operation": row[0], "size": float(row[1])},
                            "score": float(row[2]),
                        }
                    )
    except Exception as e:
        _log_cannot_load_benchmarks(server, framework, e)

    framework = "compression_text"
    try:
        algos = _server_framework_stdout_from_json(server, framework)
        for algo, levels in algos.items():
            for level, datas in levels.items():
                for data in datas:
                    threads = data["threads"]
                    config = {
                        "algo": algo,
                        "compression_level": None if level == "null" else int(level),
                        "cores": (
                            Parallelism.MULTI if threads > 1 else Parallelism.SINGLE
                        ),
                    }
                    if data.get("extra_args", {}).get("block_size") is not None:
                        config["block_size"] = data["extra_args"]["block_size"]
                    for measurement in ["ratio", "compress", "decompress"]:
                        if data[measurement]:
                            benchmarks.append(
                                {
                                    **_benchmark_metafields(
                                        server,
                                        benchmark_id=":".join([framework, measurement]),
                                    ),
                                    "config": config,
                                    "score": float(data[measurement]),
                                }
                            )
    except Exception as e:
        _log_cannot_load_benchmarks(server, framework, e, True)

    framework = "geekbench"
    try:
        with open(_server_framework_path(server, framework, "results.json"), "r") as fp:
            scores = json.load(fp)
        kernel_version = next(
            (
                m.group(1)
                for line in _server_geekbench(server)
                if line.startswith("Kernel")
                for m in [search(r"(\d+\.\d+\.\d+\S*)", line)]
                if m
            ),
            None,
        )
        for cores, workloads in scores.items():
            parallelism = (
                Parallelism.SINGLE
                if cores == "Single-Core Performance"
                else Parallelism.MULTI
            )
            for workload, values in workloads.items():
                workload_fields = {
                    "config": {"cores": parallelism},
                    "score": float(values["score"]),
                }
                if values.get("description"):
                    workload_fields["note"] = values["description"]
                benchmarks.append(
                    {
                        **_benchmark_metafields(
                            server,
                            benchmark_id=":".join(
                                [framework, sub(r"\W+", "_", workload.lower())]
                            ),
                            kernel_version_fallback=kernel_version,
                        ),
                        **workload_fields,
                    }
                )
    except Exception as e:
        _log_cannot_load_benchmarks(server, framework, e, True)

    framework = "passmark"
    try:
        with open(_server_framework_stdout_path(server, framework), "r") as fp:
            scores = yaml_safe_load(fp)
        passmark_version = ".".join(
            [str(scores["Version"][i]) for i in ["Major", "Minor", "Build"]]
        )
        kernel_version = next(
            (
                line.split("Kernel:")[1].strip()
                for line in _server_passmark(server)
                if line.startswith("Kernel:")
            ),
            None,
        )
        for key, name in PASSMARK_MAPS.items():
            benchmarks.append(
                {
                    **_benchmark_metafields(
                        server,
                        benchmark_id=":".join(
                            [framework, sub(r"\W+", "_", name.lower())]
                        ),
                        framework_version_fallback=passmark_version,
                        kernel_version_fallback=kernel_version,
                    ),
                    "score": float(scores["Results"][key]),
                }
            )
    except Exception as e:
        _log_cannot_load_benchmarks(server, framework, e, True)

    framework = "openssl"
    try:
        with open(_server_framework_path(server, framework, "parsed.json"), "r") as fp:
            workloads = json.load(fp)
        for workload in workloads:
            benchmarks.append(
                {
                    **_benchmark_metafields(server, framework=framework),
                    "config": {
                        "algo": workload["algo"],
                        "block_size": workload["block_size"],
                    },
                    "score": float(workload["speed"]),
                }
            )
    except Exception as e:
        _log_cannot_load_benchmarks(server, framework, e, True)

    framework = "stress_ng"
    # TODO deprecate
    try:
        cores_per_path = {"stressng": server.vcpus, "stressngsinglecore": 1}
        for cores_path in cores_per_path.keys():
            line = _extract_line_from_file(
                _server_framework_stderr_path(server, cores_path),
                "bogo-ops-per-second-real-time",
            )
            benchmarks.append(
                {
                    **_benchmark_metafields(
                        server,
                        framework=cores_path,
                        benchmark_id=":".join([framework, "cpu_all"]),
                    ),
                    "config": {"cores": cores_per_path[cores_path]},
                    "score": float(line.split(": ")[1]),
                }
            )
    except Exception:
        # backfill with newer method - can be dropped once we deprecate stress_ng:cpu_all
        try:
            records = []
            with open(
                _server_framework_stdout_path(server, "stressngfull"), newline=""
            ) as f:
                rows = csv.reader(f, quoting=csv.QUOTE_NONNUMERIC)
                for row in rows:
                    records.append(row)
            for i in [0, len(records) - 1]:
                benchmarks.append(
                    {
                        **_benchmark_metafields(
                            server,
                            framework="stressngfull",
                            benchmark_id=":".join([framework, "cpu_all"]),
                        ),
                        "config": {"cores": records[i][0]},
                        "score": records[i][1],
                    }
                )
        except Exception as e:
            _log_cannot_load_benchmarks(server, framework, e, True)

    workload = "div16"
    try:
        records = []
        with open(
            _server_framework_stdout_path(server, "stressngfull"), newline=""
        ) as f:
            rows = csv.reader(f, quoting=csv.QUOTE_NONNUMERIC)
            for row in rows:
                records.append(row)
        for record in records:
            benchmarks.append(
                {
                    **_benchmark_metafields(
                        server,
                        framework="stressngfull",
                        benchmark_id=":".join([framework, workload]),
                    ),
                    "config": {"cores": record[0]},
                    "score": record[1],
                }
            )
        # best single and multi core performance
        bests = {"best1": records[0][1], "bestn": max([r[1] for r in records])}
        for k, v in bests.items():
            benchmarks.append(
                {
                    **_benchmark_metafields(
                        server,
                        framework="stressngfull",
                        benchmark_id=":".join([framework, k]),
                    ),
                    "score": v,
                }
            )
    except Exception as e:
        _log_cannot_load_benchmarks(server, framework, e, True)

    for framework in SERVER_CLIENT_FRAMEWORK_MAPS.keys():
        try:
            versions = _server_framework_meta(server, framework)["version"]
            # drop the build number at the end of the redis server version
            if framework == "redis":
                versions = sub(r" build=[a-zA-Z0-9]+", "", versions)

            records = []
            with open(
                _server_framework_stdout_path(server, framework), newline=""
            ) as f:
                rows = csv.DictReader(f, quoting=csv.QUOTE_NONNUMERIC)
                for row in rows:
                    if "connections" in row.keys():
                        row["connections_per_vcpus"] = row["connections"] / server.vcpus
                    records.append(row)

            framework_config = SERVER_CLIENT_FRAMEWORK_MAPS[framework]
            keys = framework_config["keys"]
            measurements = framework_config["measurements"]

            # don't care about threads, keep the records with the highest rps
            records = sorted(records, key=lambda x: (*[x[k] for k in keys], -x["rps"]))
            records = groupby(records, key=itemgetter(*keys))
            records = [next(group) for _, group in records]

            for record in records:
                for measurement in measurements:
                    score_field = measurement.split("-")[0]
                    if score_field == "throughput":
                        score_field = "rps"
                    score = record[score_field]
                    server_usrsys = record["server_usr"] + record["server_sys"]
                    client_usrsys = record["client_usr"] + record["client_sys"]
                    note = (
                        "CPU usage (server/client usr+sys): "
                        f"{round(server_usrsys, 4)}/{round(client_usrsys, 4)}."
                    )
                    if measurement.endswith("-extrapolated"):
                        note += f" Original RPS: {score}."
                        score = round(
                            score / server_usrsys * (server_usrsys + client_usrsys), 2
                        )
                    if measurement.startswith("throughput"):
                        # drop the "k" suffix and multiply by 1024
                        size = int(record["size"][:-1]) * 1024
                        score = score * size
                    benchmarks.append(
                        {
                            **_benchmark_metafields(
                                server,
                                framework=framework,
                                benchmark_id=":".join([framework, measurement]),
                                framework_version_fallback=versions,
                                override_framework_version=True,
                            ),
                            "config": {k: record[k] for k in keys},
                            "score": score,
                            "note": note,
                        }
                    )
        except Exception as e:
            _log_cannot_load_benchmarks(server, framework, e, True)

    framework = "membench"
    try:
        with open(_server_framework_stdout_path(server, framework), newline="") as fp:
            reader = csv.DictReader(fp)
            ram_scope_dict = {
                "read": False,
                "write": False,
                "copy": False,
                "latency": False,
            }
            cpu_cache_total_kib = (
                server.cpu_l3_cache_total
                or server.cpu_l2_cache_total
                or server.cpu_l1d_cache_total
            )
            for row in reader:
                operation = row["operation"]
                size_kb = int(float(row["size_kb"]))
                config = {"size_kb": size_kb}
                if operation == "latency":
                    score = float(row["latency_ns"])
                    if score == 0:
                        continue
                    benchmarks.append(
                        {
                            **_benchmark_metafields(
                                server,
                                framework=framework,
                                benchmark_id="membench:latency",
                            ),
                            "config": config,
                            "score": score,
                        }
                    )
                    if (
                        cpu_cache_total_kib is not None
                        and size_kb > cpu_cache_total_kib
                        and not ram_scope_dict[operation]
                    ):
                        ram_scope_dict[operation] = True
                        benchmarks.append(
                            {
                                **_benchmark_metafields(
                                    server,
                                    framework=framework,
                                    benchmark_id="membench:latency",
                                ),
                                "config": {"scope": "RAM"},
                                "score": score,
                            }
                        )
                elif operation in ("read", "write", "copy"):
                    score = float(row["bandwidth_mb_s"])
                    if score == 0:
                        continue
                    benchmarks.append(
                        {
                            **_benchmark_metafields(
                                server,
                                framework=framework,
                                benchmark_id=f"membench:bandwidth_{operation}",
                            ),
                            "config": config,
                            "score": score,
                        }
                    )
                    if (
                        cpu_cache_total_kib is not None
                        and size_kb > cpu_cache_total_kib
                        and not ram_scope_dict[operation]
                    ):
                        ram_scope_dict[operation] = True
                        benchmarks.append(
                            {
                                **_benchmark_metafields(
                                    server,
                                    framework=framework,
                                    benchmark_id=f"membench:bandwidth_{operation}",
                                ),
                                "config": {"scope": "RAM"},
                                "score": score,
                            }
                        )
    except Exception as e:
        _log_cannot_load_benchmarks(server, framework, e, True)

    framework = "llm_speed"
    try:
        assert _server_framework_meta(server, "llm")["exit_code"] == 0
        with open(_server_framework_stdout_path(server, "llm"), "r") as fp:
            for line in fp:
                record = json.loads(line)
                model_name = path.basename(record.get("model_filename", "unknown"))
                measurement = "text_generation"
                if record.get("n_prompt") != 0:
                    measurement = "prompt_processing"
                tokens = record.get("n_prompt") + record.get("n_gen")
                config = {
                    "model": model_name,
                    "tokens": tokens,
                }
                benchmarks.append(
                    {
                        **_benchmark_metafields(
                            server,
                            framework="llm",
                            benchmark_id=":".join([framework, measurement]),
                        ),
                        "config": config,
                        "score": float(record["avg_ts"]),
                    }
                )
    except Exception as e:
        _log_cannot_load_benchmarks(server, framework, e, True)

    framework = "nvbandwidth"
    try:
        with open(_server_framework_stdout_path(server, framework), "r") as fp:
            data = json.load(fp)["nvbandwidth"]
        # aborted runs (e.g. failed pattern check) still list the testcases
        # passed before the error, but the whole run is untrustworthy
        if data.get("error"):
            raise ValueError(f"nvbandwidth run aborted: {data['error']}")
        passed = {
            testcase["name"]: testcase
            for testcase in data["testcases"]
            if testcase.get("status") == "Passed"
        }
        gpu_count = 0
        for testcase in passed.values():
            matrix = testcase.get("bandwidth_matrix")
            if matrix:
                # columns are GPUs in all testcases (rows can be stacked blocks)
                gpu_count = max(gpu_count, len(matrix[0]))
        extend_environment = {
            "driver_version": data["Driver Version"],
            "cuda_version": data["CUDA Runtime Version"],
            "nvbandwidth_version": data["version"],
            "gpu_count": gpu_count,
            "p2p_supported": any(
                name.startswith("device_to_device_") for name in passed
            ),
        }
        for metric, source in (
            ("host_to_gpu", "host_to_all_memcpy_ce"),
            ("gpu_to_host", "all_to_host_memcpy_ce"),
        ):
            if source not in passed:
                continue
            benchmarks.append(
                {
                    **_benchmark_metafields(
                        server,
                        framework=framework,
                        benchmark_id=":".join([framework, "all", metric]),
                        extend_environment=extend_environment,
                    ),
                    "score": passed[source]["sum"],
                }
            )
        duplex_sources = [
            "host_to_all_bidirectional_memcpy_ce",
            "all_to_host_bidirectional_memcpy_ce",
        ]
        if all(source in passed for source in duplex_sources):
            benchmarks.append(
                {
                    **_benchmark_metafields(
                        server,
                        framework=framework,
                        benchmark_id=":".join([framework, "all", "duplex"]),
                        extend_environment=extend_environment,
                    ),
                    "score": min(passed[source]["sum"] for source in duplex_sources),
                }
            )
        for group, metric, source in (
            ("slot", "host_to_gpu", "host_to_device_memcpy_ce"),
            ("slot", "gpu_to_host", "device_to_host_memcpy_ce"),
            ("slot", "local_copy", "device_local_copy"),
            ("slot", "latency", "host_device_latency_sm"),
            ("p2p", "single", "device_to_device_memcpy_write_ce"),
            ("p2p", "duplex", "device_to_device_bidirectional_memcpy_write_ce"),
            ("p2p", "latency", "device_to_device_latency_sm"),
            ("p2p", "gather", "all_to_one_write_ce"),
        ):
            testcase = passed.get(source)
            if testcase is None:
                continue
            matrix = testcase["bandwidth_matrix"]
            directions = 1
            if len(matrix) == 3 * len(matrix[0]):
                # v0.6+ bidirectional tests stack the two directional blocks and
                # their Total, with `sum` covering only the Total block
                matrix = matrix[-len(matrix[0]) :]
                directions = 2
            elif len(matrix) > len(matrix[0]):
                _log_cannot_load_benchmarks(
                    server,
                    ":".join([framework, group, metric]),
                    f"unexpected {len(matrix)}x{len(matrix[0])} matrix",
                )
                continue
            cell_count = sum(cell != "N/A" for row in matrix for cell in row)
            if not cell_count:
                continue
            benchmarks.append(
                {
                    **_benchmark_metafields(
                        server,
                        framework=framework,
                        benchmark_id=":".join([framework, group, metric]),
                        extend_environment=extend_environment,
                    ),
                    "score": testcase["sum"] / cell_count / directions,
                }
            )
        sm = passed.get("host_to_device_memcpy_sm")
        ce = passed.get("host_to_device_memcpy_ce")
        if sm is not None and ce is not None and ce["sum"]:
            benchmarks.append(
                {
                    **_benchmark_metafields(
                        server,
                        framework=framework,
                        benchmark_id=":".join([framework, "efficiency", "sm_ce_ratio"]),
                        extend_environment=extend_environment,
                    ),
                    "score": min(sm["sum"] / ce["sum"], 1.0),
                }
            )
    except Exception as e:
        _log_cannot_load_benchmarks(server, framework, e, True)

    framework = "stressng_benchmarks"
    try:
        # lookup imports tables, which imports this module
        from .lookup import stress_ng_benchmark_id

        stressors = _server_framework_stdout_from_json(server, framework)["stressors"]
        benchmark_metafields = _benchmark_metafields(
            server, framework=framework, benchmark_id=framework
        )
        for method, by_cores in stressors.items():
            try:
                benchmark_id = stress_ng_benchmark_id(method)
                if benchmark_id is None:
                    _log_cannot_load_benchmarks(
                        server, framework, f"unknown stressor {method!r}"
                    )
                    continue
                for cores, result in by_cores.items():
                    if result is None:
                        continue
                    score = float(result["bogo_ops_per_sec"])
                    if score <= 0:
                        continue
                    cores_count = int(cores)
                    row = {
                        **benchmark_metafields,
                        "benchmark_id": benchmark_id,
                        "config": {
                            "cores": (
                                Parallelism.SINGLE
                                if cores_count == 1
                                else Parallelism.MULTI
                            )
                        },
                        "score": score,
                    }
                    benchmarks.append(row)
                    # 1-vCPU servers only have a "1" key, also emit MULTI so
                    # cores=multi filters still include them
                    if cores_count == 1 and server.vcpus == 1:
                        benchmarks.append(
                            {
                                **row,
                                "config": {"cores": Parallelism.MULTI},
                            }
                        )
            except Exception as e:
                _log_cannot_load_benchmarks(server, f"{framework}:{method}", e, True)
    except Exception as e:
        _log_cannot_load_benchmarks(server, framework, e, True)

    # add server related pgbench benchmark scores
    benchmarks.extend(_pgbench_benchmark_scores(server))

    return benchmarks

inspect_database_benchmarks #

inspect_database_benchmarks(database)

Generate a list of BenchmarkScore-like dicts for the Database.

Source code in sc_crawler/inspector.py
def inspect_database_benchmarks(database: "Database") -> list[dict]:
    """Generate a list of BenchmarkScore-like dicts for the Database."""
    return _pgbench_benchmark_scores(
        database, framework_path_postfix="_postgres_dbaas_ro_durable"
    )

inspect_update_server_dict #

inspect_update_server_dict(server)

Update a Server-like dict based on inspector data.

Source code in sc_crawler/inspector.py
def inspect_update_server_dict(server: dict) -> dict:
    """Update a Server-like dict based on inspector data."""
    server_obj = ServerBase.model_validate(server)

    lookups = {
        "dmidecode_cpu": lambda: _server_dmidecode_section(
            server_obj, "Processor Information"
        ),
        "dmidecode_cpus": lambda: _server_dmidecode_sections(
            server_obj, "Processor Information"
        ),
        "dmidecode_memory": lambda: _server_dmidecode_section(
            server_obj, "Memory Device"
        ),
        "dmidecode_memory_devices": lambda: _server_dmidecode_sections(
            server_obj, "Memory Device"
        ),
        "lscpu": lambda: _server_lscpu(server_obj),
        "lshw": lambda: _server_lshw(server_obj),
        "lstopo": lambda: _server_lstopo(server_obj),
        "lsblk": lambda: _server_lsblk(server_obj),
        "lsblk_discard": lambda: _server_lsblk_discard(server_obj),
        "lsblk_topo": lambda: _server_lsblk_topo(server_obj),
        "nvidiasmi": lambda: _server_nvidiasmi(server_obj),
        "virtualization": lambda: _server_virtualization(server_obj),
        "stressngfull": lambda: _server_stressngfull(server_obj),
        "gpu": lambda: lookups["nvidiasmi"].find("gpu"),
        "gpus": lambda: lookups["nvidiasmi"].findall("gpu"),
    }
    for k, f in lookups.items():
        try:
            lookups[k] = f()
        except Exception as e:
            lookups[k] = Exception(str(e))

    def lscpu_lookup(field: str):
        return _listsearch(lookups["lscpu"], "field", field)["data"]

    # Parse lsblk/lshw storage info once (default StorageInfo instance if lsblk/lshw lookup failed)
    inspector_storage_info = _parse_storage_info(
        lookups["lshw"],
        lookups["lsblk"],
        lookups["lsblk_discard"],
        lookups["lsblk_topo"],
        server_obj,
    )

    # Parse CPU cache info once (empty dict if lscpu lookup failed)
    cpu_cache_info = (
        {}
        if isinstance(lookups["lscpu"], Exception)
        else _get_cpu_cache_info(lookups["lscpu"], lookups["lstopo"])
    )

    def get_cpu_speed():
        """Extract CPU speed from lscpu or dmidecode."""
        # lscpu is more reliable, extracting from "Model name: ... @ X.XGHz"
        speed = None
        with suppress(Exception):
            cpu_model = lscpu_lookup("Model name:")
            match = search(r" @ ([0-9\.]*)GHz$", cpu_model)
            if match:
                speed = float(match.group(1))
        # fall back to dmidecode
        if not speed:
            with suppress(Exception):
                # use 1st CPU's speed, convert to Ghz
                speed = lookups["dmidecode_cpu"]["Max Speed"] / 1e9
        # 2 GHz CPU speed is a lie at GCP
        if server_obj.vendor_id == "gcp" and speed == 2:
            speed = None
        return speed

    def get_cpu_manufacturer():
        """Extract CPU manufacturer from lscpu or dmidecode."""
        with suppress(Exception):
            cpu_model = lscpu_lookup("Model name:")
            for manufacturer in ["Intel", "AMD", "Ampere"]:
                if manufacturer.lower() in cpu_model.lower():
                    return manufacturer
        # fall back to dmidecode
        with suppress(Exception):
            return _standardize_manufacturer(lookups["dmidecode_cpu"]["Manufacturer"])
        # no CPU manufacturer data available
        return None

    def get_cpu_family():
        """Extract CPU family from lscpu or dmidecode."""
        with suppress(Exception):
            cpu_model = lscpu_lookup("Model name:")
            for family in ["Xeon", "EPYC", "Altra"]:
                if family.lower() in cpu_model.lower():
                    return family
        # fall back to dmidecode
        with suppress(Exception):
            return _standardize_cpu_family(lookups["dmidecode_cpu"]["Family"])
        # no CPU family data available
        return None

    def get_cpu_model():
        """Extract CPU model from lscpu or dmidecode."""
        with suppress(Exception):
            return _standardize_cpu_model(lscpu_lookup("Model name:"))
        with suppress(Exception):
            return _standardize_cpu_model(lookups["dmidecode_cpu"]["Version"])
        return None

    def calculate_ecpus():
        """Calculate ecpus from stressngfull."""
        stressngfull = lookups.get("stressngfull")
        if (
            isinstance(stressngfull, Exception)
            or not isinstance(stressngfull, list)
            or not stressngfull
        ):
            return None
        with suppress(Exception):
            best1_score = stressngfull[0][1]
            bestn_score = max(score for _, score in stressngfull)
            return round(bestn_score / best1_score, 1)
        return None

    def get_memory_amount_actual():
        """Extract memory amount from lstopo, lshw or dmidecode."""
        return (
            _parse_lstopo_memory_amount_mib(lookups["lstopo"])
            or _parse_lshw_memory_amount_mib(lookups["lshw"])
            or _parse_dmidecode_memory_amount_mib(lookups["dmidecode_memory_devices"])
            or None
        )

    mappings = {
        "vcpus": lambda: int(lscpu_lookup("CPU(s):")),
        "cpu_cores": lambda: (
            int(lscpu_lookup("Core(s) per socket:")) * int(lscpu_lookup("Socket(s):"))
        ),
        "cpu_speed": get_cpu_speed,
        "cpu_manufacturer": get_cpu_manufacturer,
        "cpu_family": get_cpu_family,
        "cpu_model": get_cpu_model,
        "cpu_l1d_cache": lambda: cpu_cache_info.get("L1d", {}).get("per_instance_KiB"),
        "cpu_l1d_cache_total": lambda: cpu_cache_info.get("L1d", {}).get("total_KiB"),
        "cpu_l1i_cache": lambda: cpu_cache_info.get("L1i", {}).get("per_instance_KiB"),
        "cpu_l1i_cache_total": lambda: cpu_cache_info.get("L1i", {}).get("total_KiB"),
        "cpu_l2_cache": lambda: cpu_cache_info.get("L2", {}).get("per_instance_KiB"),
        "cpu_l2_cache_total": lambda: cpu_cache_info.get("L2", {}).get("total_KiB"),
        "cpu_l3_cache": lambda: cpu_cache_info.get("L3", {}).get("per_instance_KiB"),
        "cpu_l3_cache_total": lambda: cpu_cache_info.get("L3", {}).get("total_KiB"),
        "cpu_flags": lambda: lscpu_lookup("Flags:").split(" "),
        "ecpus": calculate_ecpus,
        "scalability": lambda: (
            round(server["ecpus"] / server["vcpus"] * 100, 2)
            if server.get("ecpus") is not None and server.get("vcpus") is not None
            else None
        ),
        "hw_virt": lambda: lookups["virtualization"].get("kvm", None),
        "memory_amount_actual": get_memory_amount_actual,
        "memory_generation": lambda: DdrGeneration[lookups["dmidecode_memory"]["Type"]],
        # convert to Mhz
        "memory_speed": lambda: int(lookups["dmidecode_memory"]["Speed"]) / 1e6,
        "accelerators": lambda: _gpus_details(lookups["gpus"]),
        "accelerator_manufacturer": lambda: _gpu_most_common(
            server["accelerators"], "manufacturer"
        ),
        "accelerator_family": lambda: _gpu_most_common(
            server["accelerators"], "family"
        ),
        "accelerator_model": lambda: _gpu_most_common(server["accelerators"], "model"),
        # skip update if there is no HW-inspected GPU info
        "accelerator_count": lambda: (
            len(server["accelerators"]) if len(server["accelerators"]) else None
        ),
        # nvidia-smi only reports GPUs
        "accelerator_type": lambda: (
            AcceleratorType.GPU
            if isinstance(lookups["gpus"], list) and lookups["gpus"]
            else None
        ),
        "accelerator_memory_min": lambda: min(
            [gpu["memory"] for gpu in server["accelerators"]]
        ),
        "accelerator_memory_total": lambda: sum(
            [gpu["memory"] for gpu in server["accelerators"]]
        ),
        # skip storage update if lshw parsing failed or API data is present
        "storage_type": lambda: (
            inspector_storage_info.storage_type if inspector_storage_info else None
        ),
        "storage_size": lambda: (
            inspector_storage_info.storage_size if inspector_storage_info else None
        ),
        "storages": lambda: (
            inspector_storage_info.storages if inspector_storage_info else None
        ),
        "average_time_to_start": lambda: _server_average_time_to_start(server_obj),
    }

    def override_mapping(server, field, inspector_data):
        """Decide whether to override vendor-provided server data with inspector data.

        Args:
            server: Server-like dict with vendor-provided data.
            field: Server table column name.
            inspector_data: New data provided by inspector.

        Returns:
            The data provided by inspector, or the vendor.
        """
        vendor_id = server.get("vendor_id")
        vendor_data = server.get(field)

        # TODO drop once we have full lsblk coverage
        # always override GCP fields where vendor data is known to be missing
        storage_fields = ["storage_type", "storage_size", "storages"]
        if vendor_id == "gcp" and field in ["accelerator_model", *storage_fields]:
            return inspector_data
        # don't trust HDD/SSD inspection data at other vendors yet
        if vendor_id != "gcp" and field in storage_fields:
            return vendor_data

        # keep inspector data for detailed fields that's not available from vendor API
        if (
            field == "accelerators"
            and inspector_data
            and isinstance(inspector_data, list)
            and len(inspector_data) > 0
            and inspector_data[0].get("bios_version")
        ):
            return inspector_data
        # never override vendor data with None
        if inspector_data is None:
            return vendor_data
        # return inspector data if vendor data is missing
        if not vendor_data:
            return inspector_data
        # last resort: keep vendor data
        return vendor_data

    for k, f in mappings.items():
        try:
            newval = f()
            if newval is not None:
                server[k] = override_mapping(server, k, newval)
        except Exception as e:
            _log_cannot_update_server(server_obj, k, e)

    # standardize GPU model / fractional count (suffixes stripped from model)
    if server.get("accelerator_model"):
        server["accelerator_count"] = _standardize_gpu_count(
            server["accelerator_model"],
            server.get("accelerator_count", 0),
            server.get("accelerator_memory_total", 0),
            description=server.get("description"),
        )
        server["accelerator_model"] = _standardize_gpu_model(
            server["accelerator_model"], server
        )
        server["accelerator_family"] = _standardize_gpu_family(server)
        if (
            not server.get("accelerator_manufacturer")
            and server["accelerator_model"] in _NVIDIA_GPU_MODELS
        ):
            server["accelerator_manufacturer"] = "NVIDIA"

    return server