From 099bbaadd1904b7f4d4e451f0b2fa4c3c2d2d027 Mon Sep 17 00:00:00 2001 From: Nathan Leblond Date: Fri, 4 Oct 2024 16:58:22 +0200 Subject: [PATCH 001/108] docs(hwpc-sensor): refactor documentation, remove Destination/Source raw text references --- .../sensors/hwpc-sensor-overview.svg | 4 + docs/reference/sensors/hwpc-sensor.md | 101 ++++++++++++------ 2 files changed, 73 insertions(+), 32 deletions(-) create mode 100644 docs/assets/images/reference/sensors/hwpc-sensor-overview.svg diff --git a/docs/assets/images/reference/sensors/hwpc-sensor-overview.svg b/docs/assets/images/reference/sensors/hwpc-sensor-overview.svg new file mode 100644 index 0000000..d1ce55c --- /dev/null +++ b/docs/assets/images/reference/sensors/hwpc-sensor-overview.svg @@ -0,0 +1,4 @@ + + + +
HWPC Sensor inputs
cgroup path1
Program 1
PID : 1
Program 2
PID : 2
cgroup path2
Program 3
PID : 3
Program 4
PID : 4
Program 31
PID : 31
Parent's PID : 3
HWPC Sensor
HWPC Sensor ouputs
Database


HWPC
Reports
\ No newline at end of file diff --git a/docs/reference/sensors/hwpc-sensor.md b/docs/reference/sensors/hwpc-sensor.md index 7dc0bc7..d72fdf4 100644 --- a/docs/reference/sensors/hwpc-sensor.md +++ b/docs/reference/sensors/hwpc-sensor.md @@ -4,42 +4,54 @@ HardWare Performance Counter (HWPC) Sensor is a tool that monitors the Intel CPU performance counter and the power consumption of CPU. HWPC Sensor uses the RAPL (Running Average Power Limit) technology to monitor CPU -power consumption. This technology is only available on **Intel Sandy Bridge** -architecture or **newer**. However, Intel Core **Tiger Lake**, **Alder Lake** and **Raptor Lake** families for **desktop** and **mobile** **are not supported**. The sensor is also available on **AMD Zen (1,2,34)**. **Power/ARM/RISCV are not supported** architectures. +power consumption. The following table gives a glimpse of RAPL support regarding +most common architectures: -In particular, it exploits the `perf` API of the **Linux kernel**. It is only available on Linux -and need to have **root access** to be used. If you are using version **1.2 or older**, the sensor requires **cgroup V1**. +???+ info "HWPC Sensor PreRequisites" + `lscpu` will give you the necessary information about your CPU Architecture -**The sensor can not be used in a virtual machine**, it must have access (via Linux -kernel API) to the real CPU register to read performance counter values. +| Architecture | RAPL Supported | +|--------------|----------------| +| Intel Tiger Lake | :material-close: Not Supported | +| Intel Alder Lake | :material-close: Not Supported | +| Intel Raptor Lake | :material-close: Not Supported | +| Power / ARM / RISCV | :material-close: Not Supported | +| AMD Zen (1, 2, 3, 4) | :material-check: Supported | +| Intel Sandy Bridge and [newer](https://en.wikipedia.org/wiki/List_of_Intel_Core_processors#Core_i_(2nd_gen)) (except for above mentions) | :material-check: Supported | + +???+ info "HWPC Sensor PreRequisites" + In addition of a supported architecture, there is some pre-requisites: + - Using a Linux distribution exposing the [perf](https://perf.wiki.kernel.org/index.php/Main_Page) api + - Using Cgroup version 1 when using version 1.2 or older. See [this section](../cgroup/cgroup_v1_activation.md) about its configuration + - Deploying on a physical device as the HWPC Sensor must have access to the real CPU register + +![HWPC Sensor Overview](../../assets/images/reference/sensors/hwpc-sensor-overview.svg){ width="1000px"} + +## Sensor outputs The sensor provides raw values of performance counters as well as `RAPL` raw values in microjoules. ## Installation +The default installation is done through Docker container. +The different images can be found on the [Docker Hub](https://hub.docker.com/r/powerapi/hwpc-sensor/tags) + +Here is a sample to deploy the latest image version available. === "Docker" ```bash - docker pull powerapi/hwpc-sensor + docker pull powerapi/hwpc-sensor:latest + ``` ## Usage -For running the sensor, a Source and a configuration defined via a file or CLI parameters are required. - -### Source +An HWPC Sensor instance needs several parameteres to be configured in order to be used. +The following tabs gives a complete overview of available parameters, along with their default values and description. -For running HWPC Sensor we are using MongoDB as Source as a docker container. +### Global parameters -To start a MongoDB instance via the command line - -``` -docker run -d --name mongo_destination -p 27017:27017 mongo -``` - -### Root Parameters - -The table below shows the different parameters related to the Sensor Configuration: +The table below shows the different parameters related to the Sensor global configuration, nested objects (system, container, output) are described in dedicated sections below: | Parameter | Type | CLI shortcut | Default Value | Description | | ------------- | ----- | ------------- | ------------- | ------------------------------------ | @@ -49,16 +61,16 @@ The table below shows the different parameters related to the Sensor Configurati |`cgroup_basepath` | `string` | `p` | `/sys/fs/cgroup` (`cgroup` V2) | The base path for `cgroups`. To use `cgroup` V1 `/sys/fs/cgroup/perf_event` needs to be used as value | |`system` | `dict` | `s` | - | A system group with a monitoring type and a list of system events (cf. [`system` Group Parameters](hwpc-sensor.md#system-and-container-groups-parameters)) | |`container` | `dict` | `c` | - | A group with a monitoring type and a list of events (cf. [`system` Group Parameters](hwpc-sensor.md#system-and-container-groups-parameters)) | -|`output` | Destination | `r` | ` csv` | The Destination used as output. The Sensor only supports [MongoDB](../database/sources_destinations.md#mongodb) (`mongodb`) and [CSV](../database/sources_destinations.md#csv) (`csv`) as Destination. | - - +|`output` | Output | `r` | ` csv` | The [output information](hwpc-sensor.md#output), the Sensor only supports [MongoDB](../database/sources_destinations.md#mongodb) (`mongodb`) and [CSV](../database/sources_destinations.md#csv) (`csv`) as output. | ### `system` and `container` Groups Parameters +The table below shows the different parameters related to the Sensor `system` and `container` configuration fields: + | Parameter | Type | CLI shortcut | Default Value | Description | | ------------- | ----- | ------------- | ------------- | ------------------------------------ | -|`events` | `string` | `e` | - | List of events to be monitored. As CLI parameter, each event is indicated with `e` | -|`monitoring_type` | `string` (`MONITOR_ONE_CPU_PER_SOCKET`, `MONITOR_ALL_CPU_PER_SOCKET` ) | `o` (flag) | `MONITOR_ALL_CPU_PER_SOCKET` | The monitoring type. If `o` is specified as CLI parameter, `MONITOR_ONE_CPU_PER_SOCKET` is used as type +|`events` | `string` | `e` | - | List of events to be monitored. As CLI parameter, each event is indicated with `e`. The structure of events is given [below](hwpc-sensor.md#events) | +|`monitoring_type` | `string` (`MONITOR_ONE_CPU_PER_SOCKET`, `MONITOR_ALL_CPU_PER_SOCKET` ) | `o` (flag) | `MONITOR_ALL_CPU_PER_SOCKET` | The monitoring type. If `o` is specified as CLI parameter, `MONITOR_ONE_CPU_PER_SOCKET` is used as type | ### Events @@ -68,14 +80,43 @@ Table below depicts the different group events for compatible Intel and AMD arch | ------------- | ----- | ------------- | |Intel Sandy Bridge and newer, AMD Zen 2 | `rapl` | `RAPL_ENERGY_PKG`, `RAPL_ENERGY_DRAM`| |Intel Sandy Bridge and newer, AMD Zen 2 | `msr` | `TSC`, `APERF`, `MPERF`| -|Intel Skylake, Whiskey Lake, Coffe Lake| `core` | `CPU_CLK_THREAD_UNHALTED:REF_P`, `CPU_CLK_THREAD_UNHALTED:THREAD_P`, `LLC_MISSES`,`INSTRUCTIONS_RETIRED`| +|Intel Skylake, Whiskey Lake, Coffee Lake| `core` | `CPU_CLK_THREAD_UNHALTED:REF_P`, `CPU_CLK_THREAD_UNHALTED:THREAD_P`, `LLC_MISSES`,`INSTRUCTIONS_RETIRED`| |Intel Sandy Bridge, Comet Lake | `core` | `CPU_CLK_UNHALTED:REF_P`, `CPU_CLK_UNHALTED:THREAD_P`, `LLC_MISSES`,`INSTRUCTIONS_RETIRED`| |AMD Zen 2 | `core`| `CYCLES_NOT_IN_HALT`, `RETIRED_INSTRUCTIONS` , `RETIRED_UOPS`| |AMD Zen 3 | `core`| `CYCLES_NOT_IN_HALT`, `RETIRED_INSTRUCTIONS` , `RETIRED_OPS`| +### Output + +As precised, two kinds of outputs are supported, MongoDB and CSV files. + +#### MongoDB Output + +Table below depicts the different parameters for MongoDB type output with HWPC Sensor: +| Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | +| ------------- | ----- | ------------- | ------------- | ---------- | ------------------------------------ | +| `uri` | string | `U` | N/A | Yes | The IP address of your MongoDB instance | +| `database` | string | `D` | N/A | Yes | The name of your database | +| `collection` | string | `HWPCSensor` | N/A | Yes | The name of the collection inside `db` | + +You can start a MongoDB instance via a Docker container by running: + +``` +docker run -d --name mongo_output -p 27017:27017 mongo:latest +``` + +The different images can be found on the [Docker Hub](https://hub.docker.com/_/mongo/tags) + +#### CSV Output + +Table below depicts the different parameters for CSV type output: +| Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | +| ------------- | ----- | ------------- | ------------- | ----------| ------------------------------------ | +| `directory` | string | `U` | "." (Current directory) | No |The directory where output CSV files will be written | ### Running the Sensor with a Configuration File +The following snippet describe the configuration of an HWPC Sensor instance, writting reports to a MongoDB intance as output: + ```json { "name": "sensor", @@ -109,8 +150,7 @@ Table below depicts the different group events for compatible Intel and AMD arch } ``` -Once you have your configuration file, run HWPC Sensor using one of the following command lines, depending on the installation you use: - +The following CLI command shows how to use this configuration file in the deployment of an HWPC Sensor container : === "Docker" ```sh @@ -128,8 +168,7 @@ Once you have your configuration file, run HWPC Sensor using one of the followin ### Running the Sensor via CLI parameters -In order to run the Sensor without a configuration file, run HWPC Sensor using one of the following command lines, depending on the installation you use: - +The following CLI command shows how to launch an instance of HWPC Sensor with the same configuration as [above](hwpc-sensor.md#running-the-sensor-with-a-configuration-file) === "Docker" ```sh @@ -149,8 +188,6 @@ In order to run the Sensor without a configuration file, run HWPC Sensor using o -c "core" -e "CPU_CLK_THREAD_UNHALTED:REF_P" -e "CPU_CLK_THREAD_UNHALTED:THREAD_P" -e "LLC_MISSES" -e "INSTRUCTIONS_RETIRED" ``` - - ???+ info "Reports' Storage" Your [`HWPCReports`](../reports/reports.md#hwpc-reports) will be stored on MongoDB. From bb5ec29b919e9ae749520663c138bc9fc8149e76 Mon Sep 17 00:00:00 2001 From: Nathan Leblond Date: Fri, 4 Oct 2024 16:58:40 +0200 Subject: [PATCH 002/108] docs(smartwatts): refactor documentation, remove Destination/Source raw text references --- docs/reference/formulas/smartwatts.md | 50 ++++++++++++++++----------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/docs/reference/formulas/smartwatts.md b/docs/reference/formulas/smartwatts.md index f8477f8..ed40ead 100644 --- a/docs/reference/formulas/smartwatts.md +++ b/docs/reference/formulas/smartwatts.md @@ -1,17 +1,13 @@ # SmartWatts Formula -SmartWatts is a software-defined power meter based on the PowerAPI toolkit. -SmartWatts is a configurable software that can estimate the power consumption of -software in real-time. -SmartWatts needs to receive several metrics provided by -[HWPC Sensor](../sensors/hwpc-sensor.md#events) : +SmartWatts is a Formula, a configurable software that can estimate the power consumption of software in real-time. +SmartWatts needs to receive several metrics available in [Reports](../report/report.md), [HWPC Sensor](../sensors/hwpc-sensor.md#events) is a Sensor compatible (i.e making the necessary metrics available in HWPC Reports): - The Running Average Power Limit (`RAPL`) - `msr` events (`TSC`, `APERF`, `MPERF`) - `core` events which depend on the Processor Architucture -These metrics are then used as inputs for a power model that estimates the power -consumption of each software. +These metrics are then used as inputs for a power model that estimates the power consumption of each software, those estimations are recorded in Power Reports. The model is calibrated each time a `cpu-error-threshold` is reached by learning a new power model with previous reports. @@ -20,7 +16,8 @@ Software-Defined Power Meter for Containers](https://hal.inria.fr/hal-02470128) ## Installation -You can use the following command to install SmartWatts: +The default installation is done through Docker container. +The different images can be found on the [Docker Hub](https://hub.docker.com/r/powerapi/smartwatts-formula/tags) === "Docker" ``` @@ -36,33 +33,46 @@ You can use the following command to install SmartWatts: For running the SmartWatts Formula you need: a Source and a Destination, a Sensor that provides `HWPCReports` and a configuration. -### Source and Destination -For running SmartWatts we are using MongoDB as Source and InfluxDB 2.X as Destination as dockers containers. +### Input and Output +As any Formula, SmartWatts needs both inputs and outputs. +We can choose those among [this list](../database/sources_destinations.md#summary) -To start a MongoDB instance via the command line - -```sh -docker run -d --name mongo_source -p 27017:27017 mongo +#### MongoDB as input +MongoDB can be used as input for Reports. +You can start a MongoDB instance via a Docker container by running: ``` -And a InfluxDB 2.X instance +docker run -d --name mongo_output -p 27017:27017 mongo:latest +``` +The different images can be found on the [Docker Hub](https://hub.docker.com/_/mongo/tags) + +#### InfluxDB as output +On the other hand, we can use InfluxDB instance as output for our Power Reports +You can start an InfluxDB instance, in version >= 2.0.0 via a Docker container by running: ```sh docker run -p 8086:8086 -v "/tmp/powerapi-influx/data:/var/lib/influxdb2" -v "/tmp/powerapi-influx/config:/etc/influxdb2" influxdb:2 ``` +The different images can be found on the [Docker Hub](https://hub.docker.com/_/influxdb/tags?name=2) + ???+ tip "Set up influxdb 2.X for the first time" If it is the first time that you are using `influxdb 2.X`, there are several methods (UI, CLI, API) to make a set up. Please check [here](https://docs.influxdata.com/influxdb/v2/get-started/setup/) for more information. ### Sensor -[HWPC Sensor](../sensors/hwpc-sensor.md) is used in order to get `HWPCReports`. Start by installing the HWPC Sensor (see -[here](../sensors/hwpc-sensor.md#installation)) and start it (see -[here](../sensors/hwpc-sensor.md#usage)). +[HWPC Sensor](../sensors/hwpc-sensor.md) can be used in order to get `HWPCReports` which provided the necessary information for SmartWatts. +If you wish to use it : +- Install the HWPC Sensor (see [here](../sensors/hwpc-sensor.md#installation)) +- Start the Sensor (see [here](../sensors/hwpc-sensor.md#usage)) ### Parameters Besides the [basic parameters](../formulas/configuration_files.md), the following ones are specific to SmartWatts: +???+ info "Hardware dependent values" + Some parameters values depend of your hardware. In particular, `cpu-base-freq`. You can obtain this value from `CPU MHz` field by using `lscpu` command. + + | Parameter | Type | CLI shortcut | Default Value | Description | | ------------- | ----- | ------------- | ------------- | ------------------------------------ | |`disable-cpu-formula` | `bool` (flag) | - | `false` | Disable CPU Formula | @@ -71,7 +81,7 @@ Besides the [basic parameters](../formulas/configuration_files.md), the followin |`dram-rapl-ref-event` | `string` | - | `"RAPL_ENERGY_DRAM"` | RAPL event used as reference for the DRAM power models | |`cpu-tdp` | `int` | - | `125` | CPU TDP (in Watt)| |`cpu-base-clock` | `int` | - | `100` | CPU base clock (in MHz) | -|`cpu-base-freq` | `int` | - | `2100` | CPU base frequency (in MHz) | +|`cpu-base-freq` | `int` | - | `2100` | CPU base frequency (in MHz), depend of your hardware. You can obtain this value from `CPU MHz` field by using `lscpu` command. | |`cpu-error-threshold` | `float` | - | `2.0` | Error threshold for the CPU power models (in Watts) | |`dram-error-threshold` | `float` | - | `2.0` | Error threshold for the DRAM power models (in Watts) | |`learn-min-samples-required` | `int` | - | `10` | Minimum amount of samples required before trying to learn a power model | @@ -109,7 +119,7 @@ In order to run the Formula, you can execute one of the following command lines, --sensor-reports-frequency 1000 ``` -In this configuration we are using MongoDB as source and InfluxDB 2.X as Destination. Some parameters values depend of your hardware. In particular, `cpu-base-freq`. You can obtain this value from `CPU MHz` field by using `lscpu` command. +In this configuration we are using MongoDB as source and InfluxDB 2.X as Destination. ???+ info "Estimations' Storage" Your `PowerReports` will be stored on InfluxDB2. You can watch them in a grafana by using the [following tutorial](../grafana/grafana.md). From 41ef0b3aa5782fd4a3352079dab4b195506b8ec6 Mon Sep 17 00:00:00 2001 From: Nathan Leblond Date: Mon, 7 Oct 2024 16:20:31 +0200 Subject: [PATCH 003/108] docs(getting-started): Add necessary example to run a complete PowerAPI Stack and visualize it --- docs/getting_started.md | 354 +++++++++++++++++++++++++++++++++++----- 1 file changed, 317 insertions(+), 37 deletions(-) diff --git a/docs/getting_started.md b/docs/getting_started.md index 9fd9adf..fe3d62a 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -1,57 +1,337 @@ # Getting started -If you want to monitor the energy consumption of your process we have some -ready-to-use tools +## Define elements to monitor -???+ info "Source and Destination" - In order to use any Formula, you need to run a Source and a Destination. The former is used by a Sensor to store metrics. The later allows the Formula to make available the estimations. For starting, you can use [MongoDB](https://hub.docker.com/_/mongo) as Source and [InfluxDB:2.X](https://hub.docker.com/_/influxdb) as Destination by installing them as Docker containers. - For more details about Sources and Destinations please check this [section](reference/database/sources_destinations.md). +### Create a cGroup +We need a subset of runnings programs to be monitored. For this, we use the +Linux abstraction of [cGroups](https://www.redhat.com/sysadmin/cgroups-part-one). - +Once the group created, we need to fill it with programs to be monitored. +To do so, you can use the following : -## **SmartWatts Formula** +```sh +cgclassify -g perf_event:new_cgroup_name PID +``` -!!! note "" - for monitoring the power consumption of your process +with `PID`, the pid of the process you want to monitor. If you want to monitor a +program composed of many process, replace PID with `$(pidof program_name)`. -Smartwatts is made for tracking the power consumption of processes on a -machine. -To install Smartwatts on a baremetal server or a PC run [the following -script](script/smartwatts_install.sh) in a Terminal. Please notice that you will need [pip](https://pip.pypa.io/en/stable/installation/) or [docker](https://docs.docker.com/engine/install/) in order to use the Formula. +### Example program to monitor -The script explains what it will do and then pauses before it does it. +[stress-ng](https://wiki.ubuntu.com/Kernel/Reference/stress-ng) can be used to +generate load on one's system. +An example usage, once installed : -Please notice that you need a **Linux distribution** in order to use the HWPC Sensor installed by the script as -well as a **comptible Intel** (Sandy Bridge and newer) or **AMD Processor** (Zen). You also need [docker](https://docs.docker.com/engine/install/). **Power/ARM/RISCV are not supported** architectures. HWPC Sensor will **not work on a Virtual Machine**. However, you can install the Formula by hand in a Virtual Machine if need it. +```sh +stress-ng --cpu 1 --timeout 5m +``` +The PID can be found using : +```sh +STRESS_PID="$(pgrep stress-ng)" +``` -#### CGroups -If you need to monitor a process or a group of process via SmartWatts by using HWPC Sensor **version 1.2 or older**, you can follow this [tutorial](reference/cgroup/cgroup.md). Please notice that **cgroup V1** is required **only** for HWPC Sensor **version 1.2 or older**. If you need to enable this `cgroup` version please follow this [tutorial](reference/cgroup/cgroup_v1_activation.md). +## Which components to get a complete stack - +2. An InfluxDB2 instance to store the [Formulas](./reference/formulas/smartwatts.md) + +3. An [HWPC-Sensor](./reference/sensors/hwpc-sensor.md) that outputs its +[HWPCReports](./reference/reports/report.md#HWPCReport) in a MongoDB Database, +within the HWPCReport Collection. + +4. A [SmartWatts](./reference/formulas/smartwatts.md) that streams the +[HWPCReports](./reference/reports/report.md#HWPCReport) from the MongoDB +Database Collection, processes it and outputs its +[PowerReports](./reference/reports/report.md#PowerReports) in an InfluxDB2 +Database within the PowerReport Collection + +5. A Grafana instance to visualize the +[PowerReports](./reference/reports/report.md#PowerReports) from the InfluxDB2 +instance + +## Preparation + +### Docker-Compose + +You can download it through: + +```sh +wget "https://raw.githubusercontent.com/powerapi-ng/powerapi-ng.github.io/refs/heads/master/examples/docker-compose.yml" +``` + +```yaml +version: '3.8' +services: + mongodb: + image: mongo:latest + container_name: mongodb + ports: + - "27017:27017" + volumes: + - mongo_data:/data/db + environment: + MONGO_INITDB_DATABASE: powerapi + command: ["mongod"] + networks: + - powerapi_network + + influxdb: + image: influxdb:2.0 + container_name: influxdb + ports: + - "8086:8086" + volumes: + - influxdb_data:/var/lib/influxdb2 + environment: + INFLUXDB_DB: powerapi + INFLUXDB_ADMIN_USER: admin + INFLUXDB_ADMIN_PASSWORD: admin123 + INFLUXDB_USER: powerapi_user + INFLUXDB_PASSWORD: powerapi_password + networks: + - powerapi_network + + hwpc_sensor: + image: powerapi/hwpc-sensor + container_name: hwpc_sensor + volumes: + - ./config/hwpc-sensor-config.json:/hwpc-sensor-config.json + command: --config-file /hwpc-sensor-config.json + networks: + - powerapi_network + depends_on: + - mongodb + + smartwatts: + image: powerapi/smartwatts + container_name: smartwatts + volumes: + - ./config/smartwatts-config.json:/smartwatts-config.json + command: --config /smartwatts-config.json + networks: + - powerapi_network + depends_on: + - mongodb + - influxdb + + grafana: + image: grafana/grafana:latest + container_name: grafana + ports: + - "3000:3000" + environment: + - GF_SECURITY_ADMIN_USER=admin + - GF_SECURITY_ADMIN_PASSWORD=admin + volumes: + - grafana_data:/var/lib/grafana + - ./config/provisioning:/etc/grafana/provisioning + networks: + - powerapi_network + depends_on: + - influxdb + +volumes: + mongo_data: + influxdb_data: + +networks: + powerapi_network: + driver: bridge +``` + +### HWPC-Sensor Configuration + +As described in the [HWPC-Sensor Documentation](./reference/sensors/hwpc-sensor.md#global-parameters) +several parameters can be set, both globally and for specific Groups monitored. +The provided docker-compose.yaml file use configuration files to set those parameters. +An example configuration file for HWPC-Sensor is given below, it can also be downloaded through: + +```sh +wget "https://raw.githubusercontent.com/powerapi-ng/powerapi-ng.github.io/refs/heads/master/examples/hwpc-sensor-config.json" +``` + +```json +{ + "verbose": false, + "frequency": 1000, + "name": "hwpc-sensor", + "cgroup_basepath": "/sys/fs/cgroup/perf_event", + "system": { + "type": "MONITOR_ALL_CPU_PER_SOCKET", + "events": ["RAPL_ENERGY_PKG", "RAPL_ENERGY_DRAM"] + }, + "container": { + "type": "MONITOR_ALL_CPU_PER_SOCKET", + "events": ["RAPL_ENERGY_PKG", "RAPL_ENERGY_DRAM"] + }, + "output": { + "type": "mongodb", + "uri": "mongodb://mongodb:27017", + "db": "powerapi", + "collection": "HWPCReports" + } +} +``` + +### SmartWatts Configuration + +As described in the [SmartWatts Documentation](./reference/formulas/smartwatts.md#global-parameters) +several parameters can be set for the Formulas. +The provided docker-compose.yaml file use configuration files to set those parameters. +An example configuration file for SmartWatts is given below, it can also be downloaded through: + +```sh +wget "https://raw.githubusercontent.com/powerapi-ng/powerapi-ng.github.io/refs/heads/master/examples/smartwatts-config.json" +``` + +```json +{ + "cpu_tdp": 125, + "cpu_base_clock": 100, + "cpu_base_freq": 2100, # Use lscpu and look for CPU MHz field to know the correct value + "cpu_error_threshold": 2.0, + "dram_error_threshold": 2.0, + "learn_min_samples_required": 10, + "learn_history_window_size": 60, + "sensor_reports_frequency": 1000, + "input_mongo": { + "uri": "mongodb://mongodb:27017/powerapi", + "collection": "HWPCReports" + }, + "output_influx": { + "uri": "http://influxdb:8086", + "org": "powerapi_org", + "bucket": "PowerReports", + "token": "INFLUXDB_API_TOKEN_GENERATED" + } +} +``` + +### Grafana Configuration + +Grafana has to be configured to use our InfluxDB2 instance as a datasource, +it should also know our dashboard file in order to visualize it. + +#### Filesystem structure + +Create the following filesystem structure in the current working directory : + + +```sh +| grafana_config/ +|--provisioning/ +|----dashboards/ +|------dashboard.yaml +|------reports.json +|----datasources/ +|------datasource.yaml +``` + +#### Datasource file + +The following file gives information to Grafana to use InfluxDB2 as Datasource: + +```yaml +# datasource.yaml + +apiVersion: 1 +datasources: + - name: InfluxDB + type: influxdb + access: proxy + url: http://influxdb:8086 + isDefault: true + database: PowerReports + user: powerapi_user + password: powerapi_password + jsonData: + version: Flux + organization: powerapi_org + defaultBucket: PowerReports + token: INFLUXDB_API_TOKEN_GENERATED + editable: true +``` + +#### Dashboard files + +The following files give information to Grafana: + +1. Where to search pre-configured Dashboards +2. How to configure our Dashboards + +```yaml +# dashboard.yaml + +apiVersion: 1 +providers: + - name: 'default' + folder: '' + type: file + options: + path: /etc/grafana/provisioning/dashboards +``` + +```yaml +# reports.json + +{ + "id": null, + "title": "PowerAPI Dashboard", + "tags": [], + "timezone": "browser", + "schemaVersion": 16, + "version": 0, + "panels": [ + { + } + ] +} +``` + +## Turn the key + +Once all set, you shall be able to run the complete stack with : + +```sh +docker-compose -d up +``` + +This first spin-up before deleting containers will create the DBs and the Sensor. +Both SmartWatts Formulas and Grafana will struggle as they need an InfluxDB API key. +To resolve this, you can run : +```sh +influx auth create --org powerapi_org --read-buckets --write-buckets +``` + +You will obtain a token to change in the `smartwatts-config.json` file and in `grafana_config/provisioning/datasources/datasource.yaml` + +Once both changed, another `docker-compose -d up` should do the trick to start the +remaining components. + +Thus, once deployed, you can access [Grafana](https://localhost:3000) by default +on http://localhost:8080 with its basic credentials : admin/admin. + +In the dashboards sections, basic dashboards will be fueled with PowerReports +from SmartWatts providing real time insights about you consumption. + +Feel free to try yo make your own visualization and take a look at +[this documentation](./reference/grafana/grafana.md) for further details. From fb73d58fdc2a842b41fe863fe41e4c0ff2c66f8e Mon Sep 17 00:00:00 2001 From: Nathan Leblond Date: Tue, 8 Oct 2024 09:08:48 +0200 Subject: [PATCH 004/108] docs(getting-started): Documents the Getting-Started instructions through an archive with all necessary files --- docs/getting_started.md | 74 +++++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 36 deletions(-) diff --git a/docs/getting_started.md b/docs/getting_started.md index fe3d62a..5ca3372 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -46,8 +46,7 @@ STRESS_PID="$(pgrep stress-ng)" ## Which components to get a complete stack -If you wish to get started as soon as possible, the following docker-compose -file will allow you to deploy the following elements : +If you wish to get started as soon as possible, the following archive will allow you to deploy the following elements : 1. A MongoDB instance to store the [Sensor](./reference/sensors/hwpc-sensor.md) Reports @@ -70,15 +69,20 @@ instance ## Preparation -### Docker-Compose +You can download the archive using : -You can download it through: +```sh +wget "https://github.com/powerapi-ng/powerapi-ng.github.io/tree/master/examples/powerapi-stack.zip" +unzip powerapi-stack.zip && cd powerapi-stack +``` -```sh -wget "https://raw.githubusercontent.com/powerapi-ng/powerapi-ng.github.io/refs/heads/master/examples/docker-compose.yml" -``` +From this archive, you will have all the necessary files to get started, let us break down each elements. + +### Docker-Compose ```yaml +# ./docker-compose.yaml + version: '3.8' services: mongodb: @@ -163,13 +167,11 @@ networks: As described in the [HWPC-Sensor Documentation](./reference/sensors/hwpc-sensor.md#global-parameters) several parameters can be set, both globally and for specific Groups monitored. The provided docker-compose.yaml file use configuration files to set those parameters. -An example configuration file for HWPC-Sensor is given below, it can also be downloaded through: - -```sh -wget "https://raw.githubusercontent.com/powerapi-ng/powerapi-ng.github.io/refs/heads/master/examples/hwpc-sensor-config.json" -``` +An example configuration file for HWPC-Sensor is given below and available in the archive presented [above](./getting_started.md#preparation) : ```json +# ./hwpc-sensor-config.json + { "verbose": false, "frequency": 1000, @@ -197,13 +199,11 @@ wget "https://raw.githubusercontent.com/powerapi-ng/powerapi-ng.github.io/refs/h As described in the [SmartWatts Documentation](./reference/formulas/smartwatts.md#global-parameters) several parameters can be set for the Formulas. The provided docker-compose.yaml file use configuration files to set those parameters. -An example configuration file for SmartWatts is given below, it can also be downloaded through: - -```sh -wget "https://raw.githubusercontent.com/powerapi-ng/powerapi-ng.github.io/refs/heads/master/examples/smartwatts-config.json" -``` +An example configuration file for SmartWatts is given below and available in the archive presented [above](./getting_started.md#preparation) : ```json +# ./smartwatts-config.json + { "cpu_tdp": 125, "cpu_base_clock": 100, @@ -224,7 +224,7 @@ wget "https://raw.githubusercontent.com/powerapi-ng/powerapi-ng.github.io/refs/h "token": "INFLUXDB_API_TOKEN_GENERATED" } } -``` +``` ### Grafana Configuration @@ -233,17 +233,19 @@ it should also know our dashboard file in order to visualize it. #### Filesystem structure -Create the following filesystem structure in the current working directory : +The following filesystem structure is presented in the archive presented [above](./getting_started.md#preparation) : : ```sh -| grafana_config/ -|--provisioning/ -|----dashboards/ -|------dashboard.yaml -|------reports.json -|----datasources/ -|------datasource.yaml +|powerapi-stack/ +|--grafana_config/ +|----provisioning/ +|------dashboards/ +|--------dashboard.yaml +|--------reports.json +|------datasources/ +|--------datasource.yaml +|... ``` #### Datasource file @@ -251,7 +253,7 @@ Create the following filesystem structure in the current working directory : The following file gives information to Grafana to use InfluxDB2 as Datasource: ```yaml -# datasource.yaml +# ./grafana_config/provisioning/datasources/datasource.yaml apiVersion: 1 datasources: @@ -279,7 +281,7 @@ The following files give information to Grafana: 2. How to configure our Dashboards ```yaml -# dashboard.yaml +# ./grafana_config/provisioning/dashboards/dashboard.yaml apiVersion: 1 providers: @@ -290,8 +292,8 @@ providers: path: /etc/grafana/provisioning/dashboards ``` -```yaml -# reports.json +```json +# ./grafana_config/provisioning/dashboards/reports.json { "id": null, @@ -309,29 +311,29 @@ providers: ## Turn the key -Once all set, you shall be able to run the complete stack with : +Once all set, you shall be able to initiate the stack with : ```sh docker-compose -d up ``` -This first spin-up before deleting containers will create the DBs and the Sensor. +This first spin-up will create the DBs and the Sensor. Both SmartWatts Formulas and Grafana will struggle as they need an InfluxDB API key. To resolve this, you can run : ```sh influx auth create --org powerapi_org --read-buckets --write-buckets ``` -You will obtain a token to change in the `smartwatts-config.json` file and in `grafana_config/provisioning/datasources/datasource.yaml` +You will obtain a token to replace the *INFLUXDB_API_TOKEN_GENERATED* placeholder in the `smartwatts-config.json` file and in `grafana_config/provisioning/datasources/datasource.yaml` Once both changed, another `docker-compose -d up` should do the trick to start the remaining components. Thus, once deployed, you can access [Grafana](https://localhost:3000) by default -on http://localhost:8080 with its basic credentials : admin/admin. +on http://localhost:8080 with its basic credentials : `admin/admin`. -In the dashboards sections, basic dashboards will be fueled with PowerReports +In the dashboards sections, basic dashboards will be provisionned with PowerReports from SmartWatts providing real time insights about you consumption. Feel free to try yo make your own visualization and take a look at -[this documentation](./reference/grafana/grafana.md) for further details. +[this documentation](./reference/grafana/grafana.md) for further details. From bfd3e519403ca53e7783ef4a69a72aac89bd2d32 Mon Sep 17 00:00:00 2001 From: Nathan Leblond <111641897+Inkedstinct@users.noreply.github.com> Date: Tue, 8 Oct 2024 09:23:11 +0200 Subject: [PATCH 005/108] Add hwpc-sensor.drawio --- .../assets/images/reference/sensors/hwpc-sensor.drawio | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 docs/assets/images/reference/sensors/hwpc-sensor.drawio diff --git a/docs/assets/images/reference/sensors/hwpc-sensor.drawio b/docs/assets/images/reference/sensors/hwpc-sensor.drawio new file mode 100644 index 0000000..018e54b --- /dev/null +++ b/docs/assets/images/reference/sensors/hwpc-sensor.drawio @@ -0,0 +1,10 @@ + + + + + + + + + + From 522fec0b74e022200da75de37b8f9994c88f8ffd Mon Sep 17 00:00:00 2001 From: Nathan Leblond <111641897+Inkedstinct@users.noreply.github.com> Date: Tue, 8 Oct 2024 09:24:32 +0200 Subject: [PATCH 006/108] Add hwpc-sensor.drawio --- docs/assets/images/reference/sensors/hwpc-sensor.drawio | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/assets/images/reference/sensors/hwpc-sensor.drawio b/docs/assets/images/reference/sensors/hwpc-sensor.drawio index 018e54b..b48a588 100644 --- a/docs/assets/images/reference/sensors/hwpc-sensor.drawio +++ b/docs/assets/images/reference/sensors/hwpc-sensor.drawio @@ -1,5 +1,5 @@ - + From 81d6146587711698629e1e70c88befd937fe510e Mon Sep 17 00:00:00 2001 From: Inkedstinct Date: Tue, 8 Oct 2024 10:08:04 +0200 Subject: [PATCH 007/108] docs(hwpc-sensor): Switch diagrams to Noun Project icons --- .../PowerAPI_HWPCSensorOverview.drawio | 99 +++++++++++++++++++ .../PowerAPI_HWPCSensorOverview.drawio.svg | 4 + .../sensors/hwpc-sensor-overview.svg | 4 - .../reference/sensors/hwpc-sensor.drawio | 10 -- docs/reference/sensors/hwpc-sensor.md | 4 +- 5 files changed, 106 insertions(+), 15 deletions(-) create mode 100644 docs/assets/images/reference/sensors/PowerAPI_HWPCSensorOverview.drawio create mode 100644 docs/assets/images/reference/sensors/PowerAPI_HWPCSensorOverview.drawio.svg delete mode 100644 docs/assets/images/reference/sensors/hwpc-sensor-overview.svg delete mode 100644 docs/assets/images/reference/sensors/hwpc-sensor.drawio diff --git a/docs/assets/images/reference/sensors/PowerAPI_HWPCSensorOverview.drawio b/docs/assets/images/reference/sensors/PowerAPI_HWPCSensorOverview.drawio new file mode 100644 index 0000000..94e44a1 --- /dev/null +++ b/docs/assets/images/reference/sensors/PowerAPI_HWPCSensorOverview.drawio @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/assets/images/reference/sensors/PowerAPI_HWPCSensorOverview.drawio.svg b/docs/assets/images/reference/sensors/PowerAPI_HWPCSensorOverview.drawio.svg new file mode 100644 index 0000000..f272ee4 --- /dev/null +++ b/docs/assets/images/reference/sensors/PowerAPI_HWPCSensorOverview.drawio.svg @@ -0,0 +1,4 @@ + + + +
Local server
Storing sensor's reports
Scrapping program perfomance metrics
(Optionnal)
Remote server
PowerAPI Sensor
Local Filesystem
Local program(s)
monitored
Remote database
Sensor Reports
\ No newline at end of file diff --git a/docs/assets/images/reference/sensors/hwpc-sensor-overview.svg b/docs/assets/images/reference/sensors/hwpc-sensor-overview.svg deleted file mode 100644 index d1ce55c..0000000 --- a/docs/assets/images/reference/sensors/hwpc-sensor-overview.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - -
HWPC Sensor inputs
cgroup path1
Program 1
PID : 1
Program 2
PID : 2
cgroup path2
Program 3
PID : 3
Program 4
PID : 4
Program 31
PID : 31
Parent's PID : 3
HWPC Sensor
HWPC Sensor ouputs
Database


HWPC
Reports
\ No newline at end of file diff --git a/docs/assets/images/reference/sensors/hwpc-sensor.drawio b/docs/assets/images/reference/sensors/hwpc-sensor.drawio deleted file mode 100644 index b48a588..0000000 --- a/docs/assets/images/reference/sensors/hwpc-sensor.drawio +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/docs/reference/sensors/hwpc-sensor.md b/docs/reference/sensors/hwpc-sensor.md index d72fdf4..bbbc789 100644 --- a/docs/reference/sensors/hwpc-sensor.md +++ b/docs/reference/sensors/hwpc-sensor.md @@ -3,6 +3,9 @@ HardWare Performance Counter (HWPC) Sensor is a tool that monitors the Intel CPU performance counter and the power consumption of CPU. +The figure below depicts how Sensor works in general : +![HWPC Sensor Overview](../../assets/images/reference/sensors/PowerAPI_HWPCSensorOverview.drawio.svg){ width="1000px"} + HWPC Sensor uses the RAPL (Running Average Power Limit) technology to monitor CPU power consumption. The following table gives a glimpse of RAPL support regarding most common architectures: @@ -25,7 +28,6 @@ most common architectures: - Using Cgroup version 1 when using version 1.2 or older. See [this section](../cgroup/cgroup_v1_activation.md) about its configuration - Deploying on a physical device as the HWPC Sensor must have access to the real CPU register -![HWPC Sensor Overview](../../assets/images/reference/sensors/hwpc-sensor-overview.svg){ width="1000px"} ## Sensor outputs From 2790e42d32896bcc60936ff0163c612839b79949 Mon Sep 17 00:00:00 2001 From: Inkedstinct Date: Tue, 8 Oct 2024 11:25:29 +0200 Subject: [PATCH 008/108] docs(overview): Clarifies PowerAPI framework components and status --- docs/reference/overview.md | 81 +++++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 36 deletions(-) diff --git a/docs/reference/overview.md b/docs/reference/overview.md index ffaa567..a54dab2 100644 --- a/docs/reference/overview.md +++ b/docs/reference/overview.md @@ -1,63 +1,72 @@ # PowerAPI Overview -The goal of this project is to provide a set of tools to go forward a greener -computing. -The idea is to provide software-defined PowerMeters to mesure the power -consumption of programs. -The core of this project is the [PowerAPI](https://github.com/powerapi-ng/powerapi) toolkit for building -such PowerMeters. +This project aims to offer a range of tools to promote greener computing. The focus is on creating software-defined PowerMeters to measure the power consumption of various programs. +At the heart of this initiative is the [PowerAPI](https://github.com/powerapi-ng/powerapi) toolkit, which facilitates the development of these PowerMeters. ## Software PowerMeters -A software PowerMeter is an application built with the PowerAPI components that can -measure the power consumption of software running on a single machine or on a -cluster of machine. +A software PowerMeter is an application built using PowerAPI components, designed to measure the power consumption of software running on a single machine or across a cluster of machines. -The Figure below depicts the global architecture of a software PowerMeter in PowerAPI. +The diagram below illustrates the overall architecture of a PowerMeter within the PowerAPI framework. ![PowerAPI Architecture Overview](../assets/images/reference/overview/global-architecture.jpg){ width="1000px"} +A PowerMeter consists of two essential components: +- A [Sensor](./overview.md#Sensor), which collects system usage metrics and generates usage reports. +- A [Formula](./overview.md#Formula), which applies a computational model to the usage reports, producing power consumption data (in mJ). - - -A PowerMeter has two components, a Sensor and a Formula, used to -produce an estimation of the power consumption of a monitored software. +Additionally, [Preprocessors](./overview.md#Preprocessors) can be utilized to modify usage reports before they are processed by the Formula. ## Sensor -The Sensor is an independent software that collects raw data (metrics) correlated with the power consumption of the -monitored software. +The Sensor is a standalone software tool that gathers basic data related to the power usage of the software it monitors. + +It collects this data by querying the hardware on the same machine where the monitored software runs. The Sensor needs to operate in parallel to the monitored software for the entire duration of its execution. -Data are collected by querying the hardware’s machine that hosts the monitored -software. The sensor must be executed on the same machine as the monitored -software. The data are collected throughout the duration of the software. For -this reason, the sensor must operate in parallel. +The raw data collected is then stored as usage Reports in various formats, either locally or remotely. These formats include CSV, MongoDB, FileDB, and UNIX sockets, among others. -Collected raw data are stored in an external Source to make the data available to -the Formula. This Source may be hosted on an other machine. +### Existing Sensors -### Usage +| Sensor name | Documentation | GitHub Repository | Description | Supported | +| :---------------: |----------------------|--------------------------|----------------|---------------| +| HWPC | [HWPC Documentation](./sensors/hwpc-sensor.md) | https://github.com/powerapi-ng/hwpc-sensor | Hardware Performance Counters monitoring agent for containers | :material-check: Supported | -Currently, PowerAPI proposes one Sensor: [HWPC](sensors/hwpc-sensor.md). -Refer to the Sensor documentation to know how to use it. ## Formula -A Formula is a computational module that computes an estimation of the power -consumption of monitored software from the data collected by the sensor. +A Formula is a computational module that estimates the power consumption of the monitored software using the data collected by the Sensor. + +There are two modes in which a Formula can operate: + +- **Stream mode**: In this mode, the Formula processes data from the Sensor as it is generated, providing real-time insights + +- **Post-mortem mode**: In this mode, the Formula analyzes data that has already been collected by the Sensor during a previous monitoring phase + +### Existing Formulas + +| Formula name | Documentation | GitHub Repository | Description | Supported | +| :---------------: |----------------------|--------------------------|----------------|---------------| +| SmartWatts | [SmartWatts Documentation](./formulas/smatwatts.md) | https://github.com/powerapi-ng/smartwatts-formula | HSmartWatts is a formula for a self-adaptive software-defined power meter based on the PowerAPI framework. | :material-check: Supported | + +## Processors + +Processors allow for the customized filtering and modification of Reports. While optional, they offer additional flexibility, but Software PowerMeters can still function without them. + +The diagram below shows where Processors are integrated into the architecture of a Software PowerMeter. + +![Processor integration in the processing pipeline](../..//docs/assets/images/reference/processors/processors.jpg) -A Formula has two working modes: +### Preprocessors -- `stream` mode where the Formula read the data from the Sensor as they are - produced (in realtime). +Preprocessors are positioned between the [Sensor](./overview.md#Sensor) and the [Formula](./overview.md#Formula). +Their role is to pre-process usage reports before the power consumption estimations are computed. -- `post-mortem` mode where the Formula analyses the data already collected by the Sensor in a past monitoring phase. +#### Existing Preprocessors -### Usage +| Preprocessor name | Documentation | GitHub Repository | Description | Supported | +| :---------------: |----------------------|--------------------------|----------------|---------------| +| k8sPreprocessor | [k8sPreprocessor Documentation](./processors/processors.md#k8spreprocessor) | https://github.com/powerapi-ng/powerapi/tree/master/src/powerapi/processor/pre/k8s | Add K8S Specific metadata to corresponding Reports | :material-check: Supported | +| libvirt | [libvirtPreprocessor Documentation](.processors/processors.md#libvirt) | https://github.com/powerapi-ng/powerapi/tree/master/src/powerapi/processor/pre/libvirt | Replace `libvirt ID` in Reports with `Open Stack UUID` | :material-check: Supported | -Currently, there is one Formula: [SmartWatts](formulas/smartwatts.md). -Refer to the Formula documentation to know how to use it. From de31f7fbfe8b0b954294e18ab429aec3e14b880b Mon Sep 17 00:00:00 2001 From: Inkedstinct Date: Tue, 8 Oct 2024 17:39:13 +0200 Subject: [PATCH 009/108] docs(source/destination abstractions): Remove Source / Destination abstractions references --- .../database/sources_destinations.md | 28 +-- .../reference/formulas/configuration_files.md | 9 +- docs/reference/formulas/smartwatts.md | 197 ++++++++++++++---- docs/reference/grafana/grafana.md | 4 +- docs/reference/processors/processors.md | 4 +- docs/reference/sensors/hwpc-sensor.md | 32 +-- mkdocs.yml | 4 +- 7 files changed, 203 insertions(+), 75 deletions(-) diff --git a/docs/reference/database/sources_destinations.md b/docs/reference/database/sources_destinations.md index fa35cfa..98be258 100644 --- a/docs/reference/database/sources_destinations.md +++ b/docs/reference/database/sources_destinations.md @@ -1,19 +1,23 @@ -# Sources and Destinations +# Storage Options -A PowerAPI Formula uses Sources and Destinations in order to retrieve metrics and store estimations. +Different storage options are available to serve different purpose both for [Sensors](../overview.md#Sensor) and [Formulas](../overview.md#Formula). -For each Source/Destination the parameters to specify are different. For each one of them, -its parameters are specified in following sections. +Storage is needed to save reports produced by each components. +- Sensors store their usage reports +- Formulas retrieve usage reports and store energy consumption reports +- Visualization tools or individuals need to access reports for analysis ## Summary -| Name | Source | Destination | CLI `input`/`ouput` parameter value | JSON `type` tag parameter value | -| ------------- | ----- | ------------- | ------------- | ------------------------------------ | -| MongoDB | Yes | Yes | mongodb | mongodb | -| InfluxDB2 | No | Yes | influxdb2 | influxdb2 | -| CSV | Yes | Yes | csv | csv | -| Socket | Yes | No | socket | socket | -| File Database | Yes | Yes | filedb | filedb | -| Prometheus | No | Yes | prometheus | prometheus | + +The following table defines the existing storage options for Sensors usage reports : + +| Name | CLI `ouput` parameter value | JSON `type` tag parameter value| +| ------------ | --------------------------------------| -------------------------------------------| +| MongoDB | mongodb | mongodb | +| CSV | csv | csv | +| Socket | socket | socket | +| File Database | filedb | filedb | + ## MongoDB diff --git a/docs/reference/formulas/configuration_files.md b/docs/reference/formulas/configuration_files.md index 62ca1f2..ec2518e 100644 --- a/docs/reference/formulas/configuration_files.md +++ b/docs/reference/formulas/configuration_files.md @@ -11,14 +11,11 @@ The table below shows basic parameters. | `verbose` | `bool` (flag) | `v` |`NOTSET` | Verbose or quiet mode | | `stream` | `bool` (flag) | `s` |`False` | Real time or post-mortem mode | | `sensor-report-sampling-interval` | `int` | N/A | `1000` | The time in milliseconds between two reports (`stream` = `True`) | -| `input` | `string` (Source) | N/A | N/A | Source used as input. More information about Sources and their related parameters can be found [here](../database/sources_destinations.md)| -| `output` | `string` (Destination)| N/A | N/A | Destination used as output. More information about Destinations and their related parameters can be found [here](../database/sources_destinations.md)| +| `input` | `string` | N/A | N/A | SmartWatts input, shall match an existing Sensor output and contain HPWCReports. See [here](./smartwatts.md#smartwatts-inputs) | +| `output` | `string` | N/A | N/A | SmartWatts output to store PowerReport. See [here](./smartwatts.md#smartwatts-outputs) | | `pre-processor` | `string` | N/A | N/A | Pre-Processor to modify reports generated by a sensor. More information about Processors and their related parameters can be found [here](../processors/processors.md) | | `post-processor` | `string` | N/A | N/A | Post-Processor to modify reports generated by a formula. More information about Processors and their related parameters can be found [here](../processors/processors.md) | -???+ tip "Sources and Destinations' values" - - Sources: `mongodb`, `csv`, `socket`, `filedb` - - Destinations: `mongodb`, `influxedb`, `influxedb2`, `csv`, `socket`, `filedb`, `prom` ???+ tip "Processors' values" - Pre-Processors: `k8s`, `libvirt` @@ -52,5 +49,5 @@ PowerAPI Formulas use `json` files. These files follow the next template: $formula_parameters } ``` -???+ info "Sources and Destinations' `json` tags" +???+ info "input and output' `json` tags" More information related to `json` tags for each Source/Destination can be found [here](../database/sources_destinations.md) diff --git a/docs/reference/formulas/smartwatts.md b/docs/reference/formulas/smartwatts.md index ed40ead..6d5c3e8 100644 --- a/docs/reference/formulas/smartwatts.md +++ b/docs/reference/formulas/smartwatts.md @@ -20,6 +20,7 @@ The default installation is done through Docker container. The different images can be found on the [Docker Hub](https://hub.docker.com/r/powerapi/smartwatts-formula/tags) === "Docker" + ``` docker pull powerapi/smartwatts-formula ``` @@ -31,39 +32,14 @@ The different images can be found on the [Docker Hub](https://hub.docker.com/r/p ## Usage -For running the SmartWatts Formula you need: a Source and a Destination, a Sensor that provides `HWPCReports` and a configuration. - -### Input and Output -As any Formula, SmartWatts needs both inputs and outputs. -We can choose those among [this list](../database/sources_destinations.md#summary) - -#### MongoDB as input -MongoDB can be used as input for Reports. -You can start a MongoDB instance via a Docker container by running: -``` -docker run -d --name mongo_output -p 27017:27017 mongo:latest -``` -The different images can be found on the [Docker Hub](https://hub.docker.com/_/mongo/tags) - -#### InfluxDB as output -On the other hand, we can use InfluxDB instance as output for our Power Reports - -You can start an InfluxDB instance, in version >= 2.0.0 via a Docker container by running: -```sh -docker run -p 8086:8086 -v "/tmp/powerapi-influx/data:/var/lib/influxdb2" -v "/tmp/powerapi-influx/config:/etc/influxdb2" influxdb:2 -``` -The different images can be found on the [Docker Hub](https://hub.docker.com/_/influxdb/tags?name=2) - -???+ tip "Set up influxdb 2.X for the first time" - If it is the first time that you are using `influxdb 2.X`, there are several methods (UI, CLI, API) to make a set up. Please check [here](https://docs.influxdata.com/influxdb/v2/get-started/setup/) for more information. +???+ info "Pre-Requisites" + As SmartWatts is a formula, it needs to consume usage Reports from a [Sensor](../sensors/hwpc-sensor.md). Make sure you have Reports made available in a supported storage option. +For running the SmartWatts Formula you need: -### Sensor -[HWPC Sensor](../sensors/hwpc-sensor.md) can be used in order to get `HWPCReports` which provided the necessary information for SmartWatts. -If you wish to use it : -- Install the HWPC Sensor (see [here](../sensors/hwpc-sensor.md#installation)) -- Start the Sensor (see [here](../sensors/hwpc-sensor.md#usage)) - +- a valid configuration +- an input storage +- a output storage containing `HWPCReports` ### Parameters @@ -72,6 +48,23 @@ Besides the [basic parameters](../formulas/configuration_files.md), the followin ???+ info "Hardware dependent values" Some parameters values depend of your hardware. In particular, `cpu-base-freq`. You can obtain this value from `CPU MHz` field by using `lscpu` command. +#### Formula global parameters + +This table resumes the parameters needed for any Formula (and thus, for SmartWatts configuration) : + +| Parameter | Type | CLI shortcut | Default Value | Description | +| ------------ | ----- | ------------- | ------------- | ------------------------------------ | +| `verbose` | `bool` (flag) | `v` |`NOTSET` | Verbose or quiet mode | +| `stream` | `bool` (flag) | `s` |`False` | Real time or post-mortem mode | +| `sensor-report-sampling-interval` | `int` | N/A | `1000` | The time in milliseconds between two reports (`stream` = `True`) | +| `input` | `string` | N/A | N/A | SmartWatts input, shall match an existing Sensor output and contain HPWCReports. See [here](./smartwatts.md#smartwatts-inputs) | +| `output` | `string` | N/A | N/A | SmartWatts output to store PowerReport. See [here](./smartwatts.md#smartwatts-outputs) | +| `pre-processor` | `string` | N/A | N/A | Pre-Processor to modify reports generated by a sensor. More information about Processors and their related parameters can be found [here](../processors/processors.md) | +| `post-processor` | `string` | N/A | N/A | Post-Processor to modify reports generated by a formula. More information about Processors and their related parameters can be found [here](../processors/processors.md) | + +#### SmartWatts specific parameters + +This table resumes the parameters specific to SmartWatts configuration : | Parameter | Type | CLI shortcut | Default Value | Description | | ------------- | ----- | ------------- | ------------- | ------------------------------------ | @@ -88,6 +81,139 @@ Besides the [basic parameters](../formulas/configuration_files.md), the followin |`learn-history-window-size` | `int` | - | `60` | Size of the history window used to keep samples to learn from | |`sensor-reports-frequency` | `int` | - | `1000` | The frequency with which measurements are made (in milliseconds) | +#### SmartWatts Inputs + +As any Formula, SmartWatts needs inputs. +We can choose those among the following list, depending on where your Sensor outputs its reports: + +- [MongoDB](./smartwatts.md#mongodb-input) +- [CSV](./smartwatts.md#csv-input) +- [Socket](./smartwatts.md#socket-input) +- [FileDB](./smartwatts.md#filedb-input) + +##### MongoDB Input + +Table below depicts the different parameters for MongoDB type input: + +| Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | +| ------------- | ----- | ------------- | ------------- | ---------- | ------------------------------------ | +| `uri` | string | `u` | N/A | Yes | The IP address of your MongoDB instance | +| `database` | string | `d` | N/A | Yes | The name of your database | +| `collection` | string | `c` | N/A | Yes | The name of the collection inside `db` | +| `name` | string | `n` | puller_mongodb | No | The related puller name | +| `model` | string | `m` | HWPCReport | No | The Report type stored by the database | + +##### CSV Input + +Table below depicts the different parameters for CSV type input: + +| Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | +| ------------- | ----- | ------------- | ------------- | ----------| ------------------------------------ | +| `files` | string | `f` | "" | No | The list of input CSV files with the format "file1,file2,file3..." | +| `directory` | string | `d` | "." (Current directory) | No |The directory where output CSV files will be written | +| `name` | string | `n` | puller_csv | No | The related puller name | +| `model` | string | `m` | HWPCReport | No | The Report type stored by the database | + +##### Socket input + +Table below depicts the different parameters for CSV type input: + +| Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | +| ------------- | ----- | ------------- | ------------- | ----------| ------------------------------------ | +| `port` | int | `P` | - | Yes | The port of communication | +| `uri` | string | `U` | - | Yes | The IP address of the machine running the socket | +| `name` | string | `n` | puller_socket | No | The related puller name | +| `model` | string | `m` | HWPCReport | No | The Report type stored by the database | + +##### FileDB input + +Table below depicts the different parameters for CSV type input: + +| Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | +| ------------- | ----- | ------------- | ------------- | ----------| ------------------------------------ | +| `filename` | string | `f` | - | yes | Path to the file | +| `name` | string | `n` | puller_filedb| No | The related puller name | +| `model` | string | `m` | HWPCReport | No | The Report type stored by the database | + +#### SmartWatts Outputs + +On the same principle, SmartWatts needs to output its PowerReport. +We can choose those among the following list, depending on where your Sensor outputs its reports: + +- [MongoDB](./smartwatts.md#mongodb-output) +- [CSV](./smartwatts.md#csv-output) +- [InfluxDB](./smartwatts.md#influxdb-input) +- [Prometheus](./smartwatts.md#prometheus-input) +- [FileDB](./smartwatts.md#filedb-input) + +##### MongoDB output + +Table below depicts the different parameters for MongoDB type output: + +| Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | +| ------------- | ----- | ------------- | ------------- | ---------- | ------------------------------------ | +| `uri` | string | `u` | N/A | Yes | The IP address of your MongoDB instance | +| `database` | string | `d` | N/A | Yes | The name of your database | +| `collection` | string | `c` | N/A | Yes | The name of the collection inside `db` | +| `name` | string | `n` | pusher_mongodb | No | The related puller name | +| `model` | string | `m` | PowerReport | No | The Report type stored by the database | + +##### CSV output + +Table below depicts the different parameters for CSV type output: + +| Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | +| ------------- | ----- | ------------- | ------------- | ----------| ------------------------------------ | +| `files` | string | `f` | "" | No | The list of input CSV files with the format "file1,file2,file3..." | +| `directory` | string | `d` | "." (Current directory) | No |The directory where output CSV files will be written | +| `name` | string | `n` | pusher_csv | No | The related puller name | +| `model` | string | `m` | PowerReport | No | The Report type stored by the database | + +##### InfluxDB output + +Table below depicts the different parameters for InfluxDB2 type output: + + +| Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | +| ------------- | ----- | ------------- | ------------- | ---------- | ------------------------------------ | +|`uri` | string | `u` | N/A | Yes | The IP address of your Influxdb instance. It can contain the port number| +|`db` | string | `d` | N/A | Yes | The name of your bucket (database) | +|`port` | int | `p` | None | N/A| The port of communication. It is not mandatory if it is indicated in the `uri` | +|`token` | string | `k` | N/A | Yes | The token for accessing the database. The token owner must have write/read permissions on the bucket | +|`org` | string | `g` | N/A | Yes | The name of the organization associated to the bucket | +|`tags` | string | `t` | N/A | No | List of metadata keys of the report separated by `,` that will be kept. `sensor` and `target` are always kept as report metadata | +|`name` | string | `n` | `"pusher_influxdb2"` | No | The related pusher name | +|`model` | string | `m` | `"PowerReport"` | No | The Report type stored by the database | + +???+ tip "Set up influxdb 2.X for the first time" + If it is the first time that you are using `influxdb 2.X`, there are several methods (UI, CLI, API) to make a set up. Please check [here](https://docs.influxdata.com/influxdb/v2/get-started/setup/) for more information. + +##### Prometheus output + +Table below depicts the different parameters for Prometheus type output: + +| Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | +| ------------- | ----- | ------------- | ------------- | ---------- | ------------------------------------ | +|`uri` | string | `u` | `127.0.0.1` | No | The IP address of your Prometheus instance | +|`port` | int | `p` | N/A | Yes | The port of communication | +|`tags` | string | `t` | N/A | No | List of metadata keys of the report separated by `,` that will be kept. `sensor` and `target` are always kept as report metadata | +|`metric-name` | string | `M` | N/A | Yes | The exposed metric name | +|`metric-description` | string | `d` | `"energy consumption"` | No | The exposed metric description | +|`name` | string | `n` | `"pusher_prom"` | No | The related pusher name | +|`model` | string | `m` | `"PowerReport"` | No | The Report type exposed by Prometheus | + + +##### FileDB output + +Table below depicts the different parameters for FileDB type output: + +| Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | +| ------------- | ----- | ------------- | ------------- | ----------| ------------------------------------ | +| `filename` | string | `f` | - | yes | Path to the file | +| `name` | string | `n` | pusher_filedb | No | The related pusher name | +| `model` | string | `m` | PowerReport | No | The Report type stored by the database | + + ### Running the Formula via CLI parameters In order to run the Formula, you can execute one of the following command lines, depending on the installation you use: @@ -119,7 +245,7 @@ In order to run the Formula, you can execute one of the following command lines, --sensor-reports-frequency 1000 ``` -In this configuration we are using MongoDB as source and InfluxDB 2.X as Destination. +In this configuration we are using MongoDB as input and InfluxDB 2.X as output. ???+ info "Estimations' Storage" Your `PowerReports` will be stored on InfluxDB2. You can watch them in a grafana by using the [following tutorial](../grafana/grafana.md). @@ -135,7 +261,7 @@ Parameters are defined by using the prefixes `POWERAPI_`, `POWERAPI_INPUT_` and - `POWERAPI_INPUT__` - `POWERAPI_OUTPUT__` -where `PARAMETER_NAME` refers to names of parameters in upper case (e.g., `VERBOSE`, `CPU_BASE_FREQ`, `COLLECTION`) and `COMPONENT_NAME` to the name given to the different Sources and Destinations in upper case (e.g., `PULLER` and `PUSHER_POWER`). +where `PARAMETER_NAME` refers to names of parameters in upper case (e.g., `VERBOSE`, `CPU_BASE_FREQ`, `COLLECTION`) and `COMPONENT_NAME` to the name given to the different input and output in upper case (e.g., `PULLER` and `PUSHER_POWER`). Below you find an example for running the Formula with Docker and Pip: @@ -191,7 +317,7 @@ Below you find an example for running the Formula with Docker and Pip: ### Running the Formula with a Configuration File -Below an example is provided by using MongoDB as Source and InfluxDB as Destination. +Below an example is provided by using MongoDB as input and InfluxDB as output. ```json { @@ -223,8 +349,7 @@ Below an example is provided by using MongoDB as Source and InfluxDB as Destinat } ``` -???+ info "Alternative Source or Destination" - If you want to use another Source or Destination, please check the documentation [here](../database/sources_destinations.md) and modify your configuration according to the Source or Destination that you want to use. + Once you have your configuration file, run SmartWatts using one of the following command lines, depending on the installation you use: diff --git a/docs/reference/grafana/grafana.md b/docs/reference/grafana/grafana.md index a25fb02..46a7e35 100644 --- a/docs/reference/grafana/grafana.md +++ b/docs/reference/grafana/grafana.md @@ -6,9 +6,9 @@ Here it is explained how to visualize the power estimation computed by a Formula src="https://powerapi.org/assets/images/reference/grafana/viz_by_process.png" alt="viz_by_process" width="1000px"> -This screenshot shows the visualisation of power consumption of a Web browser and tools used for monitoring (Source, Destination, Sensor, Formula) +This screenshot shows the visualisation of power consumption of a Web browser and tools used for monitoring (Databases, Sensor, Formula) -In this tutorial, we describe how to connect a Formula to a Grafana instance by using InfluxDB 2.X as Destination. +In this tutorial, we describe how to connect a Formula to a Grafana instance by using InfluxDB 2.X as output for PowerReport. Then, we will see how to configure Grafana to visualize the power estimation computed by the Formula. This tutorial assumes that you know how launch a Formula and a Sensor to compute power estimation and that you have an InfluxDB 2.X and a Grafana instances running on your local machine. diff --git a/docs/reference/processors/processors.md b/docs/reference/processors/processors.md index 46c5a71..2fd5ac8 100644 --- a/docs/reference/processors/processors.md +++ b/docs/reference/processors/processors.md @@ -4,7 +4,7 @@ Processors enable customized filtering and/or modifications of `Reports`. There are two kinds of processors: - `PreProcessors`: They are located between the `Puller` and the `Dispatcher`. They are supposed to pre-process the `HWPCReports` before computing estimations. -- `PostProcessors`: They are located between, the `Formula` and the `Pusher`. They process `PowerReports` before storing them on the `Destination`. +- `PostProcessors`: They are located between, the `Formula` and the `Pusher`. They process `PowerReports` before storing them on the output storage option. Figure below depicts where are they introduced in the architecture of a Software `PowerMeters`. @@ -140,7 +140,7 @@ As notice, a `PreProcessor` is defined inside the `pre-processor` group. In this ### Example of Usage with SmartWatts Formula via a Configuration File -Below an example is provided by using MongoDB as Source and InfluxDB as Destination. +Below an example is provided by using MongoDB as input and InfluxDB as output. ```json { diff --git a/docs/reference/sensors/hwpc-sensor.md b/docs/reference/sensors/hwpc-sensor.md index bbbc789..a274555 100644 --- a/docs/reference/sensors/hwpc-sensor.md +++ b/docs/reference/sensors/hwpc-sensor.md @@ -41,14 +41,13 @@ The different images can be found on the [Docker Hub](https://hub.docker.com/r/p Here is a sample to deploy the latest image version available. === "Docker" - ```bash + ```sh docker pull powerapi/hwpc-sensor:latest - ``` ## Usage -An HWPC Sensor instance needs several parameteres to be configured in order to be used. +An HWPC Sensor instance needs several parameters to be configured in order to be used. The following tabs gives a complete overview of available parameters, along with their default values and description. ### Global parameters @@ -63,7 +62,7 @@ The table below shows the different parameters related to the Sensor global conf |`cgroup_basepath` | `string` | `p` | `/sys/fs/cgroup` (`cgroup` V2) | The base path for `cgroups`. To use `cgroup` V1 `/sys/fs/cgroup/perf_event` needs to be used as value | |`system` | `dict` | `s` | - | A system group with a monitoring type and a list of system events (cf. [`system` Group Parameters](hwpc-sensor.md#system-and-container-groups-parameters)) | |`container` | `dict` | `c` | - | A group with a monitoring type and a list of events (cf. [`system` Group Parameters](hwpc-sensor.md#system-and-container-groups-parameters)) | -|`output` | Output | `r` | ` csv` | The [output information](hwpc-sensor.md#output), the Sensor only supports [MongoDB](../database/sources_destinations.md#mongodb) (`mongodb`) and [CSV](../database/sources_destinations.md#csv) (`csv`) as output. | +|`output` | `dict`| `r` | { "type": "csv", "directory": "." } | The [output information](hwpc-sensor.md#output), the Sensor only supports [MongoDB](./hwpc-sensor.md#mongodb-output) (`mongodb`), [CSV](./hwpc-sensor.md#csv-output) (`csv`), [socket](./hwpc-sensor.md#socket-output) and [FileDB](./hwpc-sensor.md#filedb-output) as output. | ### `system` and `container` Groups Parameters @@ -72,7 +71,7 @@ The table below shows the different parameters related to the Sensor `system` an | Parameter | Type | CLI shortcut | Default Value | Description | | ------------- | ----- | ------------- | ------------- | ------------------------------------ | |`events` | `string` | `e` | - | List of events to be monitored. As CLI parameter, each event is indicated with `e`. The structure of events is given [below](hwpc-sensor.md#events) | -|`monitoring_type` | `string` (`MONITOR_ONE_CPU_PER_SOCKET`, `MONITOR_ALL_CPU_PER_SOCKET` ) | `o` (flag) | `MONITOR_ALL_CPU_PER_SOCKET` | The monitoring type. If `o` is specified as CLI parameter, `MONITOR_ONE_CPU_PER_SOCKET` is used as type | +|`monitoring_type` | `string` ( **one of** `MONITOR_ONE_CPU_PER_SOCKET` **or** `MONITOR_ALL_CPU_PER_SOCKET` ) | `o` (flag) | `MONITOR_ALL_CPU_PER_SOCKET` | The monitoring type. If `o` is specified as CLI parameter, `MONITOR_ONE_CPU_PER_SOCKET` is used as type | ### Events @@ -94,27 +93,29 @@ As precised, two kinds of outputs are supported, MongoDB and CSV files. #### MongoDB Output Table below depicts the different parameters for MongoDB type output with HWPC Sensor: + | Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | | ------------- | ----- | ------------- | ------------- | ---------- | ------------------------------------ | | `uri` | string | `U` | N/A | Yes | The IP address of your MongoDB instance | | `database` | string | `D` | N/A | Yes | The name of your database | -| `collection` | string | `HWPCSensor` | N/A | Yes | The name of the collection inside `db` | - -You can start a MongoDB instance via a Docker container by running: - -``` -docker run -d --name mongo_output -p 27017:27017 mongo:latest -``` - -The different images can be found on the [Docker Hub](https://hub.docker.com/_/mongo/tags) +| `collection` | string | `C` | N/A | Yes | The name of the collection inside `db` | #### CSV Output Table below depicts the different parameters for CSV type output: + | Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | | ------------- | ----- | ------------- | ------------- | ----------| ------------------------------------ | | `directory` | string | `U` | "." (Current directory) | No |The directory where output CSV files will be written | +#### Socket Output + +[!TODO] + +#### FileDB Output + +[!TODO] + ### Running the Sensor with a Configuration File The following snippet describe the configuration of an HWPC Sensor instance, writting reports to a MongoDB intance as output: @@ -152,7 +153,8 @@ The following snippet describe the configuration of an HWPC Sensor instance, wri } ``` -The following CLI command shows how to use this configuration file in the deployment of an HWPC Sensor container : +The following CLI command shows how to use this configuration file in the deployment of an HWPC Sensor container : + === "Docker" ```sh diff --git a/mkdocs.yml b/mkdocs.yml index faccb41..d4ea3d8 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -85,8 +85,8 @@ nav: #- RAPL: reference/formulas/rapl.md - SmartWatts: reference/formulas/smartwatts.md - Configuration Files: reference/formulas/configuration_files.md - - Sources/Destinations: - - Description: reference/database/sources_destinations.md +# - Sources/Destinations: +# - Description: reference/database/sources_destinations.md - Processors: - Description: reference/processors/processors.md - Reports: From dff504b8a0cc5fe1d2f94b577b4304b080b60882 Mon Sep 17 00:00:00 2001 From: Inkedstinct Date: Wed, 9 Oct 2024 10:31:45 +0200 Subject: [PATCH 010/108] docs(reports): Add JSON Schema for Reports basis --- .../reports/basis-reports.schema.json | 32 +++++++++++++++++++ docs/reference/reports/reports.md | 29 ++++++++--------- 2 files changed, 45 insertions(+), 16 deletions(-) create mode 100644 docs/reference/reports/basis-reports.schema.json diff --git a/docs/reference/reports/basis-reports.schema.json b/docs/reference/reports/basis-reports.schema.json new file mode 100644 index 0000000..ecc20d0 --- /dev/null +++ b/docs/reference/reports/basis-reports.schema.json @@ -0,0 +1,32 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://raw.githubusercontent.com/powerapi-ng/powerapi-ng.github.io/refs/heads/master/docs/reference/reports/basis-reports.schema.json", + "title": "Report Basis", + "description": "A common basis for PowerAPI Reports", + "type": "object", + "properties": { + "timestamp": { + "type": "string", + "minLength": 1 + }, + "sensor": { + "type": "string", + "minLength": 1 + }, + "target": { + "type": "string", + "minLength": 1 + }, + "report_specific_fields": { + "type": "object", + "properties": {}, + "required": [] + } + }, + "required": [ + "timestamp", + "sensor", + "target", + "report_specific_fields" + ] +} diff --git a/docs/reference/reports/reports.md b/docs/reference/reports/reports.md index 2a4470b..4a9d02e 100644 --- a/docs/reference/reports/reports.md +++ b/docs/reference/reports/reports.md @@ -2,34 +2,31 @@ PowerAPI toolkit is modular, which means any sensor can be plugged to any monitoring tool as long as the needed information is provided. -We fixed the way of encoding the information. Those encoding are called reports. +We fixed the way of encoding the information. Those encoding are called `Reports`. A report type specify the `json` fields that has to be provided to pass information of a certain kind. All reports types have a common basis: -- `timestamp`: at the format "year-month-dayThour:minutes:seconds". The - timestamp reflects the time at which the information correspond, not the - time the information was computed. - For example if a power consumption of a CPU is measured at time `t` and used to - determine the power consumption of a `cgroup` in a `PowerReport`, this report - has timestamp `t`. +- `timestamp`: at the [format](https://en.wikipedia.org/wiki/ISO_8601) "YYYY-MM-DDThh:mm:ss\.sss". The timestamp indicates when the data was collected, not when it was processed. -- `target`: the target is the subject of the measure. For example if - you produce a report that contain information relative to a program, domain, - etc., the target refers to it. It corresponds to the `cgroup` name. +- `target`: The target refers to the entity being measured. For example, if a report contains data related to a specific program, domain, or other entity, the target identifies that subject. In this context, it corresponds to the cgroup name. - `sensor`: it's a name field that is used to identify the reports produced by or computed thanks to a sensor. - Therefore, a report have the following format: +Therefore, a report shall match the following description: - ```json +```json +{ "timestamp":$timestamp, "target":$target, "sensor":$sensor, $report_specific_fields +} +``` + +A valid JSON-Schema to can be found [here](https://raw.githubusercontent.com/powerapi-ng/powerapi-ng.github.io/refs/heads/master/docs/reference/reports/basis-reports.schema.json). - ``` In the following sections we specify the `$report_specific_fields` for each type of report. ## HWPC Reports @@ -42,17 +39,17 @@ Its specific fields are the following: Each group is represented in the same way: - ```json +```json { $type: { $core_number : { $socket_number : { - List of counter and their value + Each counter name and their measured value for the type/core_number/socket_number considered triplet } } } } - ``` +``` Below you can find an example of `HWPCReport`: From 777f5ed92b7a419be451e7ea0083027f5a442ca1 Mon Sep 17 00:00:00 2001 From: Inkedstinct Date: Wed, 9 Oct 2024 11:05:55 +0200 Subject: [PATCH 011/108] docs(grafana): Redirect to Grafana documentation as GUI changes frequently --- docs/reference/grafana/grafana.md | 66 +++++++++++++++---------------- 1 file changed, 32 insertions(+), 34 deletions(-) diff --git a/docs/reference/grafana/grafana.md b/docs/reference/grafana/grafana.md index 46a7e35..8d6e733 100644 --- a/docs/reference/grafana/grafana.md +++ b/docs/reference/grafana/grafana.md @@ -2,20 +2,27 @@ Here it is explained how to visualize the power estimation computed by a Formula on a Grafana dashboard to obtain this kind of visualisation: - +alt="viz_by_process" width="1000px"--> + +This screenshot shows the visualisation of power consumption of a `Firefox` web browser and tools used for monitoring (MongoDB, InfluxDB, Sensor, Formula) -This screenshot shows the visualisation of power consumption of a Web browser and tools used for monitoring (Databases, Sensor, Formula) +In this tutorial, we describe how to connect a Formula to a Grafana instance by using InfluxDB 2.X as output for PowerReport. +Then, we will see how to configure Grafana to visualize the power estimation computed by the Formula. -In this tutorial, we describe how to connect a Formula to a Grafana instance by using InfluxDB 2.X as output for PowerReport. -Then, we will see how to configure Grafana to visualize the power estimation computed by the Formula. +This tutorial assumes that you know how launch a Formula and a Sensor to compute power estimation and that you have an InfluxDB 2.X running on your local machine. -This tutorial assumes that you know how launch a Formula and a Sensor to compute power estimation and that you have an InfluxDB 2.X and a Grafana instances running on your local machine. -The InfluxDB 2.X instance listen on port `8086`. +The InfluxDB 2.X instances listen on port `8086` by default. ## Setup Grafana +The default installation is done through Docker container. +The different images can be found on the [Docker Hub](https://hub.docker.com/r/grafana/grafana) + +Here is an example to deploy the latest image version available. ```sh docker run -d -p 3000:3000 grafana/grafana ``` @@ -24,36 +31,23 @@ After the launch, Grafana will be available at http://localhost:3000. On the sig ## Connect Grafana to the InfluxDB 2.X instance -Connect to your Grafana instance and go to the `"Data sources"`` section (in the configuration part of the side bar). - - - -Click on the `"Add new data source"` button and select `"InfluxDB"`. Enter: - -1. A data source *Name* (here we choose "InfluxDB-2"), -2. A *Query Language*, i.e., `InfluxQL` -3. An instance *URL* (`http://localhost:8086`) -4. A *Custom HTTP Header* called `Authorization` with Value `Token `, where `` is the token provided by InfluxDB 2.X for your organization. -5. A *Database* name, (here we choose `power_consumption`) that is the `db` value of your destination defined in your formula configuration. +Follow [this documentation](https://grafana.com/docs/grafana/latest/datasources/influxdb/#configure-the-data-source) in order to add InfluxDB as a DataSource. +Be carefull to use the documentation matching your Grafana version. -Then click on the `"Save & test"` button. *User* and *Password* are not required as we use a token for authentification. - - - ## Visualize the power consumption on a dashboard in real-time -Go to the `"Dashboard"` section on the side bar and select on `New > New dashboard`. Then click on `+ Add visualisation` Then select `influxdb-2` as data source. +1. Go to the `"Dashboard"` section on the side bar and select on `New > New dashboard` +2. Then click on `+ Add visualisation` +3. Then select the previously added datasource as data source + +![Add Dashboard](../../assets/images/reference/grafana/add_dashboard.png){ width="1000px"} - +alt="add_dashboard" width="1000px"--> -Click on the query edition button (the one with a pencil on it) and write the following query on the `Query` field to request the power estimations from the InfluxDB 2.X measurement: +4. Click on the query edition button (the one with a pencil on it) and write the following query on the `Query` field to request the power estimations from the InfluxDB 2.X measurement: ```sql SELECT power FROM "power_consumption" GROUP BY target @@ -61,12 +55,16 @@ SELECT power FROM "power_consumption" GROUP BY target Then write `$tag_target` on the `ALIAS BY` field to label each graph with the target name - +alt="add_query_by_process" width="600px"--> To display the power consumption in real time, you can update the range of the visualisation to `last 5 minutes` and the `refresh dashboard` parameter to `5s`. This parameter are on the top-right corner of the UI. - +alt="refresh" width="600px"--> From 6497233ed4ffe1f348ca9d32c68a560e4267be30 Mon Sep 17 00:00:00 2001 From: Inkedstinct Date: Wed, 9 Oct 2024 11:30:31 +0200 Subject: [PATCH 012/108] docs(hwpc-sensor): Include Daniel suggestions of providing examples with each possible output --- docs/reference/sensors/hwpc-sensor.md | 107 +++++++++++++++++++------- 1 file changed, 81 insertions(+), 26 deletions(-) diff --git a/docs/reference/sensors/hwpc-sensor.md b/docs/reference/sensors/hwpc-sensor.md index a274555..6fc49a2 100644 --- a/docs/reference/sensors/hwpc-sensor.md +++ b/docs/reference/sensors/hwpc-sensor.md @@ -47,7 +47,6 @@ Here is a sample to deploy the latest image version available. ## Usage -An HWPC Sensor instance needs several parameters to be configured in order to be used. The following tabs gives a complete overview of available parameters, along with their default values and description. ### Global parameters @@ -112,15 +111,15 @@ Table below depicts the different parameters for CSV type output: [!TODO] -#### FileDB Output - -[!TODO] - ### Running the Sensor with a Configuration File -The following snippet describe the configuration of an HWPC Sensor instance, writting reports to a MongoDB intance as output: +The following snippets describe the configuration file of an HWPC Sensor instance, two examples are provided for both possible outputs: + +=== "MongoDB Output" ```json +# config-file.json + { "name": "sensor", "verbose": true, @@ -153,7 +152,42 @@ The following snippet describe the configuration of an HWPC Sensor instance, wri } ``` -The following CLI command shows how to use this configuration file in the deployment of an HWPC Sensor container : +=== "CSV Output" + +```json +# config-file.json + +{ + "name": "sensor", + "verbose": true, + "frequency": 500, + "output": { + "type": "csv", + "directory": "hwpc_reports" + }, + "system": { + "rapl": { + "events": ["RAPL_ENERGY_PKG"], + "monitoring_type": "MONITOR_ONE_CPU_PER_SOCKET" + }, + "msr": { + "events": ["TSC", "APERF", "MPERF"] + } + }, + "container": { + "core": { + "events": [ + "CPU_CLK_THREAD_UNHALTED:REF_P", + "CPU_CLK_THREAD_UNHALTED:THREAD_P", + "LLC_MISSES", + "INSTRUCTIONS_RETIRED" + ] + } + } +} +``` + +The following CLI command shows how to use this configuration file in the deployment of an HWPC Sensor instance as a Docker container : === "Docker" @@ -172,28 +206,49 @@ The following CLI command shows how to use this configuration file in the deploy ### Running the Sensor via CLI parameters -The following CLI command shows how to launch an instance of HWPC Sensor with the same configuration as [above](hwpc-sensor.md#running-the-sensor-with-a-configuration-file) -=== "Docker" +The following CLI command shows how to launch an instance of HWPC Sensor with the same configuration as [above](hwpc-sensor.md#running-the-sensor-with-a-configuration-file), again two example are provided for both possible output: + +=== "Docker with MongoDB output" + +```sh +docker run --rm \ + --net=host \ + --privileged \ + --pid=host \ + -v /sys:/sys \ + -v /var/lib/docker/containers:/var/lib/docker/containers:ro \ + -v /tmp/powerapi-sensor-reporting:/reporting \ + -v $(pwd):/srv \ + powerapi/hwpc-sensor \ + -n "$(hostname -f)" \ + -r "mongodb" -U "mongodb://127.0.0.1" -D "db_sensor" -C "report_0" \ + -s "rapl" -o -e "RAPL_ENERGY_PKG" \ + -s "msr" -e "TSC" -e "APERF" -e "MPERF" \ + -c "core" -e "CPU_CLK_THREAD_UNHALTED:REF_P" -e "CPU_CLK_THREAD_UNHALTED:THREAD_P" -e "LLC_MISSES" -e "INSTRUCTIONS_RETIRED" +``` + +=== "Docker with CSV output" + +```sh +docker run --rm \ + --net=host \ + --privileged \ + --pid=host \ + -v /sys:/sys \ + -v /var/lib/docker/containers:/var/lib/docker/containers:ro \ + -v /tmp/powerapi-sensor-reporting:/reporting \ + -v $(pwd):/srv \ + powerapi/hwpc-sensor \ + -n "$(hostname -f)" \ + -r "csv" -U "hwpc_reports" \ + -s "rapl" -o -e "RAPL_ENERGY_PKG" \ + -s "msr" -e "TSC" -e "APERF" -e "MPERF" \ + -c "core" -e "CPU_CLK_THREAD_UNHALTED:REF_P" -e "CPU_CLK_THREAD_UNHALTED:THREAD_P" -e "LLC_MISSES" -e "INSTRUCTIONS_RETIRED" +``` - ```sh - docker run --rm \ - --net=host \ - --privileged \ - --pid=host \ - -v /sys:/sys \ - -v /var/lib/docker/containers:/var/lib/docker/containers:ro \ - -v /tmp/powerapi-sensor-reporting:/reporting \ - -v $(pwd):/srv \ - powerapi/hwpc-sensor \ - -n "$(hostname -f)" \ - -r "mongodb" -U "mongodb://127.0.0.1" -D "db_sensor" -C "report_0" \ - -s "rapl" -o -e "RAPL_ENERGY_PKG" \ - -s "msr" -e "TSC" -e "APERF" -e "MPERF" \ - -c "core" -e "CPU_CLK_THREAD_UNHALTED:REF_P" -e "CPU_CLK_THREAD_UNHALTED:THREAD_P" -e "LLC_MISSES" -e "INSTRUCTIONS_RETIRED" - ``` ???+ info "Reports' Storage" - Your [`HWPCReports`](../reports/reports.md#hwpc-reports) will be stored on MongoDB. + Your [`HWPCReports`](../reports/reports.md#hwpc-reports) will be stored on MongoDB or in the `hwpc_reports.d`directory regarding the output type selected. ???+ tip "CLI parameters' names" You can only use shortcuts. From 3396b9829b25fe2b4f5c03a436f33a2c059031aa Mon Sep 17 00:00:00 2001 From: Inkedstinct Date: Wed, 9 Oct 2024 15:46:52 +0200 Subject: [PATCH 013/108] docs(getting-started): Refer only 'process' as atomic monitored element --- docs/getting_started.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/getting_started.md b/docs/getting_started.md index 5ca3372..d4c4c47 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -4,7 +4,7 @@ ### Create a cGroup -We need a subset of runnings programs to be monitored. For this, we use the +We need a subset of running processes to be monitored. For this, we use the Linux abstraction of [cGroups](https://www.redhat.com/sysadmin/cgroups-part-one). In order to create a cGroup, the following command can be used from CLI : @@ -16,9 +16,9 @@ cgcreate -g perf_event:new_cgroup_name Check [here](./reference/cgroup/cgroup_v1_activation.md) if you have trouble creating the cgroup. -### Add programs to the group +### Add processes to the group -Once the group created, we need to fill it with programs to be monitored. +Once the group created, we need to fill it with processes to be monitored. To do so, you can use the following : ```sh @@ -26,9 +26,9 @@ cgclassify -g perf_event:new_cgroup_name PID ``` with `PID`, the pid of the process you want to monitor. If you want to monitor a -program composed of many process, replace PID with `$(pidof program_name)`. +process composed of many processes, replace PID with `$(pidof process_name)`. -### Example program to monitor +### Example process to monitor [stress-ng](https://wiki.ubuntu.com/Kernel/Reference/stress-ng) can be used to generate load on one's system. From 88cd731ec54ce31c663d93d7ee93e6a5e6b1ac0d Mon Sep 17 00:00:00 2001 From: Inkedstinct Date: Wed, 9 Oct 2024 15:47:37 +0200 Subject: [PATCH 014/108] docs(global): Improve examples snippet --- docs/reference/formulas/smartwatts.md | 428 +++++++++++++++++--------- docs/reference/overview.md | 4 +- docs/reference/reports/reports.md | 2 +- docs/reference/sensors/hwpc-sensor.md | 235 +++++++------- 4 files changed, 397 insertions(+), 272 deletions(-) diff --git a/docs/reference/formulas/smartwatts.md b/docs/reference/formulas/smartwatts.md index 6d5c3e8..9c23c2b 100644 --- a/docs/reference/formulas/smartwatts.md +++ b/docs/reference/formulas/smartwatts.md @@ -1,13 +1,14 @@ # SmartWatts Formula -SmartWatts is a Formula, a configurable software that can estimate the power consumption of software in real-time. -SmartWatts needs to receive several metrics available in [Reports](../report/report.md), [HWPC Sensor](../sensors/hwpc-sensor.md#events) is a Sensor compatible (i.e making the necessary metrics available in HWPC Reports): +SmartWatts is a configurable Formula that can estimate the power consumption of software in real-time. +SmartWatts needs to receive several metrics available in Sensor's Reports, [HWPCReports](../reports/reports.md#HWPC-reports) is a compatible Report type produced by HWPC-Sensor (i.e making the necessary metrics available in HWPC Reports): - The Running Average Power Limit (`RAPL`) - `msr` events (`TSC`, `APERF`, `MPERF`) - `core` events which depend on the Processor Architucture -These metrics are then used as inputs for a power model that estimates the power consumption of each software, those estimations are recorded in Power Reports. +These raw metrics are then used as inputs for a power model that estimates the power consumption of each process. These estimations are recorded in [Power Reports](../reports/reports.md#power-reports) + The model is calibrated each time a `cpu-error-threshold` is reached by learning a new power model with previous reports. @@ -16,7 +17,7 @@ Software-Defined Power Meter for Containers](https://hal.inria.fr/hal-02470128) ## Installation -The default installation is done through Docker container. +The default installation is done through a Docker container. The different images can be found on the [Docker Hub](https://hub.docker.com/r/powerapi/smartwatts-formula/tags) === "Docker" @@ -33,18 +34,16 @@ The different images can be found on the [Docker Hub](https://hub.docker.com/r/p ## Usage ???+ info "Pre-Requisites" - As SmartWatts is a formula, it needs to consume usage Reports from a [Sensor](../sensors/hwpc-sensor.md). Make sure you have Reports made available in a supported storage option. + As SmartWatts is a formula, it needs to consume compatible usage Reports from a [Sensor](../sensors/hwpc-sensor.md). Make sure you have Reports made available in a supported storage option. For running the SmartWatts Formula you need: - a valid configuration - an input storage -- a output storage containing `HWPCReports` +- a output storage containing compatible `HWPCReports` ### Parameters -Besides the [basic parameters](../formulas/configuration_files.md), the following ones are specific to SmartWatts: - ???+ info "Hardware dependent values" Some parameters values depend of your hardware. In particular, `cpu-base-freq`. You can obtain this value from `CPU MHz` field by using `lscpu` command. @@ -56,11 +55,11 @@ This table resumes the parameters needed for any Formula (and thus, for SmartWat | ------------ | ----- | ------------- | ------------- | ------------------------------------ | | `verbose` | `bool` (flag) | `v` |`NOTSET` | Verbose or quiet mode | | `stream` | `bool` (flag) | `s` |`False` | Real time or post-mortem mode | -| `sensor-report-sampling-interval` | `int` | N/A | `1000` | The time in milliseconds between two reports (`stream` = `True`) | -| `input` | `string` | N/A | N/A | SmartWatts input, shall match an existing Sensor output and contain HPWCReports. See [here](./smartwatts.md#smartwatts-inputs) | -| `output` | `string` | N/A | N/A | SmartWatts output to store PowerReport. See [here](./smartwatts.md#smartwatts-outputs) | -| `pre-processor` | `string` | N/A | N/A | Pre-Processor to modify reports generated by a sensor. More information about Processors and their related parameters can be found [here](../processors/processors.md) | -| `post-processor` | `string` | N/A | N/A | Post-Processor to modify reports generated by a formula. More information about Processors and their related parameters can be found [here](../processors/processors.md) | +| `sensor-report-sampling-interval` | `int` | - | `1000` | The time in milliseconds between two reports (`stream` = `True`) | +| `input` | `string` | - | - | SmartWatts input, shall match an existing Sensor output and contain HPWCReports. See [here](./smartwatts.md#smartwatts-inputs) | +| `output` | `string` | - | - | SmartWatts output to store PowerReport. See [here](./smartwatts.md#smartwatts-outputs) | +| `pre-processor` | `string` | - | - | Pre-Processor to modify reports generated by a sensor. More information about Processors and their related parameters can be found [here](../processors/processors.md) | +| `post-processor` | `string` | - | - | Post-Processor to modify reports generated by a formula. More information about Processors and their related parameters can be found [here](../processors/processors.md) | #### SmartWatts specific parameters @@ -84,7 +83,7 @@ This table resumes the parameters specific to SmartWatts configuration : #### SmartWatts Inputs As any Formula, SmartWatts needs inputs. -We can choose those among the following list, depending on where your Sensor outputs its reports: +We can choose those among the following list, depending on where your Sensor outputs its PowerReports: - [MongoDB](./smartwatts.md#mongodb-input) - [CSV](./smartwatts.md#csv-input) @@ -97,9 +96,9 @@ Table below depicts the different parameters for MongoDB type input: | Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | | ------------- | ----- | ------------- | ------------- | ---------- | ------------------------------------ | -| `uri` | string | `u` | N/A | Yes | The IP address of your MongoDB instance | -| `database` | string | `d` | N/A | Yes | The name of your database | -| `collection` | string | `c` | N/A | Yes | The name of the collection inside `db` | +| `uri` | string | `u` | - | Yes | The IP address of your MongoDB instance | +| `database` | string | `d` | - | Yes | The name of your database | +| `collection` | string | `c` | - | Yes | The name of the collection inside `db` | | `name` | string | `n` | puller_mongodb | No | The related puller name | | `model` | string | `m` | HWPCReport | No | The Report type stored by the database | @@ -138,13 +137,13 @@ Table below depicts the different parameters for CSV type input: #### SmartWatts Outputs On the same principle, SmartWatts needs to output its PowerReport. -We can choose those among the following list, depending on where your Sensor outputs its reports: +We can choose it among the following list, depending on where your Sensor outputs its PowerReports: - [MongoDB](./smartwatts.md#mongodb-output) - [CSV](./smartwatts.md#csv-output) -- [InfluxDB](./smartwatts.md#influxdb-input) -- [Prometheus](./smartwatts.md#prometheus-input) -- [FileDB](./smartwatts.md#filedb-input) +- [InfluxDB](./smartwatts.md#influxdb-output) +- [Prometheus](./smartwatts.md#prometheus-output) +- [FileDB](./smartwatts.md#filedb-output) ##### MongoDB output @@ -152,9 +151,9 @@ Table below depicts the different parameters for MongoDB type output: | Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | | ------------- | ----- | ------------- | ------------- | ---------- | ------------------------------------ | -| `uri` | string | `u` | N/A | Yes | The IP address of your MongoDB instance | -| `database` | string | `d` | N/A | Yes | The name of your database | -| `collection` | string | `c` | N/A | Yes | The name of the collection inside `db` | +| `uri` | string | `u` | - | Yes | The IP address of your MongoDB instance | +| `database` | string | `d` | - | Yes | The name of your database | +| `collection` | string | `c` | - | Yes | The name of the collection inside `db` | | `name` | string | `n` | pusher_mongodb | No | The related puller name | | `model` | string | `m` | PowerReport | No | The Report type stored by the database | @@ -171,23 +170,23 @@ Table below depicts the different parameters for CSV type output: ##### InfluxDB output -Table below depicts the different parameters for InfluxDB2 type output: +???+ tip "Set up influxdb 2.X for the first time" + If it is the first time that you are using `influxdb 2.X`, there are several methods (UI, CLI, API) to make a set up. Please check [here](https://docs.influxdata.com/influxdb/v2/get-started/setup/) for more information. + +Table below depicts the different parameters for InfluxDB2 type output: | Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | | ------------- | ----- | ------------- | ------------- | ---------- | ------------------------------------ | -|`uri` | string | `u` | N/A | Yes | The IP address of your Influxdb instance. It can contain the port number| -|`db` | string | `d` | N/A | Yes | The name of your bucket (database) | -|`port` | int | `p` | None | N/A| The port of communication. It is not mandatory if it is indicated in the `uri` | -|`token` | string | `k` | N/A | Yes | The token for accessing the database. The token owner must have write/read permissions on the bucket | -|`org` | string | `g` | N/A | Yes | The name of the organization associated to the bucket | -|`tags` | string | `t` | N/A | No | List of metadata keys of the report separated by `,` that will be kept. `sensor` and `target` are always kept as report metadata | +|`uri` | string | `u` | - | Yes | The IP address of your Influxdb instance. It can contain the port number| +|`db` | string | `d` | - | Yes | The name of your bucket (database) | +|`port` | int | `p` | - | Yes | The port of communication. It is not mandatory if it is indicated in the `uri` | +|`token` | string | `k` | - | Yes | The token for accessing the database. The token owner must have write/read permissions on the bucket | +|`org` | string | `g` | - | Yes | The name of the organization associated to the bucket | +|`tags` | string | `t` | - | No | List of metadata keys of the report separated by `,` that will be kept. `sensor` and `target` are always kept as report metadata | |`name` | string | `n` | `"pusher_influxdb2"` | No | The related pusher name | |`model` | string | `m` | `"PowerReport"` | No | The Report type stored by the database | -???+ tip "Set up influxdb 2.X for the first time" - If it is the first time that you are using `influxdb 2.X`, there are several methods (UI, CLI, API) to make a set up. Please check [here](https://docs.influxdata.com/influxdb/v2/get-started/setup/) for more information. - ##### Prometheus output Table below depicts the different parameters for Prometheus type output: @@ -195,9 +194,9 @@ Table below depicts the different parameters for Prometheus type output: | Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | | ------------- | ----- | ------------- | ------------- | ---------- | ------------------------------------ | |`uri` | string | `u` | `127.0.0.1` | No | The IP address of your Prometheus instance | -|`port` | int | `p` | N/A | Yes | The port of communication | -|`tags` | string | `t` | N/A | No | List of metadata keys of the report separated by `,` that will be kept. `sensor` and `target` are always kept as report metadata | -|`metric-name` | string | `M` | N/A | Yes | The exposed metric name | +|`port` | int | `p` | - | Yes | The port of communication | +|`tags` | string | `t` | - | No | List of metadata keys of the report separated by `,` that will be kept. `sensor` and `target` are always kept as report metadata | +|`metric-name` | string | `M` | - | Yes | The exposed metric name | |`metric-description` | string | `d` | `"energy consumption"` | No | The exposed metric description | |`name` | string | `n` | `"pusher_prom"` | No | The related pusher name | |`model` | string | `m` | `"PowerReport"` | No | The Report type exposed by Prometheus | @@ -209,51 +208,87 @@ Table below depicts the different parameters for FileDB type output: | Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | | ------------- | ----- | ------------- | ------------- | ----------| ------------------------------------ | -| `filename` | string | `f` | - | yes | Path to the file | +| `filename` | string | `f` | - | Yes | Path to the file | | `name` | string | `n` | pusher_filedb | No | The related pusher name | | `model` | string | `m` | PowerReport | No | The Report type stored by the database | +#### Running the Formula -### Running the Formula via CLI parameters - -In order to run the Formula, you can execute one of the following command lines, depending on the installation you use: - -=== "Docker" +You will find below different examples in order to run a Formula, depending on your use case: +- [Command-Line Interface](#running-the-formula-via-cli-parameters) +- [Environment variables](#running-the-formula-via-environment-variables) +- [Configuration File](#running-the-formula-via-configuration-file) - ```sh - docker run -t \ - --net=host \ - powerapi/smartwatts-formula --verbose \ - --input mongodb --model HWPCReport --uri mongodb://127.0.0.1 --db test --collection prep \ - --output influxdb2 --model PowerReport --uri 127.0.0.1 --port 8086 --db power_consumption --org org_test --token mytoken \ - --cpu-base-freq 1900 \ - --cpu-error-threshold 2.0 \ - --disable-dram-formula \ - --sensor-reports-frequency 1000 - ``` +!!! tip "Getting Started" + You can also find a complete stack deployment example in the [Getting Started Section](../../getting_started.md) ! -=== "Pip" +#### Running the Formula via CLI parameters - ```sh - python -m smartwatts \ - --verbose \ - --input mongodb --model HWPCReport --uri mongodb://127.0.0.1 --db test --collection prep \ - --output influxdb2 --model PowerReport --uri 127.0.0.1 --port 8086 --db power_consumption --org org_test --token mytoken\ - --cpu-base-freq 1900 \ - --cpu-error-threshold 2.0 \ - --disable-dram-formula \ - --sensor-reports-frequency 1000 - ``` +In order to run the Formula, you can execute one of the following command lines, depending on the installation you use: -In this configuration we are using MongoDB as input and InfluxDB 2.X as output. +???+ example "Examples using docker" + + === "Docker with MongoDB/InfluxDB2" + + ```sh hl_lines="4 5" + docker run -t \ + --net=host \ + powerapi/smartwatts-formula --verbose \ + --input mongodb --model HWPCReport --uri mongodb://127.0.0.1 --db test --collection prep \ + --output influxdb2 --model PowerReport --uri 127.0.0.1 --port 8086 --db power_consumption --org org_test --token mytoken \ + --cpu-base-freq 1900 \ + --cpu-error-threshold 2.0 \ + --disable-dram-formula \ + --sensor-reports-frequency 1000 + ``` + + === "Docker with CSV/InfluxDB2" + + ```sh hl_lines="4 5" + docker run -t \ + --net=host \ + powerapi/smartwatts-formula --verbose \ + --input csv --model HWPCReport --directory hwcp\_reports.d --files "hwpc\_report\_1, hwpc\_report\_2" \ + --output influxdb2 --model PowerReport --uri 127.0.0.1 --port 8086 --db power_consumption --org org_test --token mytoken \ + --cpu-base-freq 1900 \ + --cpu-error-threshold 2.0 \ + --disable-dram-formula \ + --sensor-reports-frequency 1000 + ``` + +???+ example "Examples using Pip" + + === "Pip with MongoDB/InfluxDB2" + + ```sh hl_lines="3 4" + python -m smartwatts \ + --verbose \ + --input mongodb --model HWPCReport --uri mongodb://127.0.0.1 --db test --collection prep \ + --output influxdb2 --model PowerReport --uri 127.0.0.1 --port 8086 --db power_consumption --org org_test --token mytoken\ + --cpu-base-freq 1900 \ + --cpu-error-threshold 2.0 \ + --disable-dram-formula \ + --sensor-reports-frequency 1000 + ``` + + === "Pip with CSV/InfluxDB2" + + ```sh hl_lines="3 4" + python -m smartwatts \ + --verbose \ + --input csv --model HWPCReport --directory hwcp\_reports.d --name puller\_csv --files "hwpc\_report\_1.json, hwpc\_report\_2.json" \ + --output influxdb2 --model PowerReport --uri 127.0.0.1 --port 8086 --db power_consumption --org org_test --token mytoken\ + --cpu-base-freq 1900 \ + --cpu-error-threshold 2.0 \ + --disable-dram-formula \ + --sensor-reports-frequency 1000 + ``` ???+ info "Estimations' Storage" Your `PowerReports` will be stored on InfluxDB2. You can watch them in a grafana by using the [following tutorial](../grafana/grafana.md). -???+ tip "Using shortcuts for parameters' names" - You use `-` instead of `--`. -### Running the Formula with Environment Variables +#### Running the Formula with Environment Variables Parameters are defined by using the prefixes `POWERAPI_`, `POWERAPI_INPUT_` and `POWERAPI_OUTPUT_` in the names of Environment Variables. The following conventions are used: @@ -265,98 +300,183 @@ where `PARAMETER_NAME` refers to names of parameters in upper case (e.g., `VERBO Below you find an example for running the Formula with Docker and Pip: -=== "Docker" - - ```sh - docker run -t \ - --net=host \ - -e POWERAPI_VERBOSE=true \ - -e POWERAPI_STREAM=true \ - -e POWERAPI_CPU_BASE_FREQ=1900 \ - -e POWERAPI_CPU_ERROR_THRESHOLD=2.0 \ - -e POWERAPI_DISABLE_DRAM_FORMULA=true \ - -e POWERAPI_SENSOR_REPORTS_FREQUENCY=1000 \ - -e POWERAPI_INPUT_PULLER_MODEL=HWPCReport \ - -e POWERAPI_INPUT_PULLER_TYPE=mongodb \ - -e POWERAPI_INPUT_PULLER_URI=mongodb://127.0.0.1 \ - -e POWERAPI_INPUT_PULLER_DB=test \ - -e POWERAPI_INPUT_PULLER_COLLECTION=prep \ - -e POWERAPI_OUTPUT_PUSHER_POWER_MODEL=PowerReport \ - -e POWERAPI_OUTPUT_PUSHER_POWER_TYPE=influxdb2 \ - -e POWERAPI_OUTPUT_PUSHER_POWER_URI=127.0.0.1 \ - -e POWERAPI_OUTPUT_PUSHER_POWER_PORT=8086 \ - -e POWERAPI_OUTPUT_PUSHER_POWER_DB=power_consumption \ - -e POWERAPI_OUTPUT_PUSHER_POWER_ORG=org_test \ - -e POWERAPI_OUTPUT_PUSHER_POWER_TOKEN=mytoken \ - powerapi/smartwatts-formula - ``` - -=== "Pip" - - ```sh - export POWERAPI_VERBOSE=true - export POWERAPI_STREAM=false - export POWERAPI_CPU_BASE_FREQ=1900 - export POWERAPI_CPU_ERROR_THRESHOLD=2.0 - export POWERAPI_DISABLE_DRAM_FORMULA=true - export POWERAPI_SENSOR_REPORTS_FREQUENCY=1000 - export POWERAPI_INPUT_PULLER_MODEL=HWPCReport - export POWERAPI_INPUT_PULLER_TYPE=mongodb - export POWERAPI_INPUT_PULLER_URI=mongodb://127.0.0.1 - export POWERAPI_INPUT_PULLER_DB=test - export POWERAPI_INPUT_PULLER_COLLECTION=prep - export POWERAPI_OUTPUT_PUSHER_POWER_MODEL=PowerReport - export POWERAPI_OUTPUT_PUSHER_POWER_TYPE=influxdb2 - export POWERAPI_OUTPUT_PUSHER_POWER_URI=127.0.0.1 - export POWERAPI_OUTPUT_PUSHER_POWER_PORT=8086 - export POWERAPI_OUTPUT_PUSHER_POWER_DB=power_consumption - export POWERAPI_OUTPUT_PUSHER_POWER_ORG=org_test - export POWERAPI_OUTPUT_PUSHER_POWER_TOKEN=mytoken - python -m smartwatts - ``` - -### Running the Formula with a Configuration File - -Below an example is provided by using MongoDB as input and InfluxDB as output. - -```json -{ - "verbose": true, - "stream": true, - "input": { - "puller": { - "model": "HWPCReport", - "type": "mongodb", - "uri": "mongodb://127.0.0.1", - "db": "test", - "collection": "prep" - } - }, - "output": { - "pusher_power": { - "type": "influxdb2", - "uri": "127.0.0.1", - "port": 8086, - "db": "power_consumption", - "org": "org_test", - "token": "mytoken" - } - }, - "cpu-base-freq": 1900, - "cpu-error-threshold": 2.0, - "disable-dram-formula": true, - "sensor-reports-frequency": 1000 -} -``` - +???+ example "Examples using docker" + + === "Docker with MongoDB/InfluxDB2" + + ```sh hl_lines="9 10 11 12 13 14 15 16 17 18 19 20" + docker run -t \ + --net=host \ + -e POWERAPI_VERBOSE=true \ + -e POWERAPI_STREAM=true \ + -e POWERAPI_CPU_BASE_FREQ=1900 \ + -e POWERAPI_CPU_ERROR_THRESHOLD=2.0 \ + -e POWERAPI_DISABLE_DRAM_FORMULA=true \ + -e POWERAPI_SENSOR_REPORTS_FREQUENCY=1000 \ + -e POWERAPI_INPUT_PULLER_MODEL=HWPCReport \ + -e POWERAPI_INPUT_PULLER_TYPE=mongodb \ + -e POWERAPI_INPUT_PULLER_URI=mongodb://127.0.0.1 \ + -e POWERAPI_INPUT_PULLER_DB=test \ + -e POWERAPI_INPUT_PULLER_COLLECTION=prep \ + -e POWERAPI_OUTPUT_PUSHER_POWER_MODEL=PowerReport \ + -e POWERAPI_OUTPUT_PUSHER_POWER_TYPE=influxdb2 \ + -e POWERAPI_OUTPUT_PUSHER_POWER_URI=127.0.0.1 \ + -e POWERAPI_OUTPUT_PUSHER_POWER_PORT=8086 \ + -e POWERAPI_OUTPUT_PUSHER_POWER_DB=power_consumption \ + -e POWERAPI_OUTPUT_PUSHER_POWER_ORG=org_test \ + -e POWERAPI_OUTPUT_PUSHER_POWER_TOKEN=mytoken \ + powerapi/smartwatts-formula + ``` + + === "Docker with CSV/InfluxDB2" + + ```sh hl_lines="9 10 11 12 13 14 15 16 17 18 19" + docker run -t \ + --net=host \ + -e POWERAPI_VERBOSE=true \ + -e POWERAPI_STREAM=true \ + -e POWERAPI_CPU_BASE_FREQ=1900 \ + -e POWERAPI_CPU_ERROR_THRESHOLD=2.0 \ + -e POWERAPI_DISABLE_DRAM_FORMULA=true \ + -e POWERAPI_SENSOR_REPORTS_FREQUENCY=1000 \ + -e POWERAPI_INPUT_PULLER_MODEL=HWPCReport \ + -e POWERAPI_INPUT_PULLER_TYPE=csv \ + -e POWERAPI_INPUT_PULLER_DIRECTORY=hwpc_reports.d \ + -e POWERAPI_INPUT_PULLER_files="hwpc_report_1.json, hwpc_report_2.json" \ + -e POWERAPI_OUTPUT_PUSHER_POWER_MODEL=PowerReport \ + -e POWERAPI_OUTPUT_PUSHER_POWER_TYPE=influxdb2 \ + -e POWERAPI_OUTPUT_PUSHER_POWER_URI=127.0.0.1 \ + -e POWERAPI_OUTPUT_PUSHER_POWER_PORT=8086 \ + -e POWERAPI_OUTPUT_PUSHER_POWER_DB=power_consumption \ + -e POWERAPI_OUTPUT_PUSHER_POWER_ORG=org_test \ + -e POWERAPI_OUTPUT_PUSHER_POWER_TOKEN=mytoken \ + powerapi/smartwatts-formula + ``` + + +???+ example "Examples using docker" + + === "Pip with MongoDB/InfluxDB2" + + ```sh hl_lines="7 8 9 10 11 12 13 14 15 16 17 18" + export POWERAPI_VERBOSE=true + export POWERAPI_STREAM=false + export POWERAPI_CPU_BASE_FREQ=1900 + export POWERAPI_CPU_ERROR_THRESHOLD=2.0 + export POWERAPI_DISABLE_DRAM_FORMULA=true + export POWERAPI_SENSOR_REPORTS_FREQUENCY=1000 + export POWERAPI_INPUT_PULLER_MODEL=HWPCReport + export POWERAPI_INPUT_PULLER_TYPE=mongodb + export POWERAPI_INPUT_PULLER_URI=mongodb://127.0.0.1 + export POWERAPI_INPUT_PULLER_DB=test + export POWERAPI_INPUT_PULLER_COLLECTION=prep + export POWERAPI_OUTPUT_PUSHER_POWER_MODEL=PowerReport + export POWERAPI_OUTPUT_PUSHER_POWER_TYPE=influxdb2 + export POWERAPI_OUTPUT_PUSHER_POWER_URI=127.0.0.1 + export POWERAPI_OUTPUT_PUSHER_POWER_PORT=8086 + export POWERAPI_OUTPUT_PUSHER_POWER_DB=power_consumption + export POWERAPI_OUTPUT_PUSHER_POWER_ORG=org_test + export POWERAPI_OUTPUT_PUSHER_POWER_TOKEN=mytoken + python -m smartwatts + ``` + + === "Pip with CSV/InfluxDB2" + + ```sh hl_lines="7 8 9 10 11 12 13 14 15 16 17" + export POWERAPI_VERBOSE=true + export POWERAPI_STREAM=false + export POWERAPI_CPU_BASE_FREQ=1900 + export POWERAPI_CPU_ERROR_THRESHOLD=2.0 + export POWERAPI_DISABLE_DRAM_FORMULA=true + export POWERAPI_SENSOR_REPORTS_FREQUENCY=1000 + export POWERAPI_INPUT_PULLER_MODEL=HWPCReport \ + export POWERAPI_INPUT_PULLER_TYPE=csv \ + export POWERAPI_INPUT_PULLER_DIRECTORY=hwpc_reports.d \ + export POWERAPI_INPUT_PULLER_files="hwpc_report_1.json, hwpc_report_2.json" \ + export POWERAPI_OUTPUT_PUSHER_POWER_MODEL=PowerReport + export POWERAPI_OUTPUT_PUSHER_POWER_TYPE=influxdb2 + export POWERAPI_OUTPUT_PUSHER_POWER_URI=127.0.0.1 + export POWERAPI_OUTPUT_PUSHER_POWER_PORT=8086 + export POWERAPI_OUTPUT_PUSHER_POWER_DB=power_consumption + export POWERAPI_OUTPUT_PUSHER_POWER_ORG=org_test + export POWERAPI_OUTPUT_PUSHER_POWER_TOKEN=mytoken + python -m smartwatts + ``` + +#### Running the Formula with a Configuration File + +Below you find example Configuration Files to use different input/output and how to use it with Docker or Pip: + +???+ example "Examples configurations files" + + === "Configuration file using MongoDB/InfluxDB2" + ```json hl_lines="5 6 7 8 9 10 11 14 15 16 17 18 19 20 21" title="config_file.json" + { + "verbose": true, + "stream": true, + "input": { + "puller": { + "model": "HWPCReport", + "type": "mongodb", + "uri": "mongodb://127.0.0.1", + "db": "test", + "collection": "prep" + } + }, + "output": { + "pusher_power": { + "type": "influxdb2", + "uri": "127.0.0.1", + "port": 8086, + "db": "power_consumption", + "org": "org_test", + "token": "mytoken" + } + }, + "cpu-base-freq": 1900, + "cpu-error-threshold": 2.0, + "disable-dram-formula": true, + "sensor-reports-frequency": 1000 + } + ``` + + === "Configuration file using CSV/InfluxDB2" + ```json hl_lines="5 6 7 8 9 10 13 14 15 16 17 18 19 20" title="config_file.json" + { + "verbose": true, + "stream": true, + "input": { + "puller": { + "model": "HWPCReport", + "type": "csv", + "directory": "hwpc_reports.d", + "files": "hwpc_report_1.json, hwpc_report_2.json", + } + }, + "output": { + "pusher_power": { + "type": "influxdb2", + "uri": "127.0.0.1", + "port": 8086, + "db": "power_consumption", + "org": "org_test", + "token": "mytoken" + } + }, + "cpu-base-freq": 1900, + "cpu-error-threshold": 2.0, + "disable-dram-formula": true, + "sensor-reports-frequency": 1000 + } + ``` Once you have your configuration file, run SmartWatts using one of the following command lines, depending on the installation you use: === "Docker" - ```sh + ```sh hl_lines="4" docker run -t \ --net=host \ -v $(pwd)/config_file.json:/config_file.json \ diff --git a/docs/reference/overview.md b/docs/reference/overview.md index a54dab2..b8607cc 100644 --- a/docs/reference/overview.md +++ b/docs/reference/overview.md @@ -14,8 +14,8 @@ The diagram below illustrates the overall architecture of a PowerMeter within th ![PowerAPI Architecture Overview](../assets/images/reference/overview/global-architecture.jpg){ width="1000px"} A PowerMeter consists of two essential components: -- A [Sensor](./overview.md#Sensor), which collects system usage metrics and generates usage reports. -- A [Formula](./overview.md#Formula), which applies a computational model to the usage reports, producing power consumption data (in mJ). +- A [Sensor](#Sensor), which collects system usage metrics and generates usage reports. +- A [Formula](#Formula), which applies a computational model to the usage reports, producing power consumption data (in mJ). Additionally, [Preprocessors](./overview.md#Preprocessors) can be utilized to modify usage reports before they are processed by the Formula. diff --git a/docs/reference/reports/reports.md b/docs/reference/reports/reports.md index 4a9d02e..25a1dfb 100644 --- a/docs/reference/reports/reports.md +++ b/docs/reference/reports/reports.md @@ -8,7 +8,7 @@ A report type specify the `json` fields that has to be provided to pass informat a certain kind. All reports types have a common basis: -- `timestamp`: at the [format](https://en.wikipedia.org/wiki/ISO_8601) "YYYY-MM-DDThh:mm:ss\.sss". The timestamp indicates when the data was collected, not when it was processed. +- `timestamp`: at the [format](https://en.wikipedia.org/wiki/ISO_8601) "YYYY-MM-DDThh\:mm\:ss\.sss". The timestamp indicates when the data was collected, not when it was processed. - `target`: The target refers to the entity being measured. For example, if a report contains data related to a specific program, domain, or other entity, the target identifies that subject. In this context, it corresponds to the cgroup name. diff --git a/docs/reference/sensors/hwpc-sensor.md b/docs/reference/sensors/hwpc-sensor.md index 6fc49a2..d2c8871 100644 --- a/docs/reference/sensors/hwpc-sensor.md +++ b/docs/reference/sensors/hwpc-sensor.md @@ -35,7 +35,7 @@ The sensor provides raw values of performance counters as well as `RAPL` raw val ## Installation -The default installation is done through Docker container. +The default installation is done through a Docker container. The different images can be found on the [Docker Hub](https://hub.docker.com/r/powerapi/hwpc-sensor/tags) Here is a sample to deploy the latest image version available. @@ -61,7 +61,7 @@ The table below shows the different parameters related to the Sensor global conf |`cgroup_basepath` | `string` | `p` | `/sys/fs/cgroup` (`cgroup` V2) | The base path for `cgroups`. To use `cgroup` V1 `/sys/fs/cgroup/perf_event` needs to be used as value | |`system` | `dict` | `s` | - | A system group with a monitoring type and a list of system events (cf. [`system` Group Parameters](hwpc-sensor.md#system-and-container-groups-parameters)) | |`container` | `dict` | `c` | - | A group with a monitoring type and a list of events (cf. [`system` Group Parameters](hwpc-sensor.md#system-and-container-groups-parameters)) | -|`output` | `dict`| `r` | { "type": "csv", "directory": "." } | The [output information](hwpc-sensor.md#output), the Sensor only supports [MongoDB](./hwpc-sensor.md#mongodb-output) (`mongodb`), [CSV](./hwpc-sensor.md#csv-output) (`csv`), [socket](./hwpc-sensor.md#socket-output) and [FileDB](./hwpc-sensor.md#filedb-output) as output. | +|`output` | `dict`| `r` | { "type": "csv", "directory": "." } | The [output information](hwpc-sensor.md#output), the Sensor only supports [MongoDB](./hwpc-sensor.md#mongodb-output) (`mongodb`), [CSV](./hwpc-sensor.md#csv-output) (`csv`) and [socket](./hwpc-sensor.md#socket-output) as output. | ### `system` and `container` Groups Parameters @@ -95,9 +95,9 @@ Table below depicts the different parameters for MongoDB type output with HWPC S | Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | | ------------- | ----- | ------------- | ------------- | ---------- | ------------------------------------ | -| `uri` | string | `U` | N/A | Yes | The IP address of your MongoDB instance | -| `database` | string | `D` | N/A | Yes | The name of your database | -| `collection` | string | `C` | N/A | Yes | The name of the collection inside `db` | +| `uri` | string | `U` | - | Yes | The IP address of your MongoDB instance | +| `database` | string | `D` | - | Yes | The name of your database | +| `collection` | string | `C` | - | Yes | The name of the collection inside `db` | #### CSV Output @@ -109,89 +109,92 @@ Table below depicts the different parameters for CSV type output: #### Socket Output -[!TODO] +Table below depicts the different parameters for Socket type output: + +| Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | +| ------------- | ----- | ------------- | ------------- | ----------| ------------------------------------ | +| `uri` | string | `U` | - | Yes | The IP address of the machine running the socket | +| `port` | int | `P` | - | Yes | The port of communication | ### Running the Sensor with a Configuration File The following snippets describe the configuration file of an HWPC Sensor instance, two examples are provided for both possible outputs: -=== "MongoDB Output" - -```json -# config-file.json - -{ - "name": "sensor", - "verbose": true, - "frequency": 500, - "output": { - "type": "mongodb", - "uri": "mongodb://127.0.0.1", - "database": "db_sensor", - "collection": "report_0" - }, - "system": { - "rapl": { - "events": ["RAPL_ENERGY_PKG"], - "monitoring_type": "MONITOR_ONE_CPU_PER_SOCKET" - }, - "msr": { - "events": ["TSC", "APERF", "MPERF"] - } - }, - "container": { - "core": { - "events": [ - "CPU_CLK_THREAD_UNHALTED:REF_P", - "CPU_CLK_THREAD_UNHALTED:THREAD_P", - "LLC_MISSES", - "INSTRUCTIONS_RETIRED" - ] - } - } -} -``` - -=== "CSV Output" - -```json -# config-file.json - -{ - "name": "sensor", - "verbose": true, - "frequency": 500, - "output": { - "type": "csv", - "directory": "hwpc_reports" - }, - "system": { - "rapl": { - "events": ["RAPL_ENERGY_PKG"], - "monitoring_type": "MONITOR_ONE_CPU_PER_SOCKET" - }, - "msr": { - "events": ["TSC", "APERF", "MPERF"] - } - }, - "container": { - "core": { - "events": [ - "CPU_CLK_THREAD_UNHALTED:REF_P", - "CPU_CLK_THREAD_UNHALTED:THREAD_P", - "LLC_MISSES", - "INSTRUCTIONS_RETIRED" - ] - } - } -} -``` +???+ example "Examples using a Configuration File" + + === "MongoDB Output" + + ```json hl_lines="7 8 9 10" title="config_file.json" + { + "name": "sensor", + "verbose": true, + "frequency": 500, + "output": { + "type": "mongodb", + "uri": "mongodb://127.0.0.1", + "database": "db_sensor", + "collection": "report_0" + }, + "system": { + "rapl": { + "events": ["RAPL_ENERGY_PKG"], + "monitoring_type": "MONITOR_ONE_CPU_PER_SOCKET" + }, + "msr": { + "events": ["TSC", "APERF", "MPERF"] + } + }, + "container": { + "core": { + "events": [ + "CPU_CLK_THREAD_UNHALTED:REF_P", + "CPU_CLK_THREAD_UNHALTED:THREAD_P", + "LLC_MISSES", + "INSTRUCTIONS_RETIRED" + ] + } + } + } + ``` + + === "CSV Output" + + ```json hl_lines="6 7" title="config_file.json" + { + "name": "sensor", + "verbose": true, + "frequency": 500, + "output": { + "type": "csv", + "directory": "hwpc_reports" + }, + "system": { + "rapl": { + "events": ["RAPL_ENERGY_PKG"], + "monitoring_type": "MONITOR_ONE_CPU_PER_SOCKET" + }, + "msr": { + "events": ["TSC", "APERF", "MPERF"] + } + }, + "container": { + "core": { + "events": [ + "CPU_CLK_THREAD_UNHALTED:REF_P", + "CPU_CLK_THREAD_UNHALTED:THREAD_P", + "LLC_MISSES", + "INSTRUCTIONS_RETIRED" + ] + } + } + } + ``` The following CLI command shows how to use this configuration file in the deployment of an HWPC Sensor instance as a Docker container : === "Docker" - ```sh + ```sh hl_lines="9 10" docker run --rm \ --net=host \ --privileged \ @@ -208,43 +211,45 @@ The following CLI command shows how to use this configuration file in the deploy The following CLI command shows how to launch an instance of HWPC Sensor with the same configuration as [above](hwpc-sensor.md#running-the-sensor-with-a-configuration-file), again two example are provided for both possible output: -=== "Docker with MongoDB output" - -```sh -docker run --rm \ - --net=host \ - --privileged \ - --pid=host \ - -v /sys:/sys \ - -v /var/lib/docker/containers:/var/lib/docker/containers:ro \ - -v /tmp/powerapi-sensor-reporting:/reporting \ - -v $(pwd):/srv \ - powerapi/hwpc-sensor \ - -n "$(hostname -f)" \ - -r "mongodb" -U "mongodb://127.0.0.1" -D "db_sensor" -C "report_0" \ - -s "rapl" -o -e "RAPL_ENERGY_PKG" \ - -s "msr" -e "TSC" -e "APERF" -e "MPERF" \ - -c "core" -e "CPU_CLK_THREAD_UNHALTED:REF_P" -e "CPU_CLK_THREAD_UNHALTED:THREAD_P" -e "LLC_MISSES" -e "INSTRUCTIONS_RETIRED" -``` - -=== "Docker with CSV output" - -```sh -docker run --rm \ - --net=host \ - --privileged \ - --pid=host \ - -v /sys:/sys \ - -v /var/lib/docker/containers:/var/lib/docker/containers:ro \ - -v /tmp/powerapi-sensor-reporting:/reporting \ - -v $(pwd):/srv \ - powerapi/hwpc-sensor \ - -n "$(hostname -f)" \ - -r "csv" -U "hwpc_reports" \ - -s "rapl" -o -e "RAPL_ENERGY_PKG" \ - -s "msr" -e "TSC" -e "APERF" -e "MPERF" \ - -c "core" -e "CPU_CLK_THREAD_UNHALTED:REF_P" -e "CPU_CLK_THREAD_UNHALTED:THREAD_P" -e "LLC_MISSES" -e "INSTRUCTIONS_RETIRED" -``` +???+ example "Examples using a CLI Parameters" + + === "CLI with MongoDB Output" + + ```sh + docker run --rm \ + --net=host \ + --privileged \ + --pid=host \ + -v /sys:/sys \ + -v /var/lib/docker/containers:/var/lib/docker/containers:ro \ + -v /tmp/powerapi-sensor-reporting:/reporting \ + -v $(pwd):/srv \ + powerapi/hwpc-sensor \ + -n "$(hostname -f)" \ + -r "mongodb" -U "mongodb://127.0.0.1" -D "db_sensor" -C "report_0" \ + -s "rapl" -o -e "RAPL_ENERGY_PKG" \ + -s "msr" -e "TSC" -e "APERF" -e "MPERF" \ + -c "core" -e "CPU_CLK_THREAD_UNHALTED:REF_P" -e "CPU_CLK_THREAD_UNHALTED:THREAD_P" -e "LLC_MISSES" -e "INSTRUCTIONS_RETIRED" + ``` + + === "Docker with CSV output" + + ```sh + docker run --rm \ + --net=host \ + --privileged \ + --pid=host \ + -v /sys:/sys \ + -v /var/lib/docker/containers:/var/lib/docker/containers:ro \ + -v /tmp/powerapi-sensor-reporting:/reporting \ + -v $(pwd):/srv \ + powerapi/hwpc-sensor \ + -n "$(hostname -f)" \ + -r "csv" -U "hwpc_reports" \ + -s "rapl" -o -e "RAPL_ENERGY_PKG" \ + -s "msr" -e "TSC" -e "APERF" -e "MPERF" \ + -c "core" -e "CPU_CLK_THREAD_UNHALTED:REF_P" -e "CPU_CLK_THREAD_UNHALTED:THREAD_P" -e "LLC_MISSES" -e "INSTRUCTIONS_RETIRED" + ``` ???+ info "Reports' Storage" From b667972662c266488cb9df9552179f4d6632dd4e Mon Sep 17 00:00:00 2001 From: chachignot Date: Wed, 9 Oct 2024 15:57:46 +0200 Subject: [PATCH 015/108] docs(getting-started): Add start of getting started script Add a docker compose file and the beginning of a python script to have an easy one-liner to use in the documentation --- docs/script/getting_started/.env | 22 +++++ .../getting_started/docker-compose.yaml | 93 +++++++++++++++++++ .../script/getting_started/filedb/report.json | 0 .../formula/smartwatts-mongodb-csv.json | 26 ++++++ .../getting_started/sensor/hwpc-mongodb.json | 37 ++++++++ docs/script/getting_started/start.py | 89 ++++++++++++++++++ 6 files changed, 267 insertions(+) create mode 100644 docs/script/getting_started/.env create mode 100644 docs/script/getting_started/docker-compose.yaml create mode 100644 docs/script/getting_started/filedb/report.json create mode 100644 docs/script/getting_started/formula/smartwatts-mongodb-csv.json create mode 100644 docs/script/getting_started/sensor/hwpc-mongodb.json create mode 100644 docs/script/getting_started/start.py diff --git a/docs/script/getting_started/.env b/docs/script/getting_started/.env new file mode 100644 index 0000000..34ede63 --- /dev/null +++ b/docs/script/getting_started/.env @@ -0,0 +1,22 @@ +# Sensor image +SENSOR_IMAGE=powerapi/hwpc-sensor:${HWPC_SENSOR_VERSION:-latest} + +# Formula image +FORMULA_IMAGE=powerapi/smartwatts-formula:${SMARTWATTS_VERSION:-latest} + +# Source selection +# Available options: mongodb, socket +POWERAPI_SOURCE=mongodb + +# Destination selection +# Available options: influxdb2, prometheus, mongodb, csv +POWERAPI_DESTINATION=csv + +# Third party images +MONGO_IMAGE=mongo:latest +MONGOEXPRESS_IMAGE=mongo-express:latest +INFLUXDB_IMAGE=influxdb:latest +CSV_IMAGE=busybox:stable-glibc + +# Docker compose profiles +COMPOSE_PROFILES=${POWERAPI_SOURCE},${POWERAPI_DESTINATION} diff --git a/docs/script/getting_started/docker-compose.yaml b/docs/script/getting_started/docker-compose.yaml new file mode 100644 index 0000000..bf08e15 --- /dev/null +++ b/docs/script/getting_started/docker-compose.yaml @@ -0,0 +1,93 @@ +services: + ############################################ + # SOURCES & DESTINATIONS # + ############################################ + # MongoDB + mongodb: + container_name: mongodb + image: ${MONGO_IMAGE} + networks: + - powerapi-network + profiles: + - mongodb + + # CSV (Default is a dummy image, can be used to add treatments on CSV files) + csv: + container_name: csv + image: ${CSV_IMAGE} + volumes: + - ${PWD}/csv:/tmp/csv + profiles: + - csv + + + ############################################ + # POWERAPI # + ############################################ + # PowerAPI Sensor + sensor: + container_name: sensor + image: ${SENSOR_IMAGE} + privileged: true + command: + - "--config-file" + - "/etc/sensor.json" + volumes: + - ${PWD}/sensor/hwpc-${POWERAPI_SOURCE}.json:/etc/sensor.json + - type: bind + source: /proc + target: /proc + - type: bind + source: /sys + target: /sys + - type: bind + source: /var/lib/docker/containers + target: /var/lib/docker/containers + depends_on: + - ${POWERAPI_SOURCE} + networks: + - powerapi-network + restart: unless-stopped + + # PowerAPI Formula + formula: + container_name: formula + image: ${FORMULA_IMAGE} + command: + - "--config-file" + - "/etc/formula.json" + volumes: + - ${PWD}/formula/smartwatts-${POWERAPI_SOURCE}-${POWERAPI_DESTINATION}.json:/etc/formula.json + - ${PWD}/csv:/tmp/csv + networks: + - powerapi-network + restart: unless-stopped + + + + ############################################ + # TOOLS # + ############################################ + + # Mongo Express + mongo-express: + container_name: mongo-express + image: ${MONGOEXPRESS_IMAGE} + environment: + ME_CONFIG_MONGODB_SERVER: mongodb + ME_CONFIG_MONGODB_URL: mongodb://mongodb:27017 + depends_on: + - mongodb + ports: + - "8081:8081" + networks: + - powerapi-network + profiles: + - mongodb + +############################################ +# DOCKER # +############################################ + +networks: + powerapi-network: diff --git a/docs/script/getting_started/filedb/report.json b/docs/script/getting_started/filedb/report.json new file mode 100644 index 0000000..e69de29 diff --git a/docs/script/getting_started/formula/smartwatts-mongodb-csv.json b/docs/script/getting_started/formula/smartwatts-mongodb-csv.json new file mode 100644 index 0000000..e1c7832 --- /dev/null +++ b/docs/script/getting_started/formula/smartwatts-mongodb-csv.json @@ -0,0 +1,26 @@ +{ + "verbose": true, + "stream": true, + "input": { + "puller_mongodb": { + "model": "HWPCReport", + "type": "mongodb", + "name": "puller_mongodb", + "uri": "mongodb://mongodb:27017", + "db": "db_sensor", + "collection": "prep" + } + }, + "output": { + "pusher_csv": { + "model": "PowerReport", + "type": "csv", + "name": "pusher_csv", + "directory": "/tmp/csv" + } + }, + "cpu-base-freq": 1900, + "cpu-error-threshold": 2.0, + "disable-dram-formula": true, + "sensor-reports-frequency": 1000 + } diff --git a/docs/script/getting_started/sensor/hwpc-mongodb.json b/docs/script/getting_started/sensor/hwpc-mongodb.json new file mode 100644 index 0000000..4ee5f0b --- /dev/null +++ b/docs/script/getting_started/sensor/hwpc-mongodb.json @@ -0,0 +1,37 @@ +{ + "name": "sensor", + "verbose": true, + "frequency": 1000, + "cgroup_basepath": "/sys/fs/cgroup/", + "output": { + "type": "mongodb", + "uri": "mongodb://mongodb:27017", + "database": "db_sensor", + "collection": "prep" + }, + "system": { + "rapl": { + "events": [ + "RAPL_ENERGY_PKG" + ], + "monitoring_type": "MONITOR_ONE_CPU_PER_SOCKET" + }, + "msr": { + "events": [ + "TSC", + "APERF", + "MPERF" + ] + } + }, + "container": { + "core": { + "events": [ + "CPU_CLK_THREAD_UNHALTED:REF_P", + "CPU_CLK_THREAD_UNHALTED:THREAD_P", + "LLC_MISSES", + "INSTRUCTIONS_RETIRED" + ] + } + } +} \ No newline at end of file diff --git a/docs/script/getting_started/start.py b/docs/script/getting_started/start.py new file mode 100644 index 0000000..61f5635 --- /dev/null +++ b/docs/script/getting_started/start.py @@ -0,0 +1,89 @@ +import subprocess +import json + +# List of available processor architectures +# Template: "n - Arch name" +# If an arch is added, the case statement in the start_demo function should be updated accordingly with the proper core events +# https://powerapi.org/reference/sensors/hwpc-sensor/#system-and-container-groups-parameters +list_arch = ["0 - Intel Sandy Bridge, Comet Lake", "1 - Intel Skylake, Whiskey Lake, Coffee Lake", "2 - AMD Zen 2", + "3 - AMD Zen 3"] + + +def start_demo(): + print("Enter the number associated with your processor architecture, please note that the sensor isn't available " + "for Intel Tiger Lake and newer: \n" + list_arch[0] + "\n" + list_arch[1] + "\n" + list_arch[2] + "\n" + + list_arch[3] + "\n") + choice = True + while choice: + try: + val = input() + val = int(val) + if val < 0 or val >= len(list_arch): + print("Invalid input, please enter a valid number or exit to quit") + else: + choice = False + except ValueError: + if val == "exit": + print("Exiting...") + exit(0) + else: + print("Invalid input, please enter a valid number or exit to quit") + + print("You have selected: " + list_arch[val] + "\n") + + # Update core events in the sensor configuration file based on the selected processor architecture + with open('sensor/hwpc-mongodb.json') as f: + data = json.load(f) + + match val: + case 0: + data['container']['core']['events'] = [ + "CPU_CLK_UNHALTED:REF_P", + "CPU_CLK_UNHALTED:THREAD_P", + "LLC_MISSES", + "INSTRUCTIONS_RETIRED" + ] + case 1: + data['container']['core']['events'] = [ + "CPU_CLK_THREAD_UNHALTED:REF_P", + "CPU_CLK_THREAD_UNHALTED:THREAD_P", + "LLC_MISSES", + "INSTRUCTIONS_RETIRED" + ] + case 2: + data['container']['core']['events'] = [ + "CYCLES_NOT_IN_HALT", + "RETIRED_INSTRUCTIONS", + "RETIRED_UOPS" + ] + case 3: + data['container']['core']['events'] = [ + "CYCLES_NOT_IN_HALT", + "RETIRED_INSTRUCTIONS", + "RETIRED_OPS" + ] + + print("Core events updated") + + # Check for the cgroup version and update the sensor configuration file accordingly + cgroup = subprocess.run(["stat", "-fc", "%T", "/sys/fs/cgroup/"], text=True, capture_output=True) + print(cgroup.stdout) + if cgroup.stdout == "cgroup2fs\n": + data["cgroup_basepath"] = "/sys/fs/cgroup/" + else: + data["cgroup_basepath"] = "/sys/fs/cgroup/perf_event" + + print("Cgroup version updated") + + with open('sensor/hwpc-mongodb.json', 'w') as f: + json.dump(data, f, indent=4) + + print("Starting the demo...") + +''' + up = subprocess.run(["docker", "compose", "up", "--abort-on-container-exit"], text=True, capture_output=True) + print(up.stdout) + print(up.stderr) +''' +if __name__ == '__main__': + start_demo() From dc7de38c27283572ce536e7bf558ae22c5b00f46 Mon Sep 17 00:00:00 2001 From: Inkedstinct Date: Thu, 10 Oct 2024 09:07:21 +0200 Subject: [PATCH 016/108] docs(mkdocs): Add local development tips --- README.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/README.md b/README.md index 9f4b9d8..c3e61b7 100644 --- a/README.md +++ b/README.md @@ -2,3 +2,23 @@ PowerAPI website use [MKDocs](https://www.mkdocs.org/). You can push your changes on `master` branch to deploy them on the website. + +## Local development + +When redacting changes for this documentation, you may want to visualize the +output the way it will be presented in the final website once compiled. + +One way to do so is to: + +1. Install the necessary packages : mkdocs, mkdocs-material, mkdocs-material-extensions +This can be done with: `pip install mkdocs mkdocs-material mkdocs-material-extensions` + +2. Have a local copy of this repository (taking care of *checkout-ing* the right ref) + +3. From the CLI, have this particular repository as PWD + +4. Use mkdocs in order to serve locally (default on http://localhost:8000) +This can be done with: `mkdocs serve -o` + +5. Check the URL it serves to, targets will be rebuild on modifications in the +current directory or in any nested elements (use -W to add paths to be considered for hot reloading) From e692d3257ed4c8d437eb9f3e64f6425f5a394256 Mon Sep 17 00:00:00 2001 From: Inkedstinct Date: Thu, 10 Oct 2024 09:51:46 +0200 Subject: [PATCH 017/108] docs(getting-started): Document a 'one-liner' way for quick test of PowerAPI Stack --- docs/getting_started.md | 286 +++++++++------------------------------- 1 file changed, 62 insertions(+), 224 deletions(-) diff --git a/docs/getting_started.md b/docs/getting_started.md index d4c4c47..c4dde39 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -1,7 +1,28 @@ # Getting started +!!! info "Pre-Requisites" + + **In order to follow this tutorial, you will need several elements ready on + the target server: + - A compatible processor + - A python installation ready + - Docker & Docker-Compose ready + - Root access** + +!!! warning "Testing purpose tutorial" + + This quick Getting-Started will guide you to get a quick view of PowerAPI + capabilities. + To do so in a light way, **the final output displayed is not the + intended use of the tools for an everyday deployment**, you'll only get quick & concise + statistics on the tested period. + ## Define elements to monitor +PowerAPI being a monitoring tool for energy consumption, we will need to define +the necessary elements to monitor. An example is also given if you do not already +have a process you wish to monitor. + ### Create a cGroup We need a subset of running processes to be monitored. For this, we use the @@ -28,20 +49,16 @@ cgclassify -g perf_event:new_cgroup_name PID with `PID`, the pid of the process you want to monitor. If you want to monitor a process composed of many processes, replace PID with `$(pidof process_name)`. -### Example process to monitor +### Installing a process to monitor [stress-ng](https://wiki.ubuntu.com/Kernel/Reference/stress-ng) can be used to generate load on one's system. An example usage, once installed : -```sh +```sh +cgcreate -g perf_event:stress-ng-cgroup stress-ng --cpu 1 --timeout 5m -``` - -The PID can be found using : - -```sh -STRESS_PID="$(pgrep stress-ng)" +cgclassify -g perf_event:stress-ng-cgroup $! ``` ## Which components to get a complete stack @@ -51,8 +68,6 @@ If you wish to get started as soon as possible, the following archive will allow 1. A MongoDB instance to store the [Sensor](./reference/sensors/hwpc-sensor.md) Reports -2. An InfluxDB2 instance to store the [Formulas](./reference/formulas/smartwatts.md) - 3. An [HWPC-Sensor](./reference/sensors/hwpc-sensor.md) that outputs its [HWPCReports](./reference/reports/report.md#HWPCReport) in a MongoDB Database, within the HWPCReport Collection. @@ -60,12 +75,8 @@ within the HWPCReport Collection. 4. A [SmartWatts](./reference/formulas/smartwatts.md) that streams the [HWPCReports](./reference/reports/report.md#HWPCReport) from the MongoDB Database Collection, processes it and outputs its -[PowerReports](./reference/reports/report.md#PowerReports) in an InfluxDB2 -Database within the PowerReport Collection - -5. A Grafana instance to visualize the -[PowerReports](./reference/reports/report.md#PowerReports) from the InfluxDB2 -instance +[PowerReports](./reference/reports/report.md#PowerReports) as CSV files for a +quick glimpse ## Preparation @@ -78,99 +89,27 @@ unzip powerapi-stack.zip && cd powerapi-stack From this archive, you will have all the necessary files to get started, let us break down each elements. -### Docker-Compose - -```yaml -# ./docker-compose.yaml - -version: '3.8' -services: - mongodb: - image: mongo:latest - container_name: mongodb - ports: - - "27017:27017" - volumes: - - mongo_data:/data/db - environment: - MONGO_INITDB_DATABASE: powerapi - command: ["mongod"] - networks: - - powerapi_network - - influxdb: - image: influxdb:2.0 - container_name: influxdb - ports: - - "8086:8086" - volumes: - - influxdb_data:/var/lib/influxdb2 - environment: - INFLUXDB_DB: powerapi - INFLUXDB_ADMIN_USER: admin - INFLUXDB_ADMIN_PASSWORD: admin123 - INFLUXDB_USER: powerapi_user - INFLUXDB_PASSWORD: powerapi_password - networks: - - powerapi_network - - hwpc_sensor: - image: powerapi/hwpc-sensor - container_name: hwpc_sensor - volumes: - - ./config/hwpc-sensor-config.json:/hwpc-sensor-config.json - command: --config-file /hwpc-sensor-config.json - networks: - - powerapi_network - depends_on: - - mongodb - - smartwatts: - image: powerapi/smartwatts - container_name: smartwatts - volumes: - - ./config/smartwatts-config.json:/smartwatts-config.json - command: --config /smartwatts-config.json - networks: - - powerapi_network - depends_on: - - mongodb - - influxdb - - grafana: - image: grafana/grafana:latest - container_name: grafana - ports: - - "3000:3000" - environment: - - GF_SECURITY_ADMIN_USER=admin - - GF_SECURITY_ADMIN_PASSWORD=admin - volumes: - - grafana_data:/var/lib/grafana - - ./config/provisioning:/etc/grafana/provisioning - networks: - - powerapi_network - depends_on: - - influxdb - -volumes: - mongo_data: - influxdb_data: - -networks: - powerapi_network: - driver: bridge +### Archive content + +```sh +|powerapi-stack/ +|--docker-compose.yaml +|--configs.d/ +|----hwpc-sensor-config.json +|----smartwatts-config.json +|--start.py +|--reports.d/ +|----powerreports.csv ``` -### HWPC-Sensor Configuration +#### HWPC-Sensor Configuration As described in the [HWPC-Sensor Documentation](./reference/sensors/hwpc-sensor.md#global-parameters) several parameters can be set, both globally and for specific Groups monitored. The provided docker-compose.yaml file use configuration files to set those parameters. An example configuration file for HWPC-Sensor is given below and available in the archive presented [above](./getting_started.md#preparation) : -```json -# ./hwpc-sensor-config.json +```json title="powerapi-stack/hwpc-sensor-config.json" { "verbose": false, @@ -201,111 +140,30 @@ several parameters can be set for the Formulas. The provided docker-compose.yaml file use configuration files to set those parameters. An example configuration file for SmartWatts is given below and available in the archive presented [above](./getting_started.md#preparation) : -```json -# ./smartwatts-config.json - +```json title="powerapi-stack/smartwatts-config.json" { - "cpu_tdp": 125, - "cpu_base_clock": 100, - "cpu_base_freq": 2100, # Use lscpu and look for CPU MHz field to know the correct value - "cpu_error_threshold": 2.0, - "dram_error_threshold": 2.0, - "learn_min_samples_required": 10, - "learn_history_window_size": 60, - "sensor_reports_frequency": 1000, - "input_mongo": { - "uri": "mongodb://mongodb:27017/powerapi", - "collection": "HWPCReports" + "verbose": true, + "stream": true, + "input": { + "puller": { + "model": "HWPCReport", + "type": "mongodb", + "uri": "mongodb://127.0.0.1", + "db": "powerapi", + "collection": "HWPCReports" + } }, - "output_influx": { - "uri": "http://influxdb:8086", - "org": "powerapi_org", - "bucket": "PowerReports", - "token": "INFLUXDB_API_TOKEN_GENERATED" - } -} -``` - -### Grafana Configuration - -Grafana has to be configured to use our InfluxDB2 instance as a datasource, -it should also know our dashboard file in order to visualize it. - -#### Filesystem structure - -The following filesystem structure is presented in the archive presented [above](./getting_started.md#preparation) : : - - -```sh -|powerapi-stack/ -|--grafana_config/ -|----provisioning/ -|------dashboards/ -|--------dashboard.yaml -|--------reports.json -|------datasources/ -|--------datasource.yaml -|... -``` - -#### Datasource file - -The following file gives information to Grafana to use InfluxDB2 as Datasource: - -```yaml -# ./grafana_config/provisioning/datasources/datasource.yaml - -apiVersion: 1 -datasources: - - name: InfluxDB - type: influxdb - access: proxy - url: http://influxdb:8086 - isDefault: true - database: PowerReports - user: powerapi_user - password: powerapi_password - jsonData: - version: Flux - organization: powerapi_org - defaultBucket: PowerReports - token: INFLUXDB_API_TOKEN_GENERATED - editable: true -``` - -#### Dashboard files - -The following files give information to Grafana: - -1. Where to search pre-configured Dashboards -2. How to configure our Dashboards - -```yaml -# ./grafana_config/provisioning/dashboards/dashboard.yaml - -apiVersion: 1 -providers: - - name: 'default' - folder: '' - type: file - options: - path: /etc/grafana/provisioning/dashboards -``` - -```json -# ./grafana_config/provisioning/dashboards/reports.json - -{ - "id": null, - "title": "PowerAPI Dashboard", - "tags": [], - "timezone": "browser", - "schemaVersion": 16, - "version": 0, - "panels": [ - { + "output": { + "pusher_power": { + "type": "csv", + "directory": "reports.d", + "files": "powerreports.csv" } - ] + }, + "cpu-base-freq": 1900, + "cpu-error-threshold": 2.0, + "disable-dram-formula": true, + "sensor-reports-frequency": 1000 } ``` @@ -314,26 +172,6 @@ providers: Once all set, you shall be able to initiate the stack with : ```sh -docker-compose -d up +python3 start.py ``` -This first spin-up will create the DBs and the Sensor. -Both SmartWatts Formulas and Grafana will struggle as they need an InfluxDB API key. -To resolve this, you can run : -```sh -influx auth create --org powerapi_org --read-buckets --write-buckets -``` - -You will obtain a token to replace the *INFLUXDB_API_TOKEN_GENERATED* placeholder in the `smartwatts-config.json` file and in `grafana_config/provisioning/datasources/datasource.yaml` - -Once both changed, another `docker-compose -d up` should do the trick to start the -remaining components. - -Thus, once deployed, you can access [Grafana](https://localhost:3000) by default -on http://localhost:8080 with its basic credentials : `admin/admin`. - -In the dashboards sections, basic dashboards will be provisionned with PowerReports -from SmartWatts providing real time insights about you consumption. - -Feel free to try yo make your own visualization and take a look at -[this documentation](./reference/grafana/grafana.md) for further details. From e7fadffeca403bf1b879fe5c32eed237717b5427 Mon Sep 17 00:00:00 2001 From: chachignot Date: Mon, 14 Oct 2024 13:56:46 +0200 Subject: [PATCH 018/108] docs(getting-started): Add docker compose gestion Add a docker compose gestion in the python file with a bash script --- docs/script/getting_started/start.py | 11 ++++++----- docs/script/getting_started/start.sh | 11 +++++++++++ 2 files changed, 17 insertions(+), 5 deletions(-) create mode 100755 docs/script/getting_started/start.sh diff --git a/docs/script/getting_started/start.py b/docs/script/getting_started/start.py index 61f5635..93de6b5 100644 --- a/docs/script/getting_started/start.py +++ b/docs/script/getting_started/start.py @@ -1,5 +1,8 @@ +from subprocess import call import subprocess import json +import sys +from time import sleep # List of available processor architectures # Template: "n - Arch name" @@ -80,10 +83,8 @@ def start_demo(): print("Starting the demo...") -''' - up = subprocess.run(["docker", "compose", "up", "--abort-on-container-exit"], text=True, capture_output=True) - print(up.stdout) - print(up.stderr) -''' + rc = call("./start.sh") + + if __name__ == '__main__': start_demo() diff --git a/docs/script/getting_started/start.sh b/docs/script/getting_started/start.sh new file mode 100755 index 0000000..bb1d1ce --- /dev/null +++ b/docs/script/getting_started/start.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +docker compose up -d + +docker compose logs sensor -f & + +docker compose logs formula -f & + +sleep 60 + +docker compose down \ No newline at end of file From ffd5f267a7a828ffc805122281a4c86b4fefe0d5 Mon Sep 17 00:00:00 2001 From: chachignot Date: Wed, 16 Oct 2024 11:58:27 +0200 Subject: [PATCH 019/108] docs(getting-started): Fix archive compatibility with version<3.10 pylint and flake8 OK + remove formula verbose --- .../formula/smartwatts-mongodb-csv.json | 2 +- docs/script/getting_started/start.py | 111 ++++++++++-------- 2 files changed, 66 insertions(+), 47 deletions(-) diff --git a/docs/script/getting_started/formula/smartwatts-mongodb-csv.json b/docs/script/getting_started/formula/smartwatts-mongodb-csv.json index e1c7832..cefbd37 100644 --- a/docs/script/getting_started/formula/smartwatts-mongodb-csv.json +++ b/docs/script/getting_started/formula/smartwatts-mongodb-csv.json @@ -1,5 +1,5 @@ { - "verbose": true, + "verbose": false, "stream": true, "input": { "puller_mongodb": { diff --git a/docs/script/getting_started/start.py b/docs/script/getting_started/start.py index 93de6b5..2ca72f3 100644 --- a/docs/script/getting_started/start.py +++ b/docs/script/getting_started/start.py @@ -1,75 +1,89 @@ + + +import sys from subprocess import call import subprocess import json -import sys -from time import sleep -# List of available processor architectures -# Template: "n - Arch name" -# If an arch is added, the case statement in the start_demo function should be updated accordingly with the proper core events -# https://powerapi.org/reference/sensors/hwpc-sensor/#system-and-container-groups-parameters -list_arch = ["0 - Intel Sandy Bridge, Comet Lake", "1 - Intel Skylake, Whiskey Lake, Coffee Lake", "2 - AMD Zen 2", +# List of available processor architectures Template: "n - Arch name" +# If an arch is added, the case statement in the +# start_demo function should be updated accordingly with the proper core events +# https://powerapi.org/reference/sensors/hwpc-sensor/ +list_arch = ["0 - Intel Sandy Bridge, Comet Lake", + "1 - Intel Skylake, Whiskey Lake, Coffee Lake", + "2 - AMD Zen 2", "3 - AMD Zen 3"] def start_demo(): - print("Enter the number associated with your processor architecture, please note that the sensor isn't available " - "for Intel Tiger Lake and newer: \n" + list_arch[0] + "\n" + list_arch[1] + "\n" + list_arch[2] + "\n" + - list_arch[3] + "\n") + """ + Start the demo by selecting the processor architecture + this will update the sensor configuration file + """ + print("Enter the number associated with your processor architecture, " + "please note that the sensor isn't available " + "for Intel Tiger Lake and newer: \n" + list_arch[0] + + "\n" + list_arch[1] + + "\n" + list_arch[2] + + "\n" + list_arch[3] + + "\n") + choice = True while choice: try: val = input() val = int(val) if val < 0 or val >= len(list_arch): - print("Invalid input, please enter a valid number or exit to quit") + print("Invalid input, please enter a valid number or exit") else: choice = False except ValueError: if val == "exit": print("Exiting...") - exit(0) + sys.exit() else: - print("Invalid input, please enter a valid number or exit to quit") + print("Invalid input, please enter a valid number or exit") print("You have selected: " + list_arch[val] + "\n") - # Update core events in the sensor configuration file based on the selected processor architecture - with open('sensor/hwpc-mongodb.json') as f: + # Update core events in the sensor configuration + # file based on the selected processor architecture + with open('sensor/hwpc-mongodb.json', encoding='UTF-8') as f: data = json.load(f) - match val: - case 0: - data['container']['core']['events'] = [ - "CPU_CLK_UNHALTED:REF_P", - "CPU_CLK_UNHALTED:THREAD_P", - "LLC_MISSES", - "INSTRUCTIONS_RETIRED" - ] - case 1: - data['container']['core']['events'] = [ - "CPU_CLK_THREAD_UNHALTED:REF_P", - "CPU_CLK_THREAD_UNHALTED:THREAD_P", - "LLC_MISSES", - "INSTRUCTIONS_RETIRED" - ] - case 2: - data['container']['core']['events'] = [ - "CYCLES_NOT_IN_HALT", - "RETIRED_INSTRUCTIONS", - "RETIRED_UOPS" - ] - case 3: - data['container']['core']['events'] = [ - "CYCLES_NOT_IN_HALT", - "RETIRED_INSTRUCTIONS", - "RETIRED_OPS" - ] + if val == 0: + data['container']['core']['events'] = [ + "CPU_CLK_UNHALTED:REF_P", + "CPU_CLK_UNHALTED:THREAD_P", + "LLC_MISSES", + "INSTRUCTIONS_RETIRED" + ] + elif val == 1: + data['container']['core']['events'] = [ + "CPU_CLK_THREAD_UNHALTED:REF_P", + "CPU_CLK_THREAD_UNHALTED:THREAD_P", + "LLC_MISSES", + "INSTRUCTIONS_RETIRED" + ] + elif val == 2: + data['container']['core']['events'] = [ + "CYCLES_NOT_IN_HALT", + "RETIRED_INSTRUCTIONS", + "RETIRED_UOPS" + ] + elif val == 3: + data['container']['core']['events'] = [ + "CYCLES_NOT_IN_HALT", + "RETIRED_INSTRUCTIONS", + "RETIRED_OPS" + ] print("Core events updated") - # Check for the cgroup version and update the sensor configuration file accordingly - cgroup = subprocess.run(["stat", "-fc", "%T", "/sys/fs/cgroup/"], text=True, capture_output=True) + # Check for the cgroup version and update + # the sensor configuration file accordingly + cgroup = subprocess.run(["stat", "-fc", "%T", "/sys/fs/cgroup/"], + text=True, capture_output=True, check=True) print(cgroup.stdout) if cgroup.stdout == "cgroup2fs\n": data["cgroup_basepath"] = "/sys/fs/cgroup/" @@ -78,12 +92,17 @@ def start_demo(): print("Cgroup version updated") - with open('sensor/hwpc-mongodb.json', 'w') as f: + with open('sensor/hwpc-mongodb.json', 'w', encoding='UTF-8') as f: json.dump(data, f, indent=4) print("Starting the demo...") - rc = call("./start.sh") + call("./start.sh") + + print("The demo has ended, " + "you can see the result under the /csv directory" + " or use 'python pretty_print.py' " + "to get a quick summary of the result in the terminal") if __name__ == '__main__': From 9819bbf8cb3ca27f80b84559b11cce4fe283eb6a Mon Sep 17 00:00:00 2001 From: chachignot Date: Wed, 16 Oct 2024 13:25:27 +0200 Subject: [PATCH 020/108] docs(getting-started): Early design for the pretty print idea --- docs/script/getting_started/pretty_print.py | 57 +++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 docs/script/getting_started/pretty_print.py diff --git a/docs/script/getting_started/pretty_print.py b/docs/script/getting_started/pretty_print.py new file mode 100644 index 0000000..6b1315c --- /dev/null +++ b/docs/script/getting_started/pretty_print.py @@ -0,0 +1,57 @@ +import csv +import os + + +def start_pretty_print(): + """ + Pretty print the result of the demo by parsing the csv files of each cgroup, + then proceed to calculate the average, maximum, and minimum consumption of each cgroup + and print them in a table format + """ + data = [] + result = [["Cgroup", "Average consumption", "Maximum consumption", "Minimum consumption"]] + + directory = './csv' + + print("The consumption are given in Watt, note that the precision depend on the value given in the configuration file (the base CPU frequence, the CPU TDP, ...) ") + + # Get all the csv power report in the csv directory + for dirpath, dirnames, filenames in os.walk(directory): + for filename in filenames: + if filename.endswith('.csv'): + file_path = os.path.join(dirpath, filename) + + with open(file_path, mode='r', newline='') as f: + reader = csv.DictReader(f) + for row in reader: + data.append(row) + + cgroup_data = {} + + # We remove rapl, but it's still available in the csv files + for row in data: + target = row['target'] + consumption = float(row['power']) + + if target not in cgroup_data and not target == 'rapl': + cgroup_data[target] = [] + + if not target == 'rapl': + cgroup_data[target].append(consumption) + + for target, consumptions in cgroup_data.items(): + avg_consumption = sum(consumptions) / len(consumptions) + max_consumption = max(consumptions) + min_consumption = min(consumptions) + + result.append([target, f"{avg_consumption:.2f}", f"{max_consumption:.2f}", f"{min_consumption:.2f}"]) + + print(f"{'Cgroup':<20} {'Average consumption':<20} {'Maximum consumption':<20} {'Minimum consumption':<20}") + print("=" * 80) + + for row in result[1:]: + print(f"{row[0]:<20} {row[1]:<20} {row[2]:<20} {row[3]:<20}") + + +if __name__ == '__main__': + start_pretty_print() From a0d8de3e029e6f91505356a6db47ca560f61ab31 Mon Sep 17 00:00:00 2001 From: chachignot Date: Wed, 16 Oct 2024 13:37:06 +0200 Subject: [PATCH 021/108] docs(getting-started): Pretty print improvement and start.py update --- docs/script/getting_started/pretty_print.py | 11 +++++++++-- docs/script/getting_started/start.py | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/docs/script/getting_started/pretty_print.py b/docs/script/getting_started/pretty_print.py index 6b1315c..88decca 100644 --- a/docs/script/getting_started/pretty_print.py +++ b/docs/script/getting_started/pretty_print.py @@ -13,7 +13,7 @@ def start_pretty_print(): directory = './csv' - print("The consumption are given in Watt, note that the precision depend on the value given in the configuration file (the base CPU frequence, the CPU TDP, ...) ") + print("\nThe consumptions are given in Watt, note that the precision depend on the value given in the configuration file (the base CPU frequence, the CPU TDP, ...) \n") # Get all the csv power report in the csv directory for dirpath, dirnames, filenames in os.walk(directory): @@ -44,7 +44,10 @@ def start_pretty_print(): max_consumption = max(consumptions) min_consumption = min(consumptions) - result.append([target, f"{avg_consumption:.2f}", f"{max_consumption:.2f}", f"{min_consumption:.2f}"]) + if not target == 'global': + result.append([target, f"{avg_consumption:.2f}", f"{max_consumption:.2f}", f"{min_consumption:.2f}"]) + else: + total = [avg_consumption, max_consumption, min_consumption] print(f"{'Cgroup':<20} {'Average consumption':<20} {'Maximum consumption':<20} {'Minimum consumption':<20}") print("=" * 80) @@ -52,6 +55,10 @@ def start_pretty_print(): for row in result[1:]: print(f"{row[0]:<20} {row[1]:<20} {row[2]:<20} {row[3]:<20}") + print("=" * 80) + print(f"{'Global':<20} {total[0]:<20.2f} {total[1]:<20.2f} {total[2]:<20.2f}") + + print("\nIf you want to get a more precise evaluation, we encourage you to read the documentation of PowerAPI and adapte the sensor and formula configuration file in consequence \n") if __name__ == '__main__': start_pretty_print() diff --git a/docs/script/getting_started/start.py b/docs/script/getting_started/start.py index 2ca72f3..4941794 100644 --- a/docs/script/getting_started/start.py +++ b/docs/script/getting_started/start.py @@ -101,7 +101,7 @@ def start_demo(): print("The demo has ended, " "you can see the result under the /csv directory" - " or use 'python pretty_print.py' " + " or use 'python3 pretty_print.py' " "to get a quick summary of the result in the terminal") From 8455db537d6df166cf40223d57d0e297aa06eb5a Mon Sep 17 00:00:00 2001 From: chachignot Date: Thu, 17 Oct 2024 13:37:16 +0200 Subject: [PATCH 022/108] docs(getting-started): Getting started archive is ready for use The first version of the getting started archive is ready to be used and tested. --- docs/script/getting_started/pretty_print.py | 77 ++++++++++++++++----- docs/script/getting_started/start.py | 29 +++++++- 2 files changed, 88 insertions(+), 18 deletions(-) diff --git a/docs/script/getting_started/pretty_print.py b/docs/script/getting_started/pretty_print.py index 88decca..1c64733 100644 --- a/docs/script/getting_started/pretty_print.py +++ b/docs/script/getting_started/pretty_print.py @@ -1,33 +1,64 @@ +# Copyright (c) 2024, INRIA +# Copyright (c) 2024, University of Lille +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + import csv import os def start_pretty_print(): """ - Pretty print the result of the demo by parsing the csv files of each cgroup, - then proceed to calculate the average, maximum, and minimum consumption of each cgroup + Pretty print the result of the demo by parsing the csv files, + then proceed to calculate the average, maximum, and minimum consumption and print them in a table format """ data = [] - result = [["Cgroup", "Average consumption", "Maximum consumption", "Minimum consumption"]] - - directory = './csv' + result = [["Cgroup", + "Average consumption", + "Maximum consumption", + "Minimum consumption"]] - print("\nThe consumptions are given in Watt, note that the precision depend on the value given in the configuration file (the base CPU frequence, the CPU TDP, ...) \n") + print("\nThe consumptions are given in Watt, " + "note that the precision depend on the value given in the " + "configuration file (the base CPU frequency, the CPU TDP, ...) \n") # Get all the csv power report in the csv directory - for dirpath, dirnames, filenames in os.walk(directory): - for filename in filenames: + for root, _, files in os.walk('./csv'): + for filename in files: if filename.endswith('.csv'): - file_path = os.path.join(dirpath, filename) + file_path = os.path.join(root, filename) - with open(file_path, mode='r', newline='') as f: - reader = csv.DictReader(f) - for row in reader: + with open(file_path, mode='r', newline='', encoding='UTF-8') as f: + for row in csv.DictReader(f): data.append(row) cgroup_data = {} - + total = [0, 0, 0] # We remove rapl, but it's still available in the csv files for row in data: target = row['target'] @@ -45,20 +76,32 @@ def start_pretty_print(): min_consumption = min(consumptions) if not target == 'global': - result.append([target, f"{avg_consumption:.2f}", f"{max_consumption:.2f}", f"{min_consumption:.2f}"]) + result.append([target, + f"{avg_consumption:.2f}", + f"{max_consumption:.2f}", + f"{min_consumption:.2f}"]) else: total = [avg_consumption, max_consumption, min_consumption] - print(f"{'Cgroup':<20} {'Average consumption':<20} {'Maximum consumption':<20} {'Minimum consumption':<20}") + print(f"{'Target':<20} " + f"{'Average consumption':<20} " + f"{'Maximum consumption':<20} " + f"{'Minimum consumption':<20}") print("=" * 80) for row in result[1:]: print(f"{row[0]:<20} {row[1]:<20} {row[2]:<20} {row[3]:<20}") print("=" * 80) - print(f"{'Global':<20} {total[0]:<20.2f} {total[1]:<20.2f} {total[2]:<20.2f}") + print(f"{'Global':<20} " + f"{total[0]:<20.2f} " + f"{total[1]:<20.2f} " + f"{total[2]:<20.2f}") + + print("\nIf you want to get a more precise evaluation, " + "we encourage you to read the documentation of PowerAPI " + "and adapt the sensor/formula configuration file in consequence \n") - print("\nIf you want to get a more precise evaluation, we encourage you to read the documentation of PowerAPI and adapte the sensor and formula configuration file in consequence \n") if __name__ == '__main__': start_pretty_print() diff --git a/docs/script/getting_started/start.py b/docs/script/getting_started/start.py index 4941794..fbef2a0 100644 --- a/docs/script/getting_started/start.py +++ b/docs/script/getting_started/start.py @@ -1,4 +1,31 @@ - +# Copyright (c) 2024, INRIA +# Copyright (c) 2024, University of Lille +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import sys from subprocess import call From d243f8b89546aa2ac3b5f393d0f752ba84e10fa5 Mon Sep 17 00:00:00 2001 From: chachignot Date: Thu, 17 Oct 2024 16:55:40 +0200 Subject: [PATCH 023/108] docs(getting-started): Refactor Preety_print + change to start --- docs/script/getting_started/pretty_print.py | 111 ++++++++++---------- docs/script/getting_started/start.py | 28 ++++- docs/script/getting_started/start.sh | 2 +- 3 files changed, 82 insertions(+), 59 deletions(-) diff --git a/docs/script/getting_started/pretty_print.py b/docs/script/getting_started/pretty_print.py index 1c64733..768f88d 100644 --- a/docs/script/getting_started/pretty_print.py +++ b/docs/script/getting_started/pretty_print.py @@ -31,76 +31,81 @@ import os -def start_pretty_print(): +def load_data(directory='./csv'): """ - Pretty print the result of the demo by parsing the csv files, - then proceed to calculate the average, maximum, and minimum consumption - and print them in a table format + Load CSV files from the specified directory and return the data as a list of dictionaries. """ data = [] - result = [["Cgroup", - "Average consumption", - "Maximum consumption", - "Minimum consumption"]] - - print("\nThe consumptions are given in Watt, " - "note that the precision depend on the value given in the " - "configuration file (the base CPU frequency, the CPU TDP, ...) \n") - - # Get all the csv power report in the csv directory - for root, _, files in os.walk('./csv'): + for root, _, files in os.walk(directory): for filename in files: if filename.endswith('.csv'): file_path = os.path.join(root, filename) - with open(file_path, mode='r', newline='', encoding='UTF-8') as f: - for row in csv.DictReader(f): - data.append(row) + data.extend(csv.DictReader(f)) + return data + - cgroup_data = {} - total = [0, 0, 0] - # We remove rapl, but it's still available in the csv files +def calculate_statistics(data, scope): + """ + Calculate average, maximum, and minimum consumption for the given scope (cpu or dram). + """ + stats = {} for row in data: - target = row['target'] - consumption = float(row['power']) + if row['scope'] == scope and row['target'] != 'rapl': + target = row['target'] + consumption = float(row['power']) + stats.setdefault(target, []).append(consumption) - if target not in cgroup_data and not target == 'rapl': - cgroup_data[target] = [] + return { + target: { + 'avg': sum(consumptions) / len(consumptions), + 'max': max(consumptions), + 'min': min(consumptions) + } for target, consumptions in stats.items() + } - if not target == 'rapl': - cgroup_data[target].append(consumption) - for target, consumptions in cgroup_data.items(): - avg_consumption = sum(consumptions) / len(consumptions) - max_consumption = max(consumptions) - min_consumption = min(consumptions) +def print_statistics(stats, title): + """ + Print statistics (average, max, min) for the given data in a formatted table. + """ + if not stats: + return + + print(f"\n{title}\n") + print(f"{'Target':<20} {'Average consumption':<20} {'Maximum consumption':<20} {'Minimum consumption':<20}") + print("=" * 80) - if not target == 'global': - result.append([target, - f"{avg_consumption:.2f}", - f"{max_consumption:.2f}", - f"{min_consumption:.2f}"]) + total = {'avg': 0, 'max': 0, 'min': 0} + for target, values in stats.items(): + if target != 'global': + print(f"{target:<20} {values['avg']:<20.2f} {values['max']:<20.2f} {values['min']:<20.2f}") else: - total = [avg_consumption, max_consumption, min_consumption] + total = values - print(f"{'Target':<20} " - f"{'Average consumption':<20} " - f"{'Maximum consumption':<20} " - f"{'Minimum consumption':<20}") - print("=" * 80) + print("-" * 80) + print(f"{'Global':<20} {total['avg']:<20.2f} {total['max']:<20.2f} {total['min']:<20.2f}") - for row in result[1:]: - print(f"{row[0]:<20} {row[1]:<20} {row[2]:<20} {row[3]:<20}") - print("=" * 80) - print(f"{'Global':<20} " - f"{total[0]:<20.2f} " - f"{total[1]:<20.2f} " - f"{total[2]:<20.2f}") - - print("\nIf you want to get a more precise evaluation, " - "we encourage you to read the documentation of PowerAPI " - "and adapt the sensor/formula configuration file in consequence \n") +def start_pretty_print(): + """ + Pretty print the CPU and DRAM power consumption statistics from CSV files. + """ + print("The consumptions are given in Watt, note that the precision depends on the configuration file\n") + + data = load_data() + + # Calculate and print CPU statistics + cpu_stats = calculate_statistics(data, 'cpu') + print_statistics(cpu_stats, "CPU Consumption Statistics :") + + # Calculate and print DRAM statistics + dram_stats = calculate_statistics(data, 'dram') + print_statistics(dram_stats, "DRAM Consumption Statistics :") + + # Could add the GPU statistics here + + print("\nFor more precise evaluation, consult the PowerAPI documentation to adjust configurations.\n") if __name__ == '__main__': diff --git a/docs/script/getting_started/start.py b/docs/script/getting_started/start.py index fbef2a0..4085b80 100644 --- a/docs/script/getting_started/start.py +++ b/docs/script/getting_started/start.py @@ -26,7 +26,7 @@ # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - +import os import sys from subprocess import call import subprocess @@ -55,6 +55,7 @@ def start_demo(): "\n" + list_arch[3] + "\n") + val = "" choice = True while choice: try: @@ -123,13 +124,30 @@ def start_demo(): json.dump(data, f, indent=4) print("Starting the demo...") + print("The demo will run for approximately 2 minutes\n") call("./start.sh") - print("The demo has ended, " - "you can see the result under the /csv directory" - " or use 'python3 pretty_print.py' " - "to get a quick summary of the result in the terminal") + verification = 0 + + # Get all the csv power report in the csv directory + for root, _, files in os.walk('./csv'): + for filename in files: + if filename.endswith('.csv'): + verification += 1 + file_path = os.path.join(root, filename) + print("The power report is available at: " + file_path) + + if verification == 0: + print("\nNo power report available, " + "please check the configuration file " + "and the sensor availability for your " + "processor architecture\n") + else: + print("\nThe demo has ended, " + "you can see the result under the /csv directory" + " or use 'python3 pretty_print.py' " + "to get a quick summary of the result in the terminal\n") if __name__ == '__main__': diff --git a/docs/script/getting_started/start.sh b/docs/script/getting_started/start.sh index bb1d1ce..bc23b58 100755 --- a/docs/script/getting_started/start.sh +++ b/docs/script/getting_started/start.sh @@ -6,6 +6,6 @@ docker compose logs sensor -f & docker compose logs formula -f & -sleep 60 +sleep 120 docker compose down \ No newline at end of file From 95717811304ee73dc2ba11f39837021ced31af0b Mon Sep 17 00:00:00 2001 From: chachignot Date: Mon, 21 Oct 2024 13:32:00 +0200 Subject: [PATCH 024/108] docs(getting-started): Adapt getting started doc structure Adapt the getting started documentation to better match the archive --- docs/getting_started.md | 129 ++++++++++-------- .../script/getting_started/filedb/report.json | 0 2 files changed, 75 insertions(+), 54 deletions(-) delete mode 100644 docs/script/getting_started/filedb/report.json diff --git a/docs/getting_started.md b/docs/getting_started.md index c4dde39..d33e7fb 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -20,14 +20,12 @@ ## Define elements to monitor PowerAPI being a monitoring tool for energy consumption, we will need to define -the necessary elements to monitor. An example is also given if you do not already -have a process you wish to monitor. +the necessary elements to monitor. +In the testing archive, we will be able to see the consumption of the docker container by the default. +But if we want to monitor a specific process, we can use the Linux abstraction of [cGroups](https://www.redhat.com/sysadmin/cgroups-part-one). ### Create a cGroup -We need a subset of running processes to be monitored. For this, we use the -Linux abstraction of [cGroups](https://www.redhat.com/sysadmin/cgroups-part-one). - In order to create a cGroup, the following command can be used from CLI : ```sh @@ -87,19 +85,22 @@ wget "https://github.com/powerapi-ng/powerapi-ng.github.io/tree/master/examples/ unzip powerapi-stack.zip && cd powerapi-stack ``` -From this archive, you will have all the necessary files to get started, let us break down each elements. +From this archive, you will have all the necessary files to get started, let us break down each element. ### Archive content ```sh -|powerapi-stack/ -|--docker-compose.yaml -|--configs.d/ -|----hwpc-sensor-config.json -|----smartwatts-config.json +|getting_started/ +|--csv/ +|--fomula/ +|----smartwatts-mongodb-csv.json +|--sensor/ +|----hwpc-mongodb.json +|--start.sh |--start.py -|--reports.d/ -|----powerreports.csv +|--pretty_print.py +|--docker-compose.yaml +|--.env ``` #### HWPC-Sensor Configuration @@ -112,24 +113,41 @@ An example configuration file for HWPC-Sensor is given below and available in th ```json title="powerapi-stack/hwpc-sensor-config.json" { - "verbose": false, - "frequency": 1000, - "name": "hwpc-sensor", - "cgroup_basepath": "/sys/fs/cgroup/perf_event", - "system": { - "type": "MONITOR_ALL_CPU_PER_SOCKET", - "events": ["RAPL_ENERGY_PKG", "RAPL_ENERGY_DRAM"] - }, - "container": { - "type": "MONITOR_ALL_CPU_PER_SOCKET", - "events": ["RAPL_ENERGY_PKG", "RAPL_ENERGY_DRAM"] - }, - "output": { - "type": "mongodb", - "uri": "mongodb://mongodb:27017", - "db": "powerapi", - "collection": "HWPCReports" - } + "name": "sensor", + "verbose": true, + "frequency": 1000, + "cgroup_basepath": "/sys/fs/cgroup/", + "output": { + "type": "mongodb", + "uri": "mongodb://mongodb:27017", + "database": "db_sensor", + "collection": "prep" + }, + "system": { + "rapl": { + "events": [ + "RAPL_ENERGY_PKG" + ], + "monitoring_type": "MONITOR_ONE_CPU_PER_SOCKET" + }, + "msr": { + "events": [ + "TSC", + "APERF", + "MPERF" + ] + } + }, + "container": { + "core": { + "events": [ + "CPU_CLK_THREAD_UNHALTED:REF_P", + "CPU_CLK_THREAD_UNHALTED:THREAD_P", + "LLC_MISSES", + "INSTRUCTIONS_RETIRED" + ] + } + } } ``` @@ -142,29 +160,32 @@ An example configuration file for SmartWatts is given below and available in the ```json title="powerapi-stack/smartwatts-config.json" { - "verbose": true, - "stream": true, - "input": { - "puller": { - "model": "HWPCReport", - "type": "mongodb", - "uri": "mongodb://127.0.0.1", - "db": "powerapi", - "collection": "HWPCReports" - } - }, - "output": { - "pusher_power": { - "type": "csv", - "directory": "reports.d", - "files": "powerreports.csv" - } - }, - "cpu-base-freq": 1900, - "cpu-error-threshold": 2.0, - "disable-dram-formula": true, - "sensor-reports-frequency": 1000 -} + "verbose": false, + "stream": true, + "input": { + "puller_mongodb": { + "model": "HWPCReport", + "type": "mongodb", + "name": "puller_mongodb", + "uri": "mongodb://mongodb:27017", + "db": "db_sensor", + "collection": "prep" + } + }, + "output": { + "pusher_csv": { + "model": "PowerReport", + "type": "csv", + "name": "pusher_csv", + "directory": "/tmp/csv" + } + }, + "cpu-base-freq": 1900, + "cpu-error-threshold": 2.0, + "disable-dram-formula": true, + "sensor-reports-frequency": 1000 + } + ``` ## Turn the key diff --git a/docs/script/getting_started/filedb/report.json b/docs/script/getting_started/filedb/report.json deleted file mode 100644 index e69de29..0000000 From fec3645a74cfc202569770d9ce06d63f637fb66c Mon Sep 17 00:00:00 2001 From: chachignot Date: Mon, 21 Oct 2024 14:16:15 +0200 Subject: [PATCH 025/108] docs(getting-started): Change to the preparation part --- docs/getting_started.md | 98 +++++++---------------------------------- 1 file changed, 15 insertions(+), 83 deletions(-) diff --git a/docs/getting_started.md b/docs/getting_started.md index d33e7fb..08b9e38 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -68,7 +68,7 @@ Reports 3. An [HWPC-Sensor](./reference/sensors/hwpc-sensor.md) that outputs its [HWPCReports](./reference/reports/report.md#HWPCReport) in a MongoDB Database, -within the HWPCReport Collection. +within the HWPCReport Collection 4. A [SmartWatts](./reference/formulas/smartwatts.md) that streams the [HWPCReports](./reference/reports/report.md#HWPCReport) from the MongoDB @@ -103,90 +103,14 @@ From this archive, you will have all the necessary files to get started, let us |--.env ``` -#### HWPC-Sensor Configuration - -As described in the [HWPC-Sensor Documentation](./reference/sensors/hwpc-sensor.md#global-parameters) -several parameters can be set, both globally and for specific Groups monitored. -The provided docker-compose.yaml file use configuration files to set those parameters. -An example configuration file for HWPC-Sensor is given below and available in the archive presented [above](./getting_started.md#preparation) : - -```json title="powerapi-stack/hwpc-sensor-config.json" - -{ - "name": "sensor", - "verbose": true, - "frequency": 1000, - "cgroup_basepath": "/sys/fs/cgroup/", - "output": { - "type": "mongodb", - "uri": "mongodb://mongodb:27017", - "database": "db_sensor", - "collection": "prep" - }, - "system": { - "rapl": { - "events": [ - "RAPL_ENERGY_PKG" - ], - "monitoring_type": "MONITOR_ONE_CPU_PER_SOCKET" - }, - "msr": { - "events": [ - "TSC", - "APERF", - "MPERF" - ] - } - }, - "container": { - "core": { - "events": [ - "CPU_CLK_THREAD_UNHALTED:REF_P", - "CPU_CLK_THREAD_UNHALTED:THREAD_P", - "LLC_MISSES", - "INSTRUCTIONS_RETIRED" - ] - } - } -} -``` +#### HWPC-Sensor and SmartWatts Configuration -### SmartWatts Configuration - -As described in the [SmartWatts Documentation](./reference/formulas/smartwatts.md#global-parameters) -several parameters can be set for the Formulas. -The provided docker-compose.yaml file use configuration files to set those parameters. -An example configuration file for SmartWatts is given below and available in the archive presented [above](./getting_started.md#preparation) : - -```json title="powerapi-stack/smartwatts-config.json" -{ - "verbose": false, - "stream": true, - "input": { - "puller_mongodb": { - "model": "HWPCReport", - "type": "mongodb", - "name": "puller_mongodb", - "uri": "mongodb://mongodb:27017", - "db": "db_sensor", - "collection": "prep" - } - }, - "output": { - "pusher_csv": { - "model": "PowerReport", - "type": "csv", - "name": "pusher_csv", - "directory": "/tmp/csv" - } - }, - "cpu-base-freq": 1900, - "cpu-error-threshold": 2.0, - "disable-dram-formula": true, - "sensor-reports-frequency": 1000 - } +As described in the [HWPC-Sensor Documentation](./reference/sensors/hwpc-sensor.md#global-parameters) and in the [SmartWatts Documentation](./reference/formulas/smartwatts.md#global-parameters) +several parameters can be set, both globally and for specific Groups monitored for the sensor or the formula. + +The provided docker-compose.yaml file use configuration files and the **.env** to set those parameters. +You can find example of both those configuration files in the archive under the **formula** and **sensor** directories. -``` ## Turn the key @@ -196,3 +120,11 @@ Once all set, you shall be able to initiate the stack with : python3 start.py ``` +After the 2 minutes of monitoring, you will be able to see the result inside the **csv** directory. +If you have trouble understanding the output, you can read the [Power Report documentation](./reference/reports/reports.md#power-Reports). + +Only in the context of this testing archive, after the monitoring, you can use the following command to get a pretty print of the result directly inside the terminal. + +```sh +python3 pretty_print.py +``` From 8a1f7f01f74c07e1f7c9345c24f32f4b2898cacf Mon Sep 17 00:00:00 2001 From: chachignot Date: Mon, 21 Oct 2024 15:00:18 +0200 Subject: [PATCH 026/108] docs(getting-started): Beginning update --- docs/getting_started.md | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/docs/getting_started.md b/docs/getting_started.md index 08b9e38..096910b 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -1,28 +1,27 @@ # Getting started -!!! info "Pre-Requisites" - - **In order to follow this tutorial, you will need several elements ready on - the target server: - - A compatible processor - - A python installation ready - - Docker & Docker-Compose ready - - Root access** - -!!! warning "Testing purpose tutorial" - - This quick Getting-Started will guide you to get a quick view of PowerAPI - capabilities. - To do so in a light way, **the final output displayed is not the - intended use of the tools for an everyday deployment**, you'll only get quick & concise - statistics on the tested period. +In this tutorial, we will guide you through the first steps to get started with PowerAPI. +The objective is to get a quick view of the capabilities of PowerAPI, by monitoring a process and getting a quick glimpse at the energy consumption. +A few things are required before we start : + +- A compatible processor, you can see the compatible CPU architecture [here](./reference/sensors/hwpc-sensor.md#) and you can look on the following pages to find your CPU architecture : + - For [Intel Processor](https://en.wikipedia.org/wiki/List_of_Intel_processors) + - For [Intel Xeon Processor](https://en.wikipedia.org/wiki/List_of_Intel_Xeon_processors) + - For [AMD Processor](https://en.wikipedia.org/wiki/Table_of_AMD_processors) + +- A python installation ready +- Docker & Docker-Compose ready +- Root access + +The first step of the tutorial will be to define the elements to monitor. +In the testing archive, we will be able to see the consumption of the docker container by the default. +So feel free to skip directly to the [preparation part](#preparation) if you don't want to monitor a specific process. ## Define elements to monitor PowerAPI being a monitoring tool for energy consumption, we will need to define -the necessary elements to monitor. -In the testing archive, we will be able to see the consumption of the docker container by the default. -But if we want to monitor a specific process, we can use the Linux abstraction of [cGroups](https://www.redhat.com/sysadmin/cgroups-part-one). +the necessary elements to monitor. +To do so we can use the Linux abstraction of [cGroups](https://www.redhat.com/sysadmin/cgroups-part-one). ### Create a cGroup From 1e8fa2febe4d09a65b1ebdb91b6108dc8bbaa490 Mon Sep 17 00:00:00 2001 From: Inkedstinct Date: Mon, 21 Oct 2024 15:17:33 +0200 Subject: [PATCH 027/108] fix(overview): Fix markdown list --- docs/reference/overview.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/reference/overview.md b/docs/reference/overview.md index b8607cc..7bc63d7 100644 --- a/docs/reference/overview.md +++ b/docs/reference/overview.md @@ -14,8 +14,9 @@ The diagram below illustrates the overall architecture of a PowerMeter within th ![PowerAPI Architecture Overview](../assets/images/reference/overview/global-architecture.jpg){ width="1000px"} A PowerMeter consists of two essential components: + - A [Sensor](#Sensor), which collects system usage metrics and generates usage reports. -- A [Formula](#Formula), which applies a computational model to the usage reports, producing power consumption data (in mJ). +- A [Formula](#Formula), which applies a computational model to the usage reports, producing power consumption data (in mJ). Additionally, [Preprocessors](./overview.md#Preprocessors) can be utilized to modify usage reports before they are processed by the Formula. From 671beb4c6692f5a25334510c4d88317064a059d2 Mon Sep 17 00:00:00 2001 From: Inkedstinct Date: Mon, 21 Oct 2024 15:18:20 +0200 Subject: [PATCH 028/108] fix(overview): Add highlights for 'quick overview' with pretty_print.py --- docs/getting_started.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/getting_started.md b/docs/getting_started.md index 096910b..5e388af 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -4,11 +4,10 @@ In this tutorial, we will guide you through the first steps to get started with The objective is to get a quick view of the capabilities of PowerAPI, by monitoring a process and getting a quick glimpse at the energy consumption. A few things are required before we start : -- A compatible processor, you can see the compatible CPU architecture [here](./reference/sensors/hwpc-sensor.md#) and you can look on the following pages to find your CPU architecture : - - For [Intel Processor](https://en.wikipedia.org/wiki/List_of_Intel_processors) - - For [Intel Xeon Processor](https://en.wikipedia.org/wiki/List_of_Intel_Xeon_processors) - - For [AMD Processor](https://en.wikipedia.org/wiki/Table_of_AMD_processors) - +- A compatible processor, you can see the compatible CPU architecture [here](./reference/sensors/hwpc-sensor.md#) and you can look on the following pages to find your CPU architecture : + * For [Intel Processor](https://en.wikipedia.org/wiki/List_of_Intel_processors) + * For [Intel Xeon Processor](https://en.wikipedia.org/wiki/List_of_Intel_Xeon_processors) + * For [AMD Processor](https://en.wikipedia.org/wiki/Table_of_AMD_processors) - A python installation ready - Docker & Docker-Compose ready - Root access @@ -122,8 +121,9 @@ python3 start.py After the 2 minutes of monitoring, you will be able to see the result inside the **csv** directory. If you have trouble understanding the output, you can read the [Power Report documentation](./reference/reports/reports.md#power-Reports). -Only in the context of this testing archive, after the monitoring, you can use the following command to get a pretty print of the result directly inside the terminal. +!!! info "Quick results overview" + Only in the context of this testing archive, after the monitoring, you can use the following command to get a pretty print of the result directly inside the terminal. -```sh -python3 pretty_print.py -``` + ```sh + python3 pretty_print.py + ``` From b6fe0764e368c2a6c9109d629c7601a8255a4db8 Mon Sep 17 00:00:00 2001 From: Inkedstinct Date: Mon, 21 Oct 2024 15:18:20 +0200 Subject: [PATCH 029/108] fix(getting-started): Add highlights for 'quick overview' with pretty_print.py --- docs/getting_started.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/getting_started.md b/docs/getting_started.md index 096910b..5e388af 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -4,11 +4,10 @@ In this tutorial, we will guide you through the first steps to get started with The objective is to get a quick view of the capabilities of PowerAPI, by monitoring a process and getting a quick glimpse at the energy consumption. A few things are required before we start : -- A compatible processor, you can see the compatible CPU architecture [here](./reference/sensors/hwpc-sensor.md#) and you can look on the following pages to find your CPU architecture : - - For [Intel Processor](https://en.wikipedia.org/wiki/List_of_Intel_processors) - - For [Intel Xeon Processor](https://en.wikipedia.org/wiki/List_of_Intel_Xeon_processors) - - For [AMD Processor](https://en.wikipedia.org/wiki/Table_of_AMD_processors) - +- A compatible processor, you can see the compatible CPU architecture [here](./reference/sensors/hwpc-sensor.md#) and you can look on the following pages to find your CPU architecture : + * For [Intel Processor](https://en.wikipedia.org/wiki/List_of_Intel_processors) + * For [Intel Xeon Processor](https://en.wikipedia.org/wiki/List_of_Intel_Xeon_processors) + * For [AMD Processor](https://en.wikipedia.org/wiki/Table_of_AMD_processors) - A python installation ready - Docker & Docker-Compose ready - Root access @@ -122,8 +121,9 @@ python3 start.py After the 2 minutes of monitoring, you will be able to see the result inside the **csv** directory. If you have trouble understanding the output, you can read the [Power Report documentation](./reference/reports/reports.md#power-Reports). -Only in the context of this testing archive, after the monitoring, you can use the following command to get a pretty print of the result directly inside the terminal. +!!! info "Quick results overview" + Only in the context of this testing archive, after the monitoring, you can use the following command to get a pretty print of the result directly inside the terminal. -```sh -python3 pretty_print.py -``` + ```sh + python3 pretty_print.py + ``` From 72b37349684ce6c5b274f7bf969f0ead238c07aa Mon Sep 17 00:00:00 2001 From: Inkedstinct Date: Mon, 21 Oct 2024 15:44:48 +0200 Subject: [PATCH 030/108] feat(getting-started/script): Add signal handler for SIGINT in order to stop docker compose stack --- docs/script/getting_started/start.py | 8 +++++++- docs/script/getting_started/start.sh | 2 +- docs/script/getting_started/stop.sh | 5 +++++ 3 files changed, 13 insertions(+), 2 deletions(-) create mode 100755 docs/script/getting_started/stop.sh diff --git a/docs/script/getting_started/start.py b/docs/script/getting_started/start.py index 4085b80..4f4b4a9 100644 --- a/docs/script/getting_started/start.py +++ b/docs/script/getting_started/start.py @@ -28,6 +28,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import os import sys +import signal from subprocess import call import subprocess import json @@ -41,6 +42,10 @@ "2 - AMD Zen 2", "3 - AMD Zen 3"] +def signal_handler(sig, frame): + print('You sent SIGINT signal, stoping docker compose stack') + call("./stop.sh") + def start_demo(): """ @@ -54,7 +59,8 @@ def start_demo(): "\n" + list_arch[2] + "\n" + list_arch[3] + "\n") - + + signal.signal(signal.SIGINT, signal_handler) val = "" choice = True while choice: diff --git a/docs/script/getting_started/start.sh b/docs/script/getting_started/start.sh index bc23b58..63a489e 100755 --- a/docs/script/getting_started/start.sh +++ b/docs/script/getting_started/start.sh @@ -8,4 +8,4 @@ docker compose logs formula -f & sleep 120 -docker compose down \ No newline at end of file +./stop.sh diff --git a/docs/script/getting_started/stop.sh b/docs/script/getting_started/stop.sh new file mode 100755 index 0000000..c31b6bd --- /dev/null +++ b/docs/script/getting_started/stop.sh @@ -0,0 +1,5 @@ +#!/bin/bash +set -ueo pipefail +set +x + +docker compose down From ebae5b0ae1e706f4f6e93b9445367514f56ce86a Mon Sep 17 00:00:00 2001 From: Inkedstinct Date: Mon, 21 Oct 2024 15:48:25 +0200 Subject: [PATCH 031/108] fix(getting-started): Add git clone instruction instead of archive download --- docs/getting_started.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/getting_started.md b/docs/getting_started.md index 5e388af..2bbcd12 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -76,11 +76,10 @@ quick glimpse ## Preparation -You can download the archive using : - +Clone the repository and get ready: ```sh -wget "https://github.com/powerapi-ng/powerapi-ng.github.io/tree/master/examples/powerapi-stack.zip" -unzip powerapi-stack.zip && cd powerapi-stack +git clone https://github.com/powerapi-ng/powerapi-ng.github.io.git +cd powerapi-ng.github.io/docs/script/getting-started ``` From this archive, you will have all the necessary files to get started, let us break down each element. @@ -96,6 +95,7 @@ From this archive, you will have all the necessary files to get started, let us |----hwpc-mongodb.json |--start.sh |--start.py +|--stop.sh |--pretty_print.py |--docker-compose.yaml |--.env From c108291dda1ca6beafa18050450b8f08e728290e Mon Sep 17 00:00:00 2001 From: Inkedstinct Date: Tue, 22 Oct 2024 09:38:31 +0200 Subject: [PATCH 032/108] docs(getting_started): Make cgroup part optionnal with more explicit information --- docs/getting_started.md | 67 ++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 35 deletions(-) diff --git a/docs/getting_started.md b/docs/getting_started.md index 2bbcd12..f80848b 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -18,45 +18,42 @@ So feel free to skip directly to the [preparation part](#preparation) if you don ## Define elements to monitor -PowerAPI being a monitoring tool for energy consumption, we will need to define -the necessary elements to monitor. -To do so we can use the Linux abstraction of [cGroups](https://www.redhat.com/sysadmin/cgroups-part-one). - -### Create a cGroup - -In order to create a cGroup, the following command can be used from CLI : - -```sh -cgcreate -g perf_event:new_cgroup_name -``` - -Check [here](./reference/cgroup/cgroup_v1_activation.md) if you have trouble -creating the cgroup. - -### Add processes to the group - -Once the group created, we need to fill it with processes to be monitored. -To do so, you can use the following : +!!! tip "Optionnal abstraction for fine-grain analysis" + This part is optionnal, it allows the definition of cgroups which can group chosen processes that make sense to you. + If you skip it, the next steps will work against all current process grouped. +PowerAPI being a monitoring tool for energy consumption, we can define logic grouping of elements to monitor. +To do so we can use the Linux abstraction of [cGroups](https://www.redhat.com/sysadmin/cgroups-part-one). +Kernel supports 2 versions of cGroups : v1 and v2. +To know which one your kernel supports, you can run : ```sh -cgclassify -g perf_event:new_cgroup_name PID -``` - -with `PID`, the pid of the process you want to monitor. If you want to monitor a -process composed of many processes, replace PID with `$(pidof process_name)`. - -### Installing a process to monitor - -[stress-ng](https://wiki.ubuntu.com/Kernel/Reference/stress-ng) can be used to -generate load on one's system. -An example usage, once installed : - -```sh -cgcreate -g perf_event:stress-ng-cgroup -stress-ng --cpu 1 --timeout 5m -cgclassify -g perf_event:stress-ng-cgroup $! +mount | grep '^cgroup' | awk '{print $1}' | uniq ``` +*Both versions can be supported at the same time*. + +??? "Create a cGroup" + + In order to create a cGroup, you can use: + + - cgroup v1 : [this doc](https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/cgroups.html#usage-examples-and-syntax) + - cgroup v2 : [this doc](https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#mounting) + + + +??? "Add processes to the group" + + Once the cgroup created, we need to fill it with processes to be monitored. + To do so, you can use: + + - cgroup v1 : [this doc](https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/cgroups.html#attaching-processes). + - cgroup v2 : [this doc](https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/cgroups.html#attaching-processes). + +??? warning "Installing a process to monitor" + + [stress-ng](https://wiki.ubuntu.com/Kernel/Reference/stress-ng) can be used to + generate load on one's system. **Be carefull** as it can be configured to be quite agressive. + ## Which components to get a complete stack If you wish to get started as soon as possible, the following archive will allow you to deploy the following elements : From bf4cf46eb0d477444ad5246959e3add82aa7de7b Mon Sep 17 00:00:00 2001 From: Kellian Leveque Date: Tue, 29 Oct 2024 14:29:09 +0100 Subject: [PATCH 033/108] docs(reference): Update overview.md - Remove duplicate lines in the overview.md file - Fix formatting issues in the overview.md file - Fix broken image links in the processors section - Update the tables headers --- docs/reference/overview.md | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/docs/reference/overview.md b/docs/reference/overview.md index 7bc63d7..c324ad8 100644 --- a/docs/reference/overview.md +++ b/docs/reference/overview.md @@ -3,8 +3,6 @@ This project aims to offer a range of tools to promote greener computing. The focus is on creating software-defined PowerMeters to measure the power consumption of various programs. At the heart of this initiative is the [PowerAPI](https://github.com/powerapi-ng/powerapi) toolkit, which facilitates the development of these PowerMeters. - - ## Software PowerMeters A software PowerMeter is an application built using PowerAPI components, designed to measure the power consumption of software running on a single machine or across a cluster of machines. @@ -13,10 +11,10 @@ The diagram below illustrates the overall architecture of a PowerMeter within th ![PowerAPI Architecture Overview](../assets/images/reference/overview/global-architecture.jpg){ width="1000px"} -A PowerMeter consists of two essential components: +A PowerMeter consists of two essential components: -- A [Sensor](#Sensor), which collects system usage metrics and generates usage reports. -- A [Formula](#Formula), which applies a computational model to the usage reports, producing power consumption data (in mJ). +- a [Sensor](#Sensor), which collects system usage metrics and generates usage reports. +- a [Formula](#Formula), which applies a computational model to the usage reports, producing power consumption data (in mJ). Additionally, [Preprocessors](./overview.md#Preprocessors) can be utilized to modify usage reports before they are processed by the Formula. @@ -30,9 +28,9 @@ The raw data collected is then stored as usage Reports in various formats, eithe ### Existing Sensors -| Sensor name | Documentation | GitHub Repository | Description | Supported | +| Sensor | Documentation | Repository | Description | Supported | | :---------------: |----------------------|--------------------------|----------------|---------------| -| HWPC | [HWPC Documentation](./sensors/hwpc-sensor.md) | https://github.com/powerapi-ng/hwpc-sensor | Hardware Performance Counters monitoring agent for containers | :material-check: Supported | +| HWPC | [HWPC Documentation](./sensors/hwpc-sensor.md) | [Github](https://github.com/powerapi-ng/hwpc-sensor) | Hardware Performance Counters monitoring agent for containers | :material-check: Supported | ## Formula @@ -47,9 +45,9 @@ There are two modes in which a Formula can operate: ### Existing Formulas -| Formula name | Documentation | GitHub Repository | Description | Supported | +| Formula | Documentation | Repository | Description | Supported | | :---------------: |----------------------|--------------------------|----------------|---------------| -| SmartWatts | [SmartWatts Documentation](./formulas/smatwatts.md) | https://github.com/powerapi-ng/smartwatts-formula | HSmartWatts is a formula for a self-adaptive software-defined power meter based on the PowerAPI framework. | :material-check: Supported | +| SmartWatts | [SmartWatts Documentation](./formulas/smatwatts.md) | [Github](https://github.com/powerapi-ng/smartwatts-formula) | HSmartWatts is a formula for a self-adaptive software-defined power meter based on the PowerAPI framework. | :material-check: Supported | ## Processors @@ -57,7 +55,7 @@ Processors allow for the customized filtering and modification of Reports. While The diagram below shows where Processors are integrated into the architecture of a Software PowerMeter. -![Processor integration in the processing pipeline](../..//docs/assets/images/reference/processors/processors.jpg) +![Processor integration in the processing pipeline](../assets/images/reference/processors/processors.jpg) ### Preprocessors @@ -66,8 +64,8 @@ Their role is to pre-process usage reports before the power consumption estimati #### Existing Preprocessors -| Preprocessor name | Documentation | GitHub Repository | Description | Supported | +| Preprocessor | Documentation | Repository | Description | Supported | | :---------------: |----------------------|--------------------------|----------------|---------------| -| k8sPreprocessor | [k8sPreprocessor Documentation](./processors/processors.md#k8spreprocessor) | https://github.com/powerapi-ng/powerapi/tree/master/src/powerapi/processor/pre/k8s | Add K8S Specific metadata to corresponding Reports | :material-check: Supported | -| libvirt | [libvirtPreprocessor Documentation](.processors/processors.md#libvirt) | https://github.com/powerapi-ng/powerapi/tree/master/src/powerapi/processor/pre/libvirt | Replace `libvirt ID` in Reports with `Open Stack UUID` | :material-check: Supported | +| k8sPreprocessor | [k8sPreprocessor Documentation](./processors/processors.md#k8spreprocessor) | [Github](https://github.com/powerapi-ng/powerapi/tree/master/src/powerapi/processor/pre/k8s) | Add K8S Specific metadata to corresponding Reports | :material-check: Supported | +| libvirt | [libvirtPreprocessor Documentation](.processors/processors.md#libvirt) | [Github](https://github.com/powerapi-ng/powerapi/tree/master/src/powerapi/processor/pre/libvirt) | Replace `libvirt ID` in Reports with `Open Stack UUID` | :material-check: Supported | From 99ecc97b8c4bff01a6caa078722a4e92849f54ca Mon Sep 17 00:00:00 2001 From: chachignot Date: Tue, 29 Oct 2024 14:58:36 +0100 Subject: [PATCH 034/108] docs(getting-started): Added cpu mapping Added a CPU mapping ,that can be used to verify if a CPU in supposed to be supported by the sensor/formula --- docs/script/getting_started/cpu.csv | 1857 +++++++++++++++++ .../formula/smartwatts-mongodb-csv.json | 33 +- .../getting_started/sensor/hwpc-mongodb.json | 10 +- docs/script/getting_started/start.py | 116 +- 4 files changed, 1960 insertions(+), 56 deletions(-) create mode 100644 docs/script/getting_started/cpu.csv diff --git a/docs/script/getting_started/cpu.csv b/docs/script/getting_started/cpu.csv new file mode 100644 index 0000000..69a5281 --- /dev/null +++ b/docs/script/getting_started/cpu.csv @@ -0,0 +1,1857 @@ +Manufacturer,Family,Name,Base frequency,TDP +Intel,Sandy bridge,Intel(R) Core(TM) i7-2720QM Processor,2.20 ,45 W +Intel,Sandy bridge,Intel(R) Core(TM) i5-2540M Processor,2.60 ,35 W +Intel,Sandy bridge,Intel(R) Core(TM) i5-2300 Processor,2.80 ,95 W +Intel,Sandy bridge,Intel(R) Core(TM) i5-2400 Processor,3.10,95 W +Intel,Sandy bridge,Intel(R) Core(TM) i5-2400S Processor,2.50 ,65 W +Intel,Sandy bridge,Intel(R) Core(TM) i5-2500 Processor,3.30 ,95 W +Intel,Sandy bridge,Intel(R) Core(TM) i5-2500K Processor,3.30 ,95 W +Intel,Sandy bridge,Intel(R) Core(TM) i5-2500S Processor,2.70 ,65 W +Intel,Sandy bridge,Intel(R) Core(TM) i5-2500T Processor,2.30 ,45 W +Intel,Sandy bridge,Intel(R) Core(TM) i7-2600 Processor,3.40 ,95 W +Intel,Sandy bridge,Intel(R) Core(TM) i7-2600K Processor,3.40 ,95 W +Intel,Sandy bridge,Intel(R) Core(TM) i7-2600S Processor,2.80 ,65 W +Intel,Sandy bridge,Intel(R) Core(TM) i7-2630QM Processor,2.00 ,45 W +Intel,Sandy bridge,Intel(R) Core(TM) i3-2310M Processor,2.10,35 W +Intel,Sandy bridge,Intel(R) Core(TM) i5-2410M Processor,2.30 ,35 W +Intel,Sandy bridge,Intel(R) Core(TM) i7-2820QM Processor,2.30 ,45 W +Intel,Sandy bridge,Intel(R) Core(TM) i5-2520M Processor,2.50 ,35 W +Intel,Sandy bridge,Intel(R) Core(TM) i7-2620M Processor,2.70 ,35 W +Intel,Sandy bridge,Intel(R) Core(TM) i7-2920XM Processor Extreme Edition,2.50 ,55 W +Intel,Sandy bridge,Intel(R) Xeon(R) Processor E3-1220,3.10,80 W +Intel,Sandy bridge,Intel(R) Xeon(R) Processor E3-1225,3.10,95 W +Intel,Sandy bridge,Intel(R) Xeon(R) Processor E3-1230,3.20 ,80 W +Intel,Sandy bridge,Intel(R) Xeon(R) Processor E3-1235,3.20 ,95 W +Intel,Sandy bridge,Intel(R) Xeon(R) Processor E3-1240,3.30 ,80 W +Intel,Sandy bridge,Intel(R) Xeon(R) Processor E3-1245,3.30 ,95 W +Intel,Sandy bridge,Intel(R) Xeon(R) Processor E3-1260L,2.40 ,45 W +Intel,Sandy bridge,Intel(R) Xeon(R) Processor E3-1270,3.40 ,80 W +Intel,Sandy bridge,Intel(R) Xeon(R) Processor E3-1275,3.40 ,95 W +Intel,Sandy bridge,Intel(R) Xeon(R) Processor E3-1280,3.50 ,95 W +Intel,Sandy bridge,Intel(R) Xeon(R) Processor E3-1220L,2.20 ,20 W +Intel,Sandy bridge,Intel(R) Celeron(R) Processor G530,2.40 ,65 W +Intel,Sandy bridge,Intel(R) Celeron(R) Processor G530T,2.00 ,35 W +Intel,Sandy bridge,Intel(R) Celeron(R) Processor G540,2.50 ,65 W +Intel,Sandy bridge,Intel(R) Celeron(R) Processor G540T,2.10,35 W +Intel,Sandy bridge,Intel(R) Celeron(R) Processor G550,2.60 ,65 W +Intel,Sandy bridge,Intel(R) Celeron(R) Processor G550T,2.20 ,35 W +Intel,Sandy bridge,Intel(R) Core(TM) i3-2100 Processor,3.10,65 W +Intel,Sandy bridge,Intel(R) Core(TM) i3-2100T Processor,2.50 ,35 W +Intel,Sandy bridge,Intel(R) Core(TM) i3-2102 Processor,3.10,65 W +Intel,Sandy bridge,Intel(R) Core(TM) i3-2120 Processor,3.30 ,65 W +Intel,Sandy bridge,Intel(R) Core(TM) i3-2120T Processor,2.60 ,35 W +Intel,Sandy bridge,Intel(R) Core(TM) i3-2130 Processor,3.40 ,65 W +Intel,Sandy bridge,Intel(R) Core(TM) i3-2312M Processor,2.10,35 W +Intel,Sandy bridge,Intel(R) Core(TM) i3-2330E Processor,2.20 ,35 W +Intel,Sandy bridge,Intel(R) Core(TM) i3-2330M Processor,2.20 ,35 W +Intel,Sandy bridge,Intel(R) Core(TM) i3-2350M Processor,2.30 ,35 W +Intel,Sandy bridge,Intel(R) Core(TM) i3-2370M Processor,2.40 ,35 W +Intel,Sandy bridge,Intel(R) Core(TM) i5-2310 Processor,2.90 ,95 W +Intel,Sandy bridge,Intel(R) Core(TM) i5-2320 Processor,3.00 ,95 W +Intel,Sandy bridge,Intel(R) Core(TM) i5-2390T Processor,2.70 ,35 W +Intel,Sandy bridge,Intel(R) Core(TM) i5-2430M Processor,2.40 ,35 W +Intel,Sandy bridge,Intel(R) Core(TM) i5-2450M Processor,2.50 ,35 W +Intel,Sandy bridge,Intel(R) Core(TM) i5-2510E Processor,2.50 ,35 W +Intel,Sandy bridge,Intel(R) Core(TM) i7-2635QM Processor,2.00 ,45 W +Intel,Sandy bridge,Intel(R) Core(TM) i7-2640M Processor,2.80 ,35 W +Intel,Sandy bridge,Intel(R) Core(TM) i7-2670QM Processor,2.20 ,45 W +Intel,Sandy bridge,Intel(R) Core(TM) i7-2675QM Processor,2.20 ,45 W +Intel,Sandy bridge,Intel(R) Core(TM) i7-2710QE Processor,2.10,45 W +Intel,Sandy bridge,Intel(R) Core(TM) i7-2760QM Processor,2.40 ,45 W +Intel,Sandy bridge,Intel(R) Core(TM) i7-2860QM Processor,2.50 ,45 W +Intel,Sandy bridge,Intel(R) Core(TM) i7-2960XM Processor Extreme Edition,2.70 ,55 W +Intel,Sandy bridge,Intel(R) Pentium(R) Processor G620,2.60 ,65 W +Intel,Sandy bridge,Intel(R) Pentium(R) Processor G620T,2.20 ,35 W +Intel,Sandy bridge,Intel(R) Pentium(R) Processor G622,2.60 ,65 W +Intel,Sandy bridge,Intel(R) Pentium(R) Processor G630,2.70 ,65 W +Intel,Sandy bridge,Intel(R) Pentium(R) Processor G630T,2.30 ,35 W +Intel,Sandy bridge,Intel(R) Pentium(R) Processor G632,2.70 ,65 W +Intel,Sandy bridge,Intel(R) Pentium(R) Processor G640,2.80 ,65 W +Intel,Sandy bridge,Intel(R) Pentium(R) Processor G640T,2.40 ,35 W +Intel,Sandy bridge,Intel(R) Pentium(R) Processor G840,2.80 ,65 W +Intel,Sandy bridge,Intel(R) Pentium(R) Processor G850,2.90 ,65 W +Intel,Sandy bridge,Intel(R) Pentium(R) Processor G860,3.00 ,65 W +Intel,Sandy bridge,Intel(R) Pentium(R) Processor G870,3.10,65 W +Intel,Sandy bridge,Intel(R) Core(TM) i7-2629M Processor,2.10,25 W +Intel,Sandy bridge,Intel(R) Core(TM) i7-2649M Processor,2.30 ,25 W +Intel,Sandy bridge,Intel(R) Core(TM) i7-2657M Processor,1.60 ,17 W +Intel,Sandy bridge,Intel(R) Core(TM) i7-2617M Processor,1.50 ,17 W +Intel,Sandy bridge,Intel(R) Core(TM) i7-2677M Processor,1.80 ,17 W +Intel,Sandy bridge,Intel(R) Core(TM) i7-2637M Processor,1.70 ,17 W +Intel,Sandy bridge,Intel(R) Core(TM) i5-2537M Processor,1.40 ,17 W +Intel,Sandy bridge,Intel(R) Core(TM) i5-2557M Processor,1.70 ,17 W +Intel,Sandy bridge,Intel(R) Core(TM) i3-2357M Processor,1.30 ,17 W +Intel,Sandy bridge,Intel(R) Core(TM) i7-2655LE Processor,2.20 ,25 W +Intel,Sandy bridge,Intel(R) Core(TM) i3-2310E Processor,2.10,35 W +Intel,Sandy bridge,Intel(R) Core(TM) i7-2715QE Processor,2.10,45 W +Intel,Sandy bridge,Intel(R) Core(TM) i7-2610UE Processor,1.50 ,17 W +Intel,Sandy bridge,Intel(R) Core(TM) i3-2340UE Processor,1.30 ,17 W +Intel,Sandy bridge,Intel(R) Core(TM) i5-2515E Processor,2.50 ,35 W +Intel,Sandy bridge,Intel(R) Core(TM) i3-2377M Processor,1.50 ,17 W +Intel,Sandy bridge,Intel(R) Core(TM) i5-2405S Processor,2.50 ,65 W +Intel,Sandy bridge,Intel(R) Core(TM) i3-2105 Processor,3.10,65 W +Intel,Sandy bridge,Intel(R) Xeon(R) Processor E3-1290,3.60 ,95 W +Intel,Sandy bridge,Intel(R) Pentium(R) Processor B940,2.00 ,35 W +Intel,Sandy bridge,Intel(R) Pentium(R) Processor B950,2.10,35 W +Intel,Sandy bridge,Intel(R) Pentium(R) Processor 957,1.20 ,17 W +Intel,Sandy bridge,Intel(R) Celeron(R) Processor B810,1.60 ,35 W +Intel,Sandy bridge,Intel(R) Celeron(R) Processor B810E,1.60 ,35 W +Intel,Sandy bridge,Intel(R) Celeron(R) Processor 847E,1.10,17 W +Intel,Sandy bridge,Intel(R) Celeron(R) Processor 827E,1.40 ,17 W +Intel,Sandy bridge,Intel(R) Core(TM) i5-2467M Processor,1.60 ,17 W +Intel,Sandy bridge,Intel(R) Celeron(R) Processor G440,1.60 ,35 W +Intel,Sandy bridge,Intel(R) Core(TM) i3-2125 Processor,3.30 ,65 W +Intel,Sandy bridge,Intel(R) Celeron(R) Processor B710,1.60 ,35 W +Intel,Sandy bridge,Intel(R) Celeron(R) Processor B800,1.50 ,35 W +Intel,Sandy bridge,Intel(R) Celeron(R) Processor 787,1.30 ,17 W +Intel,Sandy bridge,Intel(R) Celeron(R) Processor 857,1.20 ,17 W +Intel,Sandy bridge,Intel(R) Core(TM) i3-2367M Processor,1.40 ,17 W +Intel,Sandy bridge,Intel(R) Celeron(R) Processor B840,1.90 ,35 W +Intel,Sandy bridge,Intel(R) Pentium(R) Processor 967,1.30 ,17 W +Intel,Sandy bridge,Intel(R) Pentium(R) Processor B960,2.20 ,35 W +Intel,Sandy bridge,Intel(R) Core(TM) i5-2435M Processor,2.40 ,35 W +Intel,Sandy bridge,Intel(R) Core(TM) i7-2700K Processor,3.50 ,95 W +Intel,Sandy bridge,Intel(R) Celeron(R) Processor 807UE,1.00 ,10 W +Intel,Sandy bridge,Intel(R) Celeron(R) Processor G460,1.80 ,35 W +Intel,Sandy bridge,Intel(R) Pentium(R) Processor B970,2.30 ,35 W +Intel,Sandy bridge,Intel(R) Pentium(R) Processor 977,1.40 ,17 W +Intel,Sandy bridge,Intel(R) Celeron(R) Processor 797,1.40 ,17 W +Intel,Sandy bridge,Intel(R) Celeron(R) Processor 867,1.30 ,17 W +Intel,Sandy bridge,Intel(R) Celeron(R) Processor B815,1.60 ,35 W +Intel,Sandy bridge,Intel(R) Celeron(R) Processor B720,1.70 ,35 W +Intel,Sandy bridge,Intel(R) Core(TM) i5-2450P Processor,3.20 ,95 W +Intel,Sandy bridge,Intel(R) Core(TM) i5-2380P Processor,3.10,95 W +Intel,Sandy bridge,Intel(R) Core(TM) i5-2550K Processor,3.40 ,95 W +Intel,Sandy bridge,Intel(R) Pentium(R) Processor G860T,2.60 ,35 W +Intel,Sandy bridge,Intel(R) Celeron(R) Processor 877,1.40 ,17 W +Intel,Sandy bridge,Intel(R) Celeron(R) Processor B820,1.70 ,35 W +Intel,Sandy bridge,Intel(R) Pentium(R) Processor 987,1.50 ,17 W +Intel,Sandy bridge,Intel(R) Celeron(R) Processor 807,1.50 ,17 W +Intel,Sandy bridge,Intel(R) Celeron(R) Processor G555,2.70 ,65 W +Intel,Sandy bridge,Intel(R) Pentium(R) Processor G645,2.90 ,65 W +Intel,Sandy bridge,Intel(R) Pentium(R) Processor 997,1.60 ,17 W +Intel,Sandy bridge,Intel(R) Celeron(R) Processor 887,1.50 ,17 W +Intel,Sandy bridge,Intel(R) Celeron(R) Processor G465,1.90 ,35 W +Intel,Sandy bridge,Intel(R) Pentium(R) Processor G645T,2.50 ,35 W +Intel,Sandy bridge,Intel(R) Pentium(R) Processor B980,2.40 ,35 W +Intel,Sandy bridge,Intel(R) Core(TM) i3-2365M Processor,1.40 ,17 W +Intel,Sandy bridge,Intel(R) Core(TM) i3-2328M Processor,2.20 ,35 W +Intel,Sandy bridge,Intel(R) Celeron(R) Processor B830,1.80 ,35 W +Intel,Sandy bridge,Intel(R) Core(TM) i3-2375M Processor,1.50 ,17 W +Intel,Sandy bridge,Intel(R) Celeron(R) Processor G470,2.00 ,35 W +Intel,Sandy bridge,Intel(R) Core(TM) i3-2348M Processor,2.30 ,35 W +Intel,Kaby Lake R,Intel(R) Core(TM) i7-8550U Processor,1.80 ,15 W +Intel,Kaby Lake R,Intel(R) Core(TM) i5-8250U Processor,1.60 ,15 W +Intel,Kaby Lake R,Intel(R) Core(TM) i7-8650U Processor,1.90 ,15 W +Intel,Kaby Lake R,Intel(R) Core(TM) i5-8350U Processor,1.70 ,15 W +Intel,Kaby Lake R,Intel(R) Core(TM) i3-8130U Processor,2.20 ,15 W +Intel,Kaby Lake R,Intel(R) Pentium(R) Gold Processor 4417U,2.30 ,15 W +Intel,Kaby Lake R,Intel(R) Celeron(R) Processor 3867U,1.80 ,15 W +Intel,Ivy bridge,Intel(R) Core(TM) i7-3920XM Processor Extreme Edition,2.90 ,55 W +Intel,Ivy bridge,Intel(R) Core(TM) i7-3820QM Processor,2.70 ,45 W +Intel,Ivy bridge,Intel(R) Core(TM) i7-3720QM Processor,2.60 ,45 W +Intel,Ivy bridge,Intel(R) Core(TM) i7-3520M Processor,2.90 ,35 W +Intel,Ivy bridge,Intel(R) Core(TM) i5-3360M Processor,2.80 ,35 W +Intel,Ivy bridge,Intel(R) Core(TM) i5-3320M Processor,2.60 ,35 W +Intel,Ivy bridge,Intel(R) Core(TM) i7-3667U Processor,2.00 ,17 W +Intel,Ivy bridge,Intel(R) Core(TM) i7-3610QM Processor,2.30 ,45 W +Intel,Ivy bridge,Intel(R) Core(TM) i7-3615QM Processor,2.30 ,45 W +Intel,Ivy bridge,Intel(R) Core(TM) i7-3612QM Processor,2.10,35 W +Intel,Ivy bridge,Intel(R) Core(TM) i5-3427U Processor,1.80 ,17 W +Intel,Ivy bridge,Intel(R) Core(TM) i5-3330 Processor,3.00 ,77 W +Intel,Ivy bridge,Intel(R) Core(TM) i5-3330S Processor,2.70 ,65 W +Intel,Ivy bridge,Intel(R) Core(TM) i5-3450 Processor,3.10,77 W +Intel,Ivy bridge,Intel(R) Core(TM) i5-3450S Processor,2.80 ,65 W +Intel,Ivy bridge,Intel(R) Core(TM) i5-3475S Processor,2.90 ,65 W +Intel,Ivy bridge,Intel(R) Core(TM) i5-3550 Processor,3.30 ,77 W +Intel,Ivy bridge,Intel(R) Core(TM) i5-3550S Processor,3.00 ,65 W +Intel,Ivy bridge,Intel(R) Core(TM) i5-3570K Processor,3.40 ,77 W +Intel,Ivy bridge,Intel(R) Core(TM) i5-3570T Processor,2.30 ,45 W +Intel,Ivy bridge,Intel(R) Core(TM) i7-3770K Processor,3.50 ,77 W +Intel,Ivy bridge,Intel(R) Core(TM) i7-3770S Processor,3.10,65 W +Intel,Ivy bridge,Intel(R) Core(TM) i7-3770T Processor,2.50 ,45 W +Intel,Ivy bridge,Intel(R) Pentium(R) Processor G2120,3.10,55 W +Intel,Ivy bridge,Intel(R) Core(TM) i3-3240 Processor,3.40 ,55 W +Intel,Ivy bridge,Intel(R) Core(TM) i3-3225 Processor,3.30 ,55 W +Intel,Ivy bridge,Intel(R) Core(TM) i3-3220 Processor,3.30 ,55 W +Intel,Ivy bridge,Intel(R) Core(TM) i3-3220T Processor,2.80 ,35 W +Intel,Ivy bridge,Intel(R) Core(TM) i3-3217UE Processor,1.60 ,17 W +Intel,Ivy bridge,Intel(R) Core(TM) i3-3217U Processor,1.80 ,17 W +Intel,Ivy bridge,Intel(R) Core(TM) i3-3120ME Processor,2.40 ,35 W +Intel,Ivy bridge,Intel(R) Core(TM) i3-3110M Processor,2.40 ,35 W +Intel,Ivy bridge,Intel(R) Core(TM) i5-3570S Processor,3.10,65 W +Intel,Ivy bridge,Intel(R) Core(TM) i5-3570 Processor,3.40 ,77 W +Intel,Ivy bridge,Intel(R) Core(TM) i5-3470T Processor,2.90 ,35 W +Intel,Ivy bridge,Intel(R) Core(TM) i5-3610ME Processor,2.70 ,35 W +Intel,Ivy bridge,Intel(R) Core(TM) i5-3317U Processor,1.70 ,17 W +Intel,Ivy bridge,Intel(R) Core(TM) i5-3210M Processor,2.50 ,35 W +Intel,Ivy bridge,Intel(R) Core(TM) i7-3615QE Processor,2.30 ,45 W +Intel,Ivy bridge,Intel(R) Core(TM) i7-3612QE Processor,2.10,35 W +Intel,Ivy bridge,Intel(R) Core(TM) i7-3610QE Processor,2.30 ,45 W +Intel,Ivy bridge,Intel(R) Core(TM) i7-3555LE Processor,2.50 ,25 W +Intel,Ivy bridge,Intel(R) Core(TM) i7-3517UE Processor,1.70 ,17 W +Intel,Ivy bridge,Intel(R) Core(TM) i7-3517U Processor,1.90 ,17 W +Intel,Ivy bridge,Intel(R) Core(TM) i7-3770 Processor,3.40 ,77 W +Intel,Ivy bridge,Intel(R) Xeon(R) Processor E3-1290 v2,3.70 ,87 W +Intel,Ivy bridge,Intel(R) Xeon(R) Processor E3-1280 v2,3.60 ,69 W +Intel,Ivy bridge,Intel(R) Xeon(R) Processor E3-1275 v2,3.50 ,77 W +Intel,Ivy bridge,Intel(R) Xeon(R) Processor E3-1270 v2,3.50 ,69 W +Intel,Ivy bridge,Intel(R) Xeon(R) Processor E3-1265L v2,2.50 ,45 W +Intel,Ivy bridge,Intel(R) Xeon(R) Processor E3-1245 v2,3.40 ,77 W +Intel,Ivy bridge,Intel(R) Xeon(R) Processor E3-1240 v2,3.40 ,69 W +Intel,Ivy bridge,Intel(R) Xeon(R) Processor E3-1230 v2,3.30 ,69 W +Intel,Ivy bridge,Intel(R) Xeon(R) Processor E3-1225 v2,3.20 ,77 W +Intel,Ivy bridge,Intel(R) Xeon(R) Processor E3-1220 v2,3.10,69 W +Intel,Ivy bridge,Intel(R) Xeon(R) Processor E3-1220L v2,2.30 ,17 W +Intel,Ivy bridge,Intel(R) Pentium(R) Processor G2100T,2.60 ,35 W +Intel,Ivy bridge,Intel(R) Core(TM) i3-3240T Processor,2.90 ,35 W +Intel,Ivy bridge,Intel(R) Core(TM) i5-3470S Processor,2.90 ,65 W +Intel,Ivy bridge,Intel(R) Core(TM) i5-3470 Processor,3.20 ,77 W +Intel,Ivy bridge,Intel(R) Core(TM) i5-3350P Processor,3.10,69 W +Intel,Ivy bridge,Intel(R) Core(TM) i7-3840QM Processor,2.80 ,45 W +Intel,Ivy bridge,Intel(R) Core(TM) i7-3740QM Processor,2.70 ,45 W +Intel,Ivy bridge,Intel(R) Pentium(R) Processor G2130,3.20 ,55 W +Intel,Ivy bridge,Intel(R) Core(TM) i3-3210 Processor,3.20 ,55 W +Intel,Ivy bridge,Intel(R) Pentium(R) Processor G2020T,2.50 ,35 W +Intel,Ivy bridge,Intel(R) Pentium(R) Processor G2020,2.90 ,55 W +Intel,Ivy bridge,Intel(R) Pentium(R) Processor G2010,2.80 ,55 W +Intel,Ivy bridge,Intel(R) Celeron(R) Processor G1610,2.60 ,55 W +Intel,Ivy bridge,Intel(R) Celeron(R) Processor G1620,2.70 ,55 W +Intel,Ivy bridge,Intel(R) Celeron(R) Processor G1610T,2.30 ,35 W +Intel,Ivy bridge,Intel(R) Core(TM) i7-3940XM Processor Extreme Edition,3.00 ,55 W +Intel,Ivy bridge,Intel(R) Pentium(R) Processor G2120T,2.70 ,35 W +Intel,Ivy bridge,Intel(R) Pentium(R) Processor 2020M,2.40 ,35 W +Intel,Ivy bridge,Intel(R) Core(TM) i7-3540M Processor,3.00 ,35 W +Intel,Ivy bridge,Intel(R) Core(TM) i5-3380M Processor,2.90 ,35 W +Intel,Ivy bridge,Intel(R) Core(TM) i5-3340M Processor,2.70 ,35 W +Intel,Ivy bridge,Intel(R) Core(TM) i7-3687U Processor,2.10,17 W +Intel,Ivy bridge,Intel(R) Core(TM) i5-3437U Processor,1.90 ,17 W +Intel,Ivy bridge,Intel(R) Core(TM) i7-3632QM Processor,2.20 ,35 W +Intel,Ivy bridge,Intel(R) Core(TM) i7-3630QM Processor,2.40 ,45 W +Intel,Ivy bridge,Intel(R) Core(TM) i7-3635QM Processor,2.40 ,45 W +Intel,Ivy bridge,Intel(R) Core(TM) i3-3120M Processor,2.50 ,35 W +Intel,Ivy bridge,Intel(R) Pentium(R) Processor 2117U,1.80 ,17 W +Intel,Ivy bridge,Intel(R) Celeron(R) Processor 1020M,2.10,35 W +Intel,Ivy bridge,Intel(R) Celeron(R) Processor 1037U,1.80 ,17 W +Intel,Ivy bridge,Intel(R) Core(TM) i3-3229Y Processor,1.40 ,13 W +Intel,Ivy bridge,Intel(R) Core(TM) i5-3339Y Processor,1.50 ,13 W +Intel,Ivy bridge,Intel(R) Core(TM) i5-3439Y Processor,1.50 ,13 W +Intel,Ivy bridge,Intel(R) Core(TM) i7-3689Y Processor,1.50 ,13 W +Intel,Ivy bridge,Intel(R) Pentium(R) Processor 2129Y,1.10,10 W +Intel,Ivy bridge,Intel(R) Core(TM) i7-3537U Processor,2.00 ,17 W +Intel,Ivy bridge,Intel(R) Core(TM) i5-3337U Processor,1.80 ,17 W +Intel,Ivy bridge,Intel(R) Core(TM) i5-3230M Processor,2.60 ,35 W +Intel,Ivy bridge,Intel(R) Core(TM) i3-3227U Processor,1.90 ,17 W +Intel,Ivy bridge,Intel(R) Core(TM) i3-3130M Processor,2.60 ,35 W +Intel,Ivy bridge,Intel(R) Pentium(R) Processor 2030M,2.50 ,35 W +Intel,Ivy bridge,Intel(R) Celeron(R) Processor 1000M,1.80 ,35 W +Intel,Ivy bridge,Intel(R) Celeron(R) Processor 1007U,1.50 ,17 W +Intel,Ivy bridge,Intel(R) Celeron(R) Processor 1020E,2.20 ,35 W +Intel,Ivy bridge,Intel(R) Celeron(R) Processor 1047UE,1.40 ,17 W +Intel,Ivy bridge,Intel(R) Celeron(R) Processor 927UE,1.50 ,17 W +Intel,Ivy bridge,Intel(R) Core(TM) i3-3250 Processor,3.50 ,55 W +Intel,Ivy bridge,Intel(R) Core(TM) i3-3250T Processor,3.00 ,35 W +Intel,Ivy bridge,Intel(R) Core(TM) i3-3245 Processor,3.40 ,55 W +Intel,Ivy bridge,Intel(R) Pentium(R) Processor G2140,3.30 ,55 W +Intel,Ivy bridge,Intel(R) Pentium(R) Processor G2030T,2.60 ,35 W +Intel,Ivy bridge,Intel(R) Pentium(R) Processor G2030,3.00 ,55 W +Intel,Ivy bridge,Intel(R) Celeron(R) Processor 1019Y,1.00 ,10 W +Intel,Ivy bridge,Intel(R) Pentium(R) Processor 2127U,1.90 ,17 W +Intel,Ivy bridge,Intel(R) Celeron(R) Processor 1017U,1.60 ,17 W +Intel,Ivy bridge,Intel(R) Celeron(R) Processor 1005M,1.90 ,35 W +Intel,Ivy bridge,Intel(R) Xeon(R) Processor E7-2850 v2,2.30 ,105 W +Intel,Ivy bridge,Intel(R) Xeon(R) Processor E7-2870 v2,2.30 ,130 W +Intel,Ivy bridge,Intel(R) Xeon(R) Processor E7-2880 v2,2.50 ,130 W +Intel,Ivy bridge,Intel(R) Xeon(R) Processor E7-2890 v2,2.80 ,155 W +Intel,Ivy bridge,Intel(R) Xeon(R) Processor E7-4809 v2,1.90 ,105 W +Intel,Ivy bridge,Intel(R) Xeon(R) Processor E7-4820 v2,2.00 ,105 W +Intel,Ivy bridge,Intel(R) Xeon(R) Processor E7-4830 v2,2.20 ,105 W +Intel,Ivy bridge,Intel(R) Xeon(R) Processor E7-4850 v2,2.30 ,105 W +Intel,Ivy bridge,Intel(R) Xeon(R) Processor E7-4860 v2,2.60 ,130 W +Intel,Ivy bridge,Intel(R) Xeon(R) Processor E7-4870 v2,2.30 ,130 W +Intel,Ivy bridge,Intel(R) Xeon(R) Processor E7-4890 v2,2.80 ,155 W +Intel,Ivy bridge,Intel(R) Xeon(R) Processor E7-8850 v2,2.30 ,105 W +Intel,Ivy bridge,Intel(R) Xeon(R) Processor E7-8857 v2,3.00 ,130 W +Intel,Ivy bridge,Intel(R) Xeon(R) Processor E7-8870 v2,2.30 ,130 W +Intel,Ivy bridge,Intel(R) Xeon(R) Processor E7-8880L v2,2.20 ,105 W +Intel,Ivy bridge,Intel(R) Xeon(R) Processor E7-8880 v2,2.50 ,130 W +Intel,Ivy bridge,Intel(R) Xeon(R) Processor E7-8890 v2,2.80 ,155 W +Intel,Ivy bridge,Intel(R) Xeon(R) Processor E7-8891 v2,3.20 ,155 W +Intel,Ivy bridge,Intel(R) Xeon(R) Processor E7-8893 v2,3.40 ,155 W +Intel,Ivy bridge,Intel(R) Xeon(R) Processor E7-4880 v2,2.50 ,130 W +Intel,Ivy bridge,Intel(R) Core(TM) i5-3340 Processor,3.10,77 W +Intel,Ivy bridge,Intel(R) Core(TM) i5-3340S Processor,2.80 ,65 W +Intel,Ivy bridge,Intel(R) Celeron(R) Processor G1630,2.80 ,55 W +Intel,Ivy bridge,Intel(R) Celeron(R) Processor G1620T,2.40 ,35 W +Intel,Ivy bridge,Intel(R) Pentium(R) Processor 1405 v2,1.40 ,40 W +Intel,Ivy bridge,Intel(R) Pentium(R) Processor A1018,2.10,35 W +Intel,Haswell,Intel(R) Core(TM) i5-4430 Processor,3.00 ,84 W +Intel,Haswell,Intel(R) Core(TM) i5-4430S Processor,2.70 ,65 W +Intel,Haswell,Intel(R) Core(TM) i5-4440 Processor,3.10,84 W +Intel,Haswell,Intel(R) Core(TM) i5-4440S Processor,2.80 ,65 W +Intel,Haswell,Intel(R) Core(TM) i5-4570 Processor,3.20 ,84 W +Intel,Haswell,Intel(R) Core(TM) i5-4570S Processor,2.90 ,65 W +Intel,Haswell,Intel(R) Core(TM) i5-4570T Processor,2.90 ,35 W +Intel,Haswell,Intel(R) Core(TM) i5-4670 Processor,3.40 ,84 W +Intel,Haswell,Intel(R) Core(TM) i5-4670K Processor,3.40 ,84 W +Intel,Haswell,Intel(R) Core(TM) i5-4670S Processor,3.10,65 W +Intel,Haswell,Intel(R) Core(TM) i5-4670T Processor,2.30 ,45 W +Intel,Haswell,Intel(R) Core(TM) i7-4765T Processor,2.00 ,35 W +Intel,Haswell,Intel(R) Core(TM) i7-4770 Processor,3.40 ,84 W +Intel,Haswell,Intel(R) Core(TM) i7-4770K Processor,3.50 ,84 W +Intel,Haswell,Intel(R) Core(TM) i7-4770S Processor,3.10,65 W +Intel,Haswell,Intel(R) Core(TM) i7-4770T Processor,2.50 ,45 W +Intel,Haswell,Intel(R) Core(TM) i3-4130 Processor,3.40 ,54 W +Intel,Haswell,Intel(R) Core(TM) i3-4130T Processor,2.90 ,35 W +Intel,Haswell,Intel(R) Core(TM) i3-4150 Processor,3.50 ,54 W +Intel,Haswell,Intel(R) Core(TM) i3-4150T Processor,3.00 ,35 W +Intel,Haswell,Intel(R) Core(TM) i3-4160 Processor,3.60 ,54 W +Intel,Haswell,Intel(R) Core(TM) i3-4160T Processor,3.10,35 W +Intel,Haswell,Intel(R) Core(TM) i3-4170 Processor,3.70 ,54 W +Intel,Haswell,Intel(R) Core(TM) i3-4350 Processor,3.60 ,54 W +Intel,Haswell,Intel(R) Core(TM) i3-4350T Processor,3.10,35 W +Intel,Haswell,Intel(R) Core(TM) i3-4360 Processor,3.70 ,54 W +Intel,Haswell,Intel(R) Core(TM) i3-4360T Processor,3.20 ,35 W +Intel,Haswell,Intel(R) Core(TM) i3-4370 Processor,3.80 ,54 W +Intel,Haswell,Intel(R) Core(TM) i7-4771 Processor,3.50 ,84 W +Intel,Haswell,Intel(R) Core(TM) i3-4330 Processor,3.50 ,54 W +Intel,Haswell,Intel(R) Core(TM) i3-4330T Processor,3.00 ,35 W +Intel,Haswell,Intel(R) Core(TM) i3-4340 Processor,3.60 ,54 W +Intel,Haswell,Intel(R) Pentium(R) Processor G3220,3.00 ,53 W +Intel,Haswell,Intel(R) Pentium(R) Processor G3220T,2.60 ,35 W +Intel,Haswell,Intel(R) Pentium(R) Processor G3420,3.20 ,53 W +Intel,Haswell,Intel(R) Pentium(R) Processor G3420T,2.70 ,35 W +Intel,Haswell,Intel(R) Pentium(R) Processor G3430,3.30 ,53 W +Intel,Haswell,Intel(R) Core(TM) i5-4460T Processor,1.90 ,35 W +Intel,Haswell,Intel(R) Core(TM) i5-4590T Processor,2.00 ,35 W +Intel,Haswell,Intel(R) Celeron(R) Processor G1830,2.80 ,53 W +Intel,Haswell,Intel(R) Celeron(R) Processor G1820,2.70 ,53 W +Intel,Haswell,Intel(R) Celeron(R) Processor G1820T,2.40 ,35 W +Intel,Haswell,Intel(R) Pentium(R) Processor G3450,3.40 ,53 W +Intel,Haswell,Intel(R) Pentium(R) Processor G3450T,2.90 ,35 W +Intel,Haswell,Intel(R) Pentium(R) Processor G3440,3.30 ,53 W +Intel,Haswell,Intel(R) Pentium(R) Processor G3440T,2.80 ,35 W +Intel,Haswell,Intel(R) Pentium(R) Processor G3240,3.10,53 W +Intel,Haswell,Intel(R) Pentium(R) Processor G3240T,2.70 ,35 W +Intel,Haswell,Intel(R) Celeron(R) Processor G1850,2.90 ,53 W +Intel,Haswell,Intel(R) Celeron(R) Processor G1840,2.80 ,53 W +Intel,Haswell,Intel(R) Celeron(R) Processor G1840T,2.50 ,35 W +Intel,Haswell,Intel(R) Core(TM) i7-4790 Processor,3.60 ,84 W +Intel,Haswell,Intel(R) Core(TM) i7-4790S Processor,3.20 ,65 W +Intel,Haswell,Intel(R) Core(TM) i7-4790T Processor,2.70 ,45 W +Intel,Haswell,Intel(R) Core(TM) i5-4690 Processor,3.50 ,84 W +Intel,Haswell,Intel(R) Core(TM) i5-4690S Processor,3.20 ,65 W +Intel,Haswell,Intel(R) Core(TM) i5-4690T Processor,2.50 ,45 W +Intel,Haswell,Intel(R) Core(TM) i7-4785T Processor,2.20 ,35 W +Intel,Haswell,Intel(R) Core(TM) i5-4590 Processor,3.30 ,84 W +Intel,Haswell,Intel(R) Core(TM) i5-4590S Processor,3.00 ,65 W +Intel,Haswell,Intel(R) Core(TM) i5-4460 Processor,3.20 ,84 W +Intel,Haswell,Intel(R) Core(TM) i5-4460S Processor,2.90 ,65 W +Intel,Haswell,Intel(R) Core(TM) i3-4370T Processor,3.30 ,35 W +Intel,Haswell,Intel(R) Core(TM) i3-4170T Processor,3.20 ,35 W +Intel,Haswell,Intel(R) Pentium(R) Processor G3258,3.20 ,53 W +Intel,Haswell,Intel(R) Pentium(R) Processor G3460,3.50 ,53 W +Intel,Haswell,Intel(R) Pentium(R) Processor G3460T,3.00 ,35 W +Intel,Haswell,Intel(R) Pentium(R) Processor G3250,3.20 ,53 W +Intel,Haswell,Intel(R) Pentium(R) Processor G3250T,2.80 ,35 W +Intel,Haswell,Intel(R) Pentium(R) Processor G3260,3.30 ,53 W +Intel,Haswell,Intel(R) Pentium(R) Processor G3260T,2.90 ,35 W +Intel,Haswell,Intel(R) Pentium(R) Processor G3470,3.60 ,53 W +Intel,Haswell,Intel(R) Core(TM) i3-4010U Processor,1.70 ,15 W +Intel,Haswell,Intel(R) Core(TM) i7-4650U Processor,1.70 ,15 W +Intel,Haswell,Intel(R) Xeon(R) Processor E3-1225 v3,3.20 ,84 W +Intel,Haswell,Intel(R) Xeon(R) Processor E3-1275 v3,3.50 ,84 W +Intel,Haswell,Intel(R) Xeon(R) Processor E3-1268L v3,2.30 ,45 W +Intel,Haswell,Intel(R) Core(TM) i5-4570TE Processor,2.70 ,35 W +Intel,Haswell,Intel(R) Core(TM) i7-4700EQ Processor,2.40 ,47 W +Intel,Haswell,Intel(R) Core(TM) i5-4402EC Processor,2.50 ,27 W +Intel,Haswell,Intel(R) Core(TM) i7-4700EC Processor,2.70 ,43 W +Intel,Haswell,Intel(R) Core(TM) i7-4702EC Processor,2.00 ,27 W +Intel,Haswell,Intel(R) Core(TM) i7-4770TE Processor,2.30 ,45 W +Intel,Haswell,Intel(R) Core(TM) i5-4400E Processor,2.70 ,37 W +Intel,Haswell,Intel(R) Core(TM) i3-4100E Processor,2.40 ,37 W +Intel,Haswell,Intel(R) Core(TM) i3-4102E Processor,1.60 ,25 W +Intel,Haswell,Intel(R) Celeron(R) Processor 2000E,2.20 ,37 W +Intel,Haswell,Intel(R) Celeron(R) Processor 2002E,1.50 ,25 W +Intel,Haswell,Intel(R) Core(TM) i5-4402E Processor,1.60 ,25 W +Intel,Haswell,Intel(R) Core(TM) i5-4300U Processor,1.90 ,15 W +Intel,Haswell,Intel(R) Celeron(R) Processor 2980U,1.60 ,15 W +Intel,Haswell,Intel(R) Core(TM) i3-4330TE Processor,2.40 ,35 W +Intel,Haswell,Intel(R) Pentium(R) Processor G3320TE,2.30 ,35 W +Intel,Haswell,Intel(R) Celeron(R) Processor G1820TE,2.20 ,35 W +Intel,Haswell,Intel(R) Core(TM) i3-4110E Processor,2.60 ,37 W +Intel,Haswell,Intel(R) Core(TM) i3-4112E Processor,1.80 ,25 W +Intel,Haswell,Intel(R) Core(TM) i5-4410E Processor,2.90 ,37 W +Intel,Haswell,Intel(R) Core(TM) i5-4422E Processor,1.80 ,25 W +Intel,Haswell,Intel(R) Core(TM) i3-4340TE Processor,2.60 ,35 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-2628L v3,2.00 ,75 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-2609 v3,1.90 ,85 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-2648L v3,1.80 ,75 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-2658 v3,2.20 ,105 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-2680 v3,2.50 ,120 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-2608L v3,2.00 ,52 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-2618L v3,2.30 ,75 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-2620 v3,2.40 ,85 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-2640 v3,2.60 ,90 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-1428L v3,2.00 ,65 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-2408L v3,1.80 ,45 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-2418L v3,2.00 ,50 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-2428L v3,1.80 ,55 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-2438L v3,1.80 ,70 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-4648 v3,1.70 ,105 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-2658A v3,2.20 ,105 W +Intel,Haswell,Intel(R) Core(TM) i5-4200H Processor,2.80 ,47 W +Intel,Haswell,Intel(R) Core(TM) i5-4250U Processor,1.30 ,15 W +Intel,Haswell,Intel(R) Core(TM) i5-4260U Processor,1.40 ,15 W +Intel,Haswell,Intel(R) Core(TM) i5-4350U Processor,1.40 ,15 W +Intel,Haswell,Intel(R) Core(TM) i5-4360U Processor,1.50 ,15 W +Intel,Haswell,Intel(R) Core(TM) i3-4000M Processor,2.40 ,37 W +Intel,Haswell,Intel(R) Core(TM) i3-4005U Processor,1.70 ,15 W +Intel,Haswell,Intel(R) Core(TM) i3-4100U Processor,1.80 ,15 W +Intel,Haswell,Intel(R) Core(TM) i7-4550U Processor,1.50 ,15 W +Intel,Haswell,Intel(R) Core(TM) i7-4700HQ Processor,2.40 ,47 W +Intel,Haswell,Intel(R) Core(TM) i7-4700MQ Processor,2.40 ,47 W +Intel,Haswell,Intel(R) Core(TM) i7-4702HQ Processor,2.20 ,37 W +Intel,Haswell,Intel(R) Core(TM) i7-4702MQ Processor,2.20 ,37 W +Intel,Haswell,Intel(R) Core(TM) i7-4800MQ Processor,2.70 ,47 W +Intel,Haswell,Intel(R) Core(TM) i7-4900MQ Processor,2.80 ,47 W +Intel,Haswell,Intel(R) Core(TM) i7-4930MX Processor Extreme Edition,3.00 ,57 W +Intel,Haswell,Intel(R) Core(TM) i5-4200U Processor,1.60 ,15 W +Intel,Haswell,Intel(R) Core(TM) i7-4500U Processor,1.80 ,15 W +Intel,Haswell,Intel(R) Celeron(R) Processor 2955U,1.40 ,15 W +Intel,Haswell,Intel(R) Core(TM) i5-4200Y Processor,1.40 ,11.5 W +Intel,Haswell,Intel(R) Core(TM) i3-4010Y Processor,1.30 ,11.5 W +Intel,Haswell,Intel(R) Core(TM) i3-4158U Processor,2.00 ,28 W +Intel,Haswell,Intel(R) Core(TM) i5-4258U Processor,2.40 ,28 W +Intel,Haswell,Intel(R) Core(TM) i5-4288U Processor,2.60 ,28 W +Intel,Haswell,Intel(R) Core(TM) i7-4558U Processor,2.80 ,28 W +Intel,Haswell,Intel(R) Core(TM) i3-4100M Processor,2.50 ,37 W +Intel,Haswell,Intel(R) Core(TM) i5-4300M Processor,2.60 ,37 W +Intel,Haswell,Intel(R) Core(TM) i5-4200M Processor,2.50 ,37 W +Intel,Haswell,Intel(R) Core(TM) i7-4600M Processor,2.90 ,37 W +Intel,Haswell,Intel(R) Core(TM) i3-4012Y Processor,1.50 ,11.5 W +Intel,Haswell,Intel(R) Core(TM) i3-4020Y Processor,1.50 ,11.5 W +Intel,Haswell,Intel(R) Core(TM) i5-4202Y Processor,1.60 ,11.5 W +Intel,Haswell,Intel(R) Core(TM) i5-4210Y Processor,1.50 ,11.5 W +Intel,Haswell,Intel(R) Core(TM) i5-4300Y Processor,1.60 ,11.5 W +Intel,Haswell,Intel(R) Core(TM) i5-4302Y Processor,1.60 ,11.5 W +Intel,Haswell,Intel(R) Core(TM) i7-4600U Processor,2.10,15 W +Intel,Haswell,Intel(R) Core(TM) i7-4610Y Processor,1.70 ,11.5 W +Intel,Haswell,Intel(R) Pentium(R) Processor 3556U,1.70 ,15 W +Intel,Haswell,Intel(R) Pentium(R) Processor 3560Y,1.20 ,11.5 W +Intel,Haswell,Intel(R) Core(TM) i5-4330M Processor,2.80 ,37 W +Intel,Haswell,Intel(R) Celeron(R) Processor 2950M,2.00 ,37 W +Intel,Haswell,Intel(R) Pentium(R) Processor 3550M,2.30 ,37 W +Intel,Haswell,Intel(R) Core(TM) i3-4110M Processor,2.60 ,37 W +Intel,Haswell,Intel(R) Core(TM) i5-4210H Processor,2.90 ,47 W +Intel,Haswell,Intel(R) Core(TM) i7-4710HQ Processor,2.50 ,47 W +Intel,Haswell,Intel(R) Core(TM) i7-4710MQ Processor,2.50 ,47 W +Intel,Haswell,Intel(R) Core(TM) i7-4712HQ Processor,2.30 ,37 W +Intel,Haswell,Intel(R) Core(TM) i7-4712MQ Processor,2.30 ,37 W +Intel,Haswell,Intel(R) Core(TM) i7-4720HQ Processor,2.60 ,47 W +Intel,Haswell,Intel(R) Core(TM) i7-4722HQ Processor,2.40 ,37 W +Intel,Haswell,Intel(R) Core(TM) i7-4810MQ Processor,2.80 ,47 W +Intel,Haswell,Intel(R) Core(TM) i7-4910MQ Processor,2.90 ,47 W +Intel,Haswell,Intel(R) Core(TM) i7-4940MX Processor Extreme Edition,3.10,57 W +Intel,Haswell,Intel(R) Celeron(R) Processor 2957U,1.40 ,15 W +Intel,Haswell,Intel(R) Celeron(R) Processor 2961Y,1.10,11.5 W +Intel,Haswell,Intel(R) Celeron(R) Processor 2981U,1.60 ,15 W +Intel,Haswell,Intel(R) Pentium(R) Processor 3558U,1.70 ,15 W +Intel,Haswell,Intel(R) Pentium(R) Processor 3561Y,1.20 ,11.5 W +Intel,Haswell,Intel(R) Core(TM) i5-4310U Processor,2.00 ,15 W +Intel,Haswell,Intel(R) Core(TM) i5-4340M Processor,2.90 ,37 W +Intel,Haswell,Intel(R) Core(TM) i7-4610M Processor,3.00 ,37 W +Intel,Haswell,Intel(R) Core(TM) i5-4310M Processor,2.70 ,37 W +Intel,Haswell,Intel(R) Core(TM) i5-4210M Processor,2.60 ,37 W +Intel,Haswell,Intel(R) Pentium(R) Processor 3560M,2.40 ,37 W +Intel,Haswell,Intel(R) Celeron(R) Processor 2970M,2.20 ,37 W +Intel,Haswell,Intel(R) Core(TM) i7-4510U Processor,2.00 ,15 W +Intel,Haswell,Intel(R) Core(TM) i5-4210U Processor,1.70 ,15 W +Intel,Haswell,Intel(R) Core(TM) i3-4120U Processor,2.00 ,15 W +Intel,Haswell,Intel(R) Core(TM) i3-4030U Processor,1.90 ,15 W +Intel,Haswell,Intel(R) Core(TM) i3-4025U Processor,1.90 ,15 W +Intel,Haswell,Intel(R) Core(TM) i5-4220Y Processor,1.60 ,11.5 W +Intel,Haswell,Intel(R) Core(TM) i3-4030Y Processor,1.60 ,11.5 W +Intel,Haswell,Intel(R) Core(TM) i7-4578U Processor,3.00 ,28 W +Intel,Haswell,Intel(R) Core(TM) i5-4308U Processor,2.80 ,28 W +Intel,Haswell,Intel(R) Core(TM) i5-4278U Processor,2.60 ,28 W +Intel,Haswell,Intel(R) Xeon(R) Processor E3-1220 v3,3.10,80 W +Intel,Haswell,Intel(R) Xeon(R) Processor E3-1230 v3,3.30 ,80 W +Intel,Haswell,Intel(R) Xeon(R) Processor E3-1240 v3,3.40 ,80 W +Intel,Haswell,Intel(R) Xeon(R) Processor E3-1270 v3,3.50 ,80 W +Intel,Haswell,Intel(R) Xeon(R) Processor E3-1245 v3,3.40 ,84 W +Intel,Haswell,Intel(R) Xeon(R) Processor E3-1271 v3,3.60 ,80 W +Intel,Haswell,Intel(R) Xeon(R) Processor E3-1241 v3,3.50 ,80 W +Intel,Haswell,Intel(R) Xeon(R) Processor E3-1231 v3,3.40 ,80 W +Intel,Haswell,Intel(R) Xeon(R) Processor E3-1276 v3,3.60 ,84 W +Intel,Haswell,Intel(R) Xeon(R) Processor E3-1246 v3,3.50 ,84 W +Intel,Haswell,Intel(R) Xeon(R) Processor E3-1226 v3,3.30 ,84 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-2695 v3,2.30 ,120 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-2697 v3,2.60 ,145 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-2650 v3,2.30 ,105 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-2660 v3,2.60 ,105 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-2670 v3,2.30 ,120 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-2690 v3,2.60 ,135 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-2687W v3,3.10,160 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-1620 v3,3.50 ,140 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-1650 v3,3.50 ,140 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-1660 v3,3.00 ,140 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-2603 v3,1.60 ,85 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-2630 v3,2.40 ,85 W +Intel,Haswell,Intel(R) Xeon(R) Processor E3-1220L v3,1.10,13 W +Intel,Haswell,Intel(R) Xeon(R) Processor E3-1230L v3,1.80 ,25 W +Intel,Haswell,Intel(R) Xeon(R) Processor E3-1280 v3,3.60 ,82 W +Intel,Haswell,Intel(R) Xeon(R) Processor E3-1265L v3,2.50 ,45 W +Intel,Haswell,Intel(R) Xeon(R) Processor E3-1285 v3,3.60 ,84 W +Intel,Haswell,Intel(R) Xeon(R) Processor E3-1285L v3,3.10,65 W +Intel,Haswell,Intel(R) Xeon(R) Processor E3-1275L v3,2.70 ,45 W +Intel,Haswell,Intel(R) Xeon(R) Processor E3-1281 v3,3.70 ,82 W +Intel,Haswell,Intel(R) Xeon(R) Processor E3-1240L v3,2.00 ,25 W +Intel,Haswell,Intel(R) Xeon(R) Processor E3-1286 v3,3.70 ,84 W +Intel,Haswell,Intel(R) Xeon(R) Processor E3-1286L v3,3.20 ,65 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-2683 v3,2.00 ,120 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-2698 v3,2.30 ,135 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-2699 v3,2.30 ,145 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-2643 v3,3.40 ,135 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-2650L v3,1.80 ,65 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-1630 v3,3.70 ,140 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-1680 v3,3.20 ,140 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-2623 v3,3.00 ,105 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-2630L v3,1.80 ,55 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-2637 v3,3.50 ,135 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-2667 v3,3.20 ,135 W +Intel,Haswell,Intel(R) Xeon(R) Processor E7-4809 v3,2.00 ,115 W +Intel,Haswell,Intel(R) Xeon(R) Processor E7-4820 v3,1.90 ,115 W +Intel,Haswell,Intel(R) Xeon(R) Processor E7-4830 v3,2.10,115 W +Intel,Haswell,Intel(R) Xeon(R) Processor E7-4850 v3,2.20 ,115 W +Intel,Haswell,Intel(R) Xeon(R) Processor E7-8860 v3,2.20 ,140 W +Intel,Haswell,Intel(R) Xeon(R) Processor E7-8867 v3,2.50 ,165 W +Intel,Haswell,Intel(R) Xeon(R) Processor E7-8870 v3,2.10,140 W +Intel,Haswell,Intel(R) Xeon(R) Processor E7-8880 v3,2.30 ,150 W +Intel,Haswell,Intel(R) Xeon(R) Processor E7-8880L v3,2.00 ,115 W +Intel,Haswell,Intel(R) Xeon(R) Processor E7-8890 v3,2.50 ,165 W +Intel,Haswell,Intel(R) Xeon(R) Processor E7-8891 v3,2.80 ,165 W +Intel,Haswell,Intel(R) Xeon(R) Processor E7-8893 v3,3.20 ,140 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-4610 v3,1.70 ,105 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-4620 v3,2.00 ,105 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-4627 v3,2.60 ,135 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-4640 v3,1.90 ,105 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-4650 v3,2.10,105 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-4655 v3,2.90 ,135 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-4660 v3,2.10,120 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-4667 v3,2.00 ,135 W +Intel,Haswell,Intel(R) Xeon(R) Processor E5-4669 v3,2.10,135 W +Intel,Skylake,Intel(R) Core(TM) i7-7800X X-series Processor,3.50 ,140 W +Intel,Skylake,Intel(R) Core(TM) i9-7900X X-series Processor,3.30 ,140 W +Intel,Skylake,Intel(R) Core(TM) i7-7820X X-series Processor,3.60 ,140 W +Intel,Skylake,Intel(R) Core(TM) i9-7920X X-series Processor,2.90 ,140 W +Intel,Skylake,Intel(R) Core(TM) i9-7940X X-series Processor,3.10,165 W +Intel,Skylake,Intel(R) Core(TM) i9-7960X X-series Processor,2.80 ,165 W +Intel,Skylake,Intel(R) Core(TM) i9-7980XE Extreme Edition Processor,2.60 ,165 W +Intel,Skylake,Intel(R) Core(TM) i9-9820X X-series Processor,3.30 ,165 W +Intel,Skylake,Intel(R) Core(TM) i7-9800X X-series Processor,3.80 ,165 W +Intel,Skylake,Intel(R) Core(TM) i9-9960X X-series Processor,3.10,165 W +Intel,Skylake,Intel(R) Core(TM) i9-9900X X-series Processor,3.50 ,165 W +Intel,Skylake,Intel(R) Core(TM) i9-9940X X-series Processor,3.30 ,165 W +Intel,Skylake,Intel(R) Core(TM) i9-9980XE Extreme Edition Processor,3.00 ,165 W +Intel,Skylake,Intel(R) Core(TM) i9-9920X X-series Processor,3.50 ,165 W +Intel,Skylake,Intel(R) Xeon(R) W-3175X Processor,3.10,255 W +Intel,Skylake,Intel(R) Pentium(R) Processor G4400,3.30 ,54 W +Intel,Skylake,Intel(R) Core(TM) i5-6500T Processor,2.50 ,35 W +Intel,Skylake,Intel(R) Core(TM) i5-6500 Processor,3.20 ,65 W +Intel,Skylake,Intel(R) Core(TM) i5-6400 Processor,2.70 ,65 W +Intel,Skylake,Intel(R) Core(TM) i5-6400T Processor,2.20 ,35 W +Intel,Skylake,Intel(R) Core(TM) i5-6600 Processor,3.30 ,65 W +Intel,Skylake,Intel(R) Core(TM) i5-6600T Processor,2.70 ,35 W +Intel,Skylake,Intel(R) Core(TM) i5-6600K Processor,3.50 ,91 W +Intel,Skylake,Intel(R) Core(TM) i7-6700K Processor,4.00 ,91 W +Intel,Skylake,Intel(R) Core(TM) i7-6700 Processor,3.40 ,65 W +Intel,Skylake,Intel(R) Core(TM) i7-6700T Processor,2.80 ,35 W +Intel,Skylake,Intel(R) Q170 Chipset,, +Intel,Skylake,Intel(R) Q150 Chipset,, +Intel,Skylake,Intel(R) H110 Chipset,, +Intel,Skylake,Intel(R) Z170 Chipset,, +Intel,Skylake,Intel(R) B150 Chipset,, +Intel,Skylake,Intel(R) H170 Chipset,, +Intel,Skylake,Intel(R) Pentium(R) Processor G4400T,2.90 ,35 W +Intel,Skylake,Intel(R) Pentium(R) Processor G4500T,3.00 ,35 W +Intel,Skylake,Intel(R) Core(TM) i3-6300T Processor,3.30 ,35 W +Intel,Skylake,Intel(R) Core(TM) i3-6100 Processor,3.70 ,51 W +Intel,Skylake,Intel(R) Pentium(R) Processor G4500,3.50 ,51 W +Intel,Skylake,Intel(R) Core(TM) i3-6300 Processor,3.80 ,51 W +Intel,Skylake,Intel(R) Pentium(R) Processor G4520,3.60 ,51 W +Intel,Skylake,Intel(R) Core(TM) i3-6320 Processor,3.90 ,51 W +Intel,Skylake,Intel(R) Core(TM) i3-6100T Processor,3.20 ,35 W +Intel,Skylake,Intel(R) Celeron(R) Processor G3920,2.90 ,51 W +Intel,Skylake,Intel(R) Celeron(R) Processor G3900T,2.60 ,35 W +Intel,Skylake,Intel(R) Celeron(R) Processor G3900,2.80 ,51 W +Intel,Skylake,Intel(R) Core(TM) i5-6402P Processor,2.80 ,65 W +Intel,Skylake,Intel(R) Core(TM) i5-6585R Processor,2.80 ,65 W +Intel,Skylake,Intel(R) Core(TM) i5-6685R Processor,3.20 ,65 W +Intel,Skylake,Intel(R) Core(TM) i7-6785R Processor,3.30 ,65 W +Intel,Skylake,Intel(R) Core(TM) i3-6098P Processor,3.60 ,54 W +Intel,Skylake,Intel(R) Xeon(R) Gold 5118 Processor,2.3,105 W +Intel,Skylake,Intel(R) Xeon(R) Gold 6138 Processor,2.00 ,125 W +Intel,Skylake,Intel(R) Xeon(R) Gold 5120T Processor,2.20 ,105 W +Intel,Skylake,Intel(R) Xeon(R) Silver 4116 Processor,2.10,85 W +Intel,Skylake,Intel(R) Xeon(R) Gold 6126 Processor,2.60 ,125 W +Intel,Skylake,Intel(R) Xeon(R) Gold 6130 Processor,2.10,125 W +Intel,Skylake,Intel(R) Xeon(R) Bronze 3106 Processor,1.70 ,85 W +Intel,Skylake,Intel(R) Xeon(R) Gold 6138T Processor,2.00 ,125 W +Intel,Skylake,Intel(R) Xeon(R) Platinum 8160T Processor,2.10,150 W +Intel,Skylake,Intel(R) Xeon(R) Gold 6130T Processor,2.10,125 W +Intel,Skylake,Intel(R) Xeon(R) Silver 4110 Processor,2.10,85 W +Intel,Skylake,Intel(R) Xeon(R) Gold 6126T Processor,2.60 ,125 W +Intel,Skylake,Intel(R) Xeon(R) Silver 4109T Processor,2.00 ,70 W +Intel,Skylake,Intel(R) Xeon(R) Silver 4114T Processor,2.20 ,85 W +Intel,Skylake,Intel(R) Xeon(R) Gold 5119T Processor,1.90 ,85 W +Intel,Skylake,Intel(R) Xeon(R) Silver 4116T Processor,2.10,85 W +Intel,Skylake,Intel(R) Xeon(R) D-2123IT Processor,2.20 ,60 W +Intel,Skylake,Intel(R) Xeon(R) D-2143IT Processor,2.20 ,65 W +Intel,Skylake,Intel(R) Xeon(R) D-2142IT Processor,1.90 ,65 W +Intel,Skylake,Intel(R) Xeon(R) D-2145NT Processor,1.90 ,65 W +Intel,Skylake,Intel(R) Xeon(R) D-2146NT Processor,2.30 ,80 W +Intel,Skylake,Intel(R) Xeon(R) D-2177NT Processor,1.90 ,105 W +Intel,Skylake,Intel(R) Xeon(R) D-2163IT Processor,2.10,75 W +Intel,Skylake,Intel(R) Xeon(R) D-2173IT Processor,1.70 ,70 W +Intel,Skylake,Intel(R) Xeon(R) D-2166NT Processor,2.00 ,85 W +Intel,Skylake,Intel(R) Xeon(R) D-2187NT Processor,2.00 ,110 W +Intel,Skylake,Intel(R) Xeon(R) D-2183IT Processor,2.20 ,100 W +Intel,Skylake,Intel(R) Xeon(R) Processor E3-1225 v5,3.30 ,80 W +Intel,Skylake,Intel(R) Xeon(R) Processor E3-1275 v5,3.60 ,80 W +Intel,Skylake,Intel(R) Xeon(R) Processor E3-1268L v5,2.40 ,35 W +Intel,Skylake,Intel(R) Core(TM) i3-6100U Processor,2.30 ,15 W +Intel,Skylake,Intel(R) Core(TM) i3-6100TE Processor,2.70 ,35 W +Intel,Skylake,Intel(R) Core(TM) i5-6500TE Processor,2.30 ,35 W +Intel,Skylake,Intel(R) Core(TM) i5-6300U Processor,2.40 ,15 W +Intel,Skylake,Intel(R) Core(TM) i7-6600U Processor,2.60 ,15 W +Intel,Skylake,Intel(R) Core(TM) i7-6700TE Processor,2.40 ,35 W +Intel,Skylake,Intel(R) Xeon(R) Processor E3-1505M v5,2.80 ,45 W +Intel,Skylake,Intel(R) Core(TM) i5-6440EQ Processor,2.70 ,45 W +Intel,Skylake,Intel(R) Core(TM) i7-6820EQ Processor,2.80 ,45 W +Intel,Skylake,Mobile Intel(R) QM170 Chipset,, +Intel,Skylake,Mobile Intel(R) HM170 Chipset,, +Intel,Skylake,Mobile Intel(R) CM236 Chipset,, +Intel,Skylake,Intel(R) C236 Chipset,, +Intel,Skylake,Intel(R) Pentium(R) Processor G4400TE,2.40 ,35 W +Intel,Skylake,Intel(R) Core(TM) i3-6100E Processor,2.70 ,35 W +Intel,Skylake,Intel(R) Core(TM) i3-6102E Processor,1.90 ,25 W +Intel,Skylake,Intel(R) Core(TM) i7-6822EQ Processor,2.00 ,25 W +Intel,Skylake,Intel(R) Core(TM) i5-6442EQ Processor,1.90 ,25 W +Intel,Skylake,Intel(R) Xeon(R) Processor E3-1505L v5,2.00 ,25 W +Intel,Skylake,Intel(R) Celeron(R) Processor G3902E,1.60 ,25 W +Intel,Skylake,Intel(R) Celeron(R) Processor G3900TE,2.30 ,35 W +Intel,Skylake,Intel(R) Celeron(R) Processor G3900E,2.40 ,35 W +Intel,Skylake,Intel(R) Celeron(R) Processor 3955U,2.00 ,15 W +Intel,Skylake,Intel(R) Xeon(R) Processor E3-1515M v5,2.80 ,45 W +Intel,Skylake,Intel(R) Xeon(R) Processor E3-1558L v5,1.90 ,45 W +Intel,Skylake,Intel(R) Xeon(R) Processor E3-1578L v5,2.00 ,45 W +Intel,Skylake,Mobile Intel(R) QM175 Chipset,, +Intel,Skylake,Mobile Intel(R) HM175 Chipset,, +Intel,Skylake,Mobile Intel(R) CM238 Chipset,, +Intel,Skylake,Intel(R) Core(TM) i5-6200U Processor,2.30 ,15 W +Intel,Skylake,Intel(R) Core(TM) i7-6500U Processor,2.50 ,15 W +Intel,Skylake,Intel(R) Core(TM) m5-6Y57 Processor,1.10,4.5 W +Intel,Skylake,Intel(R) Core(TM) m3-6Y30 Processor,0.9,4.5 W +Intel,Skylake,Intel(R) Core(TM) m7-6Y75 Processor,1.20 ,4.5 W +Intel,Skylake,Intel(R) Core(TM) m5-6Y54 Processor,1.10,4.5 W +Intel,Skylake,Intel(R) Core(TM) i5-6300HQ Processor,2.30 ,45 W +Intel,Skylake,Intel(R) Core(TM) i5-6440HQ Processor,2.60 ,45 W +Intel,Skylake,Intel(R) Core(TM) i7-6700HQ Processor,2.60 ,45 W +Intel,Skylake,Intel(R) Core(TM) i7-6820HK Processor,2.70 ,45 W +Intel,Skylake,Intel(R) Core(TM) i7-6820HQ Processor,2.70 ,45 W +Intel,Skylake,Intel(R) Core(TM) i7-6920HQ Processor,2.90 ,45 W +Intel,Skylake,Intel(R) Core(TM) i3-6100H Processor,2.70 ,35 W +Intel,Skylake,Intel(R) Xeon(R) Processor E3-1535M v5,2.90 ,45 W +Intel,Skylake,Intel(R) Pentium(R) Processor 4405U,2.10,15 W +Intel,Skylake,Intel(R) Pentium(R) Processor 4405Y,1.50 ,6 W +Intel,Skylake,Intel(R) Core(TM) i3-6167U Processor,2.70 ,28 W +Intel,Skylake,Intel(R) Core(TM) i5-6360U Processor,2.00 ,15 W +Intel,Skylake,Intel(R) Core(TM) i3-6006U Processor,2.00 ,15 W +Intel,Skylake,Intel(R) Core(TM) i5-6260U Processor,1.80 ,15 W +Intel,Skylake,Intel(R) Core(TM) i7-6560U Processor,2.20 ,15 W +Intel,Skylake,Intel(R) Core(TM) i5-6287U Processor,3.10,28 W +Intel,Skylake,Intel(R) Core(TM) i5-6267U Processor,2.90 ,28 W +Intel,Skylake,Intel(R) Core(TM) i7-6567U Processor,3.30 ,28 W +Intel,Skylake,Intel(R) Core(TM) i7-6660U Processor,2.40 ,15 W +Intel,Skylake,Intel(R) Core(TM) i7-6650U Processor,2.20 ,15 W +Intel,Skylake,Intel(R) Celeron(R) Processor 3855U,1.60 ,15 W +Intel,Skylake,Intel(R) Core(TM) i5-6350HQ Processor,2.30 ,45 W +Intel,Skylake,Intel(R) Core(TM) i7-6970HQ Processor,2.80 ,45 W +Intel,Skylake,Intel(R) Core(TM) i7-6870HQ Processor,2.70 ,45 W +Intel,Skylake,Intel(R) Core(TM) i7-6770HQ Processor,2.60 ,45 W +Intel,Skylake,Intel(R) Xeon(R) Processor E3-1575M v5,3.00 ,45 W +Intel,Skylake,Intel(R) Xeon(R) Processor E3-1545M v5,2.90 ,45 W +Intel,Skylake,Intel(R) Core(TM) i3-6157U Processor,2.40 ,28 W +Intel,Skylake,Intel(R) Xeon(R) Gold 5118 Processor,2.3,105 W +Intel,Skylake,Intel(R) Xeon(R) Gold 5120 Processor,2.20 ,105 W +Intel,Skylake,Intel(R) Xeon(R) Gold 5122 Processor,3.60 ,105 W +Intel,Skylake,Intel(R) Xeon(R) Gold 6138 Processor,2.00 ,125 W +Intel,Skylake,Intel(R) Xeon(R) Gold 5120T Processor,2.20 ,105 W +Intel,Skylake,Intel(R) Xeon(R) Gold 6136 Processor,3.00 ,150 W +Intel,Skylake,Intel(R) Xeon(R) Silver 4116 Processor,2.10,85 W +Intel,Skylake,Intel(R) Xeon(R) Gold 6128 Processor,3.40 ,115 W +Intel,Skylake,Intel(R) Xeon(R) Gold 6126 Processor,2.60 ,125 W +Intel,Skylake,Intel(R) Xeon(R) Gold 5115 Processor,2.40 ,85 W +Intel,Skylake,Intel(R) Xeon(R) Gold 6140 Processor,2.30 ,140 W +Intel,Skylake,Intel(R) Xeon(R) Gold 6142 Processor,2.60 ,150 W +Intel,Skylake,Intel(R) Xeon(R) Gold 6148 Processor,2.40 ,150 W +Intel,Skylake,Intel(R) Xeon(R) Gold 6150 Processor,2.70 ,165 W +Intel,Skylake,Intel(R) Xeon(R) Gold 6152 Processor,2.10,140 W +Intel,Skylake,Intel(R) Xeon(R) Gold 6130 Processor,2.10,125 W +Intel,Skylake,Intel(R) Xeon(R) Gold 6134 Processor,3.20 ,130 W +Intel,Skylake,Intel(R) Xeon(R) Gold 6154 Processor,3.00 ,200 W +Intel,Skylake,Intel(R) Xeon(R) Platinum 8180 Processor,2.50 ,205 W +Intel,Skylake,Intel(R) Xeon(R) Platinum 8153 Processor,2.00 ,125 W +Intel,Skylake,Intel(R) Xeon(R) Platinum 8156 Processor,3.60 ,105 W +Intel,Skylake,Intel(R) Xeon(R) Platinum 8158 Processor,3.00 ,150 W +Intel,Skylake,Intel(R) Xeon(R) Platinum 8160 Processor,2.10,150 W +Intel,Skylake,Intel(R) Xeon(R) Platinum 8164 Processor,2.00 ,150 W +Intel,Skylake,Intel(R) Xeon(R) Platinum 8168 Processor,2.70 ,205 W +Intel,Skylake,Intel(R) Xeon(R) Platinum 8170 Processor,2.10,165 W +Intel,Skylake,Intel(R) Xeon(R) Platinum 8176 Processor,2.10,165 W +Intel,Skylake,Intel(R) Xeon(R) Bronze 3106 Processor,1.70 ,85 W +Intel,Skylake,Intel(R) Xeon(R) Gold 6132 Processor,2.60 ,140 W +Intel,Skylake,Intel(R) Xeon(R) Gold 6138T Processor,2.00 ,125 W +Intel,Skylake,Intel(R) Xeon(R) Platinum 8160T Processor,2.10,150 W +Intel,Skylake,Intel(R) Xeon(R) Silver 4108 Processor,1.80 ,85 W +Intel,Skylake,Intel(R) Xeon(R) Gold 6130T Processor,2.10,125 W +Intel,Skylake,Intel(R) Xeon(R) Bronze 3104 Processor,1.70 ,85 W +Intel,Skylake,Intel(R) Xeon(R) Silver 4110 Processor,2.10,85 W +Intel,Skylake,Intel(R) Xeon(R) Gold 6126T Processor,2.60 ,125 W +Intel,Skylake,Intel(R) Xeon(R) Silver 4109T Processor,2.00 ,70 W +Intel,Skylake,Intel(R) Xeon(R) Silver 4114 Processor,2.20 ,85 W +Intel,Skylake,Intel(R) Xeon(R) Silver 4112 Processor,2.60 ,85 W +Intel,Skylake,Intel(R) Xeon(R) Gold 6142F Processor,2.60 ,160 W +Intel,Skylake,Intel(R) Xeon(R) Gold 6138F Processor,2.00 ,135 W +Intel,Skylake,Intel(R) Xeon(R) Platinum 8160F Processor,2.10,160 W +Intel,Skylake,Intel(R) Xeon(R) Gold 6130F Processor,2.10,135 W +Intel,Skylake,Intel(R) Xeon(R) Gold 6126F Processor,2.60 ,135 W +Intel,Skylake,Intel(R) Xeon(R) Gold 6148F Processor,2.40 ,160 W +Intel,Skylake,Intel(R) Xeon(R) Gold 6146 Processor,3.20 ,165 W +Intel,Skylake,Intel(R) Xeon(R) Gold 6144 Processor,3.50 ,150 W +Intel,Skylake,Intel(R) Xeon(R) W-2123 Processor,3.60 ,120 W +Intel,Skylake,Intel(R) Xeon(R) W-2133 Processor,3.60 ,140 W +Intel,Skylake,Intel(R) Xeon(R) W-2155 Processor,3.30 ,140 W +Intel,Skylake,Intel(R) Xeon(R) Platinum 8176F Processor,2.10,173 W +Intel,Skylake,Intel(R) Xeon(R) Silver 4114T Processor,2.20 ,85 W +Intel,Skylake,Intel(R) Xeon(R) Gold 5119T Processor,1.90 ,85 W +Intel,Skylake,Intel(R) Xeon(R) Silver 4116T Processor,2.10,85 W +Intel,Skylake,Intel(R) Xeon(R) W-2145 Processor,3.70 ,140 W +Intel,Skylake,Intel(R) Xeon(R) W-2125 Processor,4.00 ,120 W +Intel,Skylake,Intel(R) Xeon(R) W-2135 Processor,3.70 ,140 W +Intel,Skylake,Intel(R) Xeon(R) W-2195 Processor,2.30 ,140 W +Intel,Skylake,Intel(R) Xeon(R) W-2175 Processor,2.50 ,140 W +Intel,Skylake,Intel(R) Xeon(R) D-2123IT Processor,2.20 ,60 W +Intel,Skylake,Intel(R) Xeon(R) D-2141I Processor,2.20 ,65 W +Intel,Skylake,Intel(R) Xeon(R) D-2143IT Processor,2.20 ,65 W +Intel,Skylake,Intel(R) Xeon(R) D-2142IT Processor,1.90 ,65 W +Intel,Skylake,Intel(R) Xeon(R) D-2145NT Processor,1.90 ,65 W +Intel,Skylake,Intel(R) Xeon(R) D-2146NT Processor,2.30 ,80 W +Intel,Skylake,Intel(R) Xeon(R) D-2161I Processor,2.20 ,90 W +Intel,Skylake,Intel(R) Xeon(R) D-2177NT Processor,1.90 ,105 W +Intel,Skylake,Intel(R) Xeon(R) D-2163IT Processor,2.10,75 W +Intel,Skylake,Intel(R) Xeon(R) D-2173IT Processor,1.70 ,70 W +Intel,Skylake,Intel(R) Xeon(R) D-2166NT Processor,2.00 ,85 W +Intel,Skylake,Intel(R) Xeon(R) D-2187NT Processor,2.00 ,110 W +Intel,Skylake,Intel(R) Xeon(R) D-2183IT Processor,2.20 ,100 W +Intel,Skylake,Intel(R) Xeon(R) Gold 6138P Processor,2.00 ,195 W +Intel,Skylake,Intel(R) Xeon(R) Processor E3-1225 v5,3.30 ,80 W +Intel,Skylake,Intel(R) Xeon(R) Processor E3-1240L v5,2.10,25 W +Intel,Skylake,Intel(R) Xeon(R) Processor E3-1235L v5,2.00 ,25 W +Intel,Skylake,Intel(R) Xeon(R) Processor E3-1280 v5,3.70 ,80 W +Intel,Skylake,Intel(R) Xeon(R) Processor E3-1220 v5,3.00 ,80 W +Intel,Skylake,Intel(R) Xeon(R) Processor E3-1245 v5,3.50 ,80 W +Intel,Skylake,Intel(R) Xeon(R) Processor E3-1270 v5,3.60 ,80 W +Intel,Skylake,Intel(R) Xeon(R) Processor E3-1260L v5,2.90 ,45 W +Intel,Skylake,Intel(R) Xeon(R) Processor E3-1240 v5,3.50 ,80 W +Intel,Skylake,Intel(R) Xeon(R) Processor E3-1275 v5,3.60 ,80 W +Intel,Skylake,Intel(R) Xeon(R) Processor E3-1230 v5,3.40 ,80 W +Intel,Skylake,Intel(R) C232 Chipset,, +Intel,Skylake,Intel(R) C236 Chipset,, +Intel,Skylake,Intel(R) Xeon(R) Processor E3-1565L v5,2.50 ,35 W +Intel,Skylake,Intel(R) Xeon(R) Processor E3-1585L v5,3.00 ,45 W +Intel,Skylake,Intel(R) Xeon(R) Processor E3-1585 v5,3.50 ,65 W +Intel,Kaby Lake,Intel(R) Core(TM) i7-7740X X-series Processor,4.30 ,112 W +Intel,Kaby Lake,Intel(R) Core(TM) i5-7640X X-series Processor,4.00 ,112 W +Intel,Kaby Lake,Intel(R) Core(TM) i3-7020U Processor,2.30 ,15 W +Intel,Kaby Lake,Intel(R) Pentium(R) Gold Processor 4415Y,1.60 ,6 W +Intel,Kaby Lake,Intel(R) Celeron(R) Processor 3965Y,1.50 ,6 W +Intel,Kaby Lake,Intel(R) Xeon(R) Processor E3-1501M v6,2.90 ,45 W +Intel,Kaby Lake,Intel(R) Xeon(R) Processor E3-1501L v6,2.10,25 W +Intel,Kaby Lake,Intel(R) Celeron(R) Processor G3930E,2.90 ,54 W +Intel,Kaby Lake,Intel(R) Celeron(R) Processor G3930TE,2.70 ,35 W +Intel,Kaby Lake,Intel(R) X299 Chipset,, +Intel,Kaby Lake,Intel(R) Core(TM) i3-7130U Processor,2.70 ,15 W +Intel,Kaby Lake,Intel(R) C422 Chipset,, +Intel,Kaby Lake,Intel(R) Core(TM) i7-7Y75 Processor,1.30 ,4.5 W +Intel,Kaby Lake,Intel(R) Core(TM) i3-7100U Processor,2.40 ,15 W +Intel,Kaby Lake,Intel(R) Core(TM) i5-7200U Processor,2.50 ,15 W +Intel,Kaby Lake,Intel(R) Core(TM) m3-7Y30 Processor,1.00 ,4.5 W +Intel,Kaby Lake,Intel(R) Core(TM) i7-7500U Processor,2.70 ,15 W +Intel,Kaby Lake,Intel(R) Core(TM) i5-7Y54 Processor,1.20 ,4.5 W +Intel,Kaby Lake,Intel(R) Celeron(R) Processor 3965U,2.20 ,15 W +Intel,Kaby Lake,Intel(R) Celeron(R) Processor 3865U,1.80 ,15 W +Intel,Kaby Lake,Intel(R) Pentium(R) Gold Processor 4415U,2.30 ,15 W +Intel,Kaby Lake,Intel(R) Core(TM) i5-7500T Processor,2.70 ,35 W +Intel,Kaby Lake,Intel(R) Core(TM) i7-7700T Processor,2.90 ,35 W +Intel,Kaby Lake,Intel(R) Core(TM) i5-7500 Processor,3.40 ,65 W +Intel,Kaby Lake,Intel(R) Core(TM) i3-7102E Processor,2.10,25 W +Intel,Kaby Lake,Intel(R) Core(TM) i3-7101TE Processor,3.40 ,35 W +Intel,Kaby Lake,Intel(R) Core(TM) i3-7100H Processor,3.00 ,35 W +Intel,Kaby Lake,Intel(R) Core(TM) i7-7820EQ Processor,3.00 ,45 W +Intel,Kaby Lake,Intel(R) Core(TM) i7-7700 Processor,3.60 ,65 W +Intel,Kaby Lake,Intel(R) Core(TM) i7-7700K Processor,4.20 ,91 W +Intel,Kaby Lake,Intel(R) Core(TM) i3-7101E Processor,3.90 ,54 W +Intel,Kaby Lake,Intel(R) Core(TM) i3-7100E Processor,2.90 ,35 W +Intel,Kaby Lake,Intel(R) Pentium(R) Gold Processor 4410Y,1.50 ,6 W +Intel,Kaby Lake,Intel(R) Pentium(R) Processor G4560,3.50 ,54 W +Intel,Kaby Lake,Intel(R) Core(TM) i5-7600K Processor,3.80 ,91 W +Intel,Kaby Lake,Intel(R) Core(TM) i5-7400 Processor,3.00 ,65 W +Intel,Kaby Lake,Intel(R) Core(TM) i5-7600 Processor,3.50 ,65 W +Intel,Kaby Lake,Intel(R) Core(TM) i5-7600T Processor,2.80 ,35 W +Intel,Kaby Lake,Intel(R) Core(TM) i5-7400T Processor,2.40 ,35 W +Intel,Kaby Lake,Intel(R) Core(TM) i7-7700HQ Processor,2.80 ,45 W +Intel,Kaby Lake,Intel(R) Celeron(R) Processor G3950,3.00 ,51 W +Intel,Kaby Lake,Intel(R) Celeron(R) Processor G3930,2.90 ,51 W +Intel,Kaby Lake,Intel(R) Pentium(R) Processor G4600,3.60 ,51 W +Intel,Kaby Lake,Intel(R) Core(TM) i3-7100 Processor,3.90 ,51 W +Intel,Kaby Lake,Intel(R) Core(TM) i5-7300HQ Processor,2.50 ,45 W +Intel,Kaby Lake,Intel(R) Core(TM) i3-7300T Processor,3.50 ,35 W +Intel,Kaby Lake,Intel(R) Core(TM) i3-7300 Processor,4.00 ,51 W +Intel,Kaby Lake,Intel(R) Core(TM) i5-7440HQ Processor,2.80 ,45 W +Intel,Kaby Lake,Intel(R) Pentium(R) Processor G4620,3.70 ,51 W +Intel,Kaby Lake,Intel(R) Core(TM) i5-7Y57 Processor,1.20 ,4.5 W +Intel,Kaby Lake,Intel(R) Core(TM) i7-7920HQ Processor,3.10,45 W +Intel,Kaby Lake,Intel(R) Xeon(R) Processor E3-1505M v6,3.00 ,45 W +Intel,Kaby Lake,Intel(R) Core(TM) i7-7820HK Processor,2.90 ,45 W +Intel,Kaby Lake,Intel(R) Pentium(R) Processor G4560T,2.90 ,35 W +Intel,Kaby Lake,Intel(R) Core(TM) i7-7600U Processor,2.80 ,15 W +Intel,Kaby Lake,Intel(R) Celeron(R) Processor G3930T,2.70 ,35 W +Intel,Kaby Lake,Intel(R) Xeon(R) Processor E3-1535M v6,3.10,45 W +Intel,Kaby Lake,Intel(R) Xeon(R) Processor E3-1240 v6,3.70 ,72 W +Intel,Kaby Lake,Intel(R) Xeon(R) Processor E3-1220 v6,3.00 ,72 W +Intel,Kaby Lake,Intel(R) Core(TM) i5-7300U Processor,2.60 ,15 W +Intel,Kaby Lake,Intel(R) Xeon(R) Processor E3-1245 v6,3.70 ,73 W +Intel,Kaby Lake,Intel(R) Xeon(R) Processor E3-1230 v6,3.50 ,72 W +Intel,Kaby Lake,Intel(R) Xeon(R) Processor E3-1280 v6,3.90 ,72 W +Intel,Kaby Lake,Intel(R) Xeon(R) Processor E3-1225 v6,3.30 ,73 W +Intel,Kaby Lake,Intel(R) Xeon(R) Processor E3-1275 v6,3.80 ,73 W +Intel,Kaby Lake,Intel(R) Xeon(R) Processor E3-1270 v6,3.80 ,72 W +Intel,Kaby Lake,Intel(R) Core(TM) i3-7320 Processor,4.10,51 W +Intel,Kaby Lake,Intel(R) Core(TM) i3-7100T Processor,3.40 ,35 W +Intel,Kaby Lake,Intel(R) Pentium(R) Processor G4600T,3.00 ,35 W +Intel,Kaby Lake,Intel(R) Core(TM) i7-7820HQ Processor,2.90 ,45 W +Intel,Kaby Lake,Intel(R) Core(TM) i5-7440EQ Processor,2.90 ,45 W +Intel,Kaby Lake,Intel(R) Core(TM) i3-7350K Processor,4.20 ,60 W +Intel,Kaby Lake,Intel(R) Core(TM) i5-7267U Processor,3.10,28 W +Intel,Kaby Lake,Intel(R) Core(TM) i5-7287U Processor,3.30 ,28 W +Intel,Kaby Lake,Intel(R) Core(TM) i5-7442EQ Processor,2.10,25 W +Intel,Kaby Lake,Intel(R) Core(TM) i5-7360U Processor,2.30 ,15 W +Intel,Kaby Lake,Intel(R) Core(TM) i7-7660U Processor,2.50 ,15 W +Intel,Kaby Lake,Intel(R) Core(TM) m3-7Y32 Processor,1.10,4.5 W +Intel,Kaby Lake,Intel(R) Core(TM) i5-7260U Processor,2.20 ,15 W +Intel,Kaby Lake,Intel(R) Core(TM) i7-7560U Processor,2.40 ,15 W +Intel,Kaby Lake,Intel(R) Core(TM) i7-7567U Processor,3.50 ,28 W +Intel,Kaby Lake,Intel(R) Core(TM) i3-7167U Processor,2.80 ,28 W +Intel,Kaby Lake,Intel(R) Xeon(R) Processor E3-1505L v6,2.20 ,25 W +Intel,Kaby Lake,Intel(R) Xeon(R) Processor E3-1285 v6,4.10,79 W +Intel,Coffee Lake,Intel(R) Core(TM) i7-8700K Processor,3.70 ,95 W +Intel,Coffee Lake,Intel(R) Core(TM) i5-8600K Processor,3.60 ,95 W +Intel,Coffee Lake,Intel(R) Core(TM) i7-8700 Processor,3.20 ,65 W +Intel,Coffee Lake,Intel(R) Core(TM) i5-8400 Processor,2.80 ,65 W +Intel,Coffee Lake,Intel(R) Core(TM) i3-8100 Processor,3.60 ,65 W +Intel,Coffee Lake,Intel(R) Core(TM) i3-8350K Processor,4.00 ,91 W +Intel,Coffee Lake,Intel(R) Celeron(R) G4900 Processor,3.10,54 W +Intel,Coffee Lake,Intel(R) Core(TM) i5-8600 Processor,3.10,65 W +Intel,Coffee Lake,Intel(R) Core(TM) i5-8600T Processor,2.30 ,35 W +Intel,Coffee Lake,Intel(R) Core(TM) i5-8500 Processor,3.00 ,65 W +Intel,Coffee Lake,Intel(R) Core(TM) i5-8400T Processor,1.70 ,35 W +Intel,Coffee Lake,Intel(R) Core(TM) i5-8500T Processor,2.10,35 W +Intel,Coffee Lake,Intel(R) Core(TM) i3-8300 Processor,3.70 ,62 W +Intel,Coffee Lake,Intel(R) Core(TM) i3-8300T Processor,3.20 ,35 W +Intel,Coffee Lake,Intel(R) Core(TM) i3-8100T Processor,3.10,35 W +Intel,Coffee Lake,Intel(R) Pentium(R) Gold G5600 Processor,3.90 ,54 W +Intel,Coffee Lake,Intel(R) Pentium(R) Gold G5500 Processor,3.80 ,54 W +Intel,Coffee Lake,Intel(R) Pentium(R) Gold G5500T Processor,3.20 ,35 W +Intel,Coffee Lake,Intel(R) Core(TM) i7-8700T Processor,2.40 ,35 W +Intel,Coffee Lake,Intel(R) Pentium(R) Gold G5400T Processor,3.10,35 W +Intel,Coffee Lake,Intel(R) Celeron(R) G4920 Processor,3.20 ,54 W +Intel,Coffee Lake,Intel(R) Pentium(R) Gold G5400 Processor,3.70 ,58 W +Intel,Coffee Lake,Intel(R) Celeron(R) G4900T Processor,2.90 ,35 W +Intel,Coffee Lake,Intel(R) Celeron(R) Processor G4930T,3.00 ,35 W +Intel,Coffee Lake,Intel(R) Core(TM) i3-9100 Processor,3.60 ,65 W +Intel,Coffee Lake,Intel(R) Core(TM) i3-9100T Processor,3.10,35 W +Intel,Coffee Lake,Intel(R) Core(TM) i3-9300T Processor,3.20 ,35 W +Intel,Coffee Lake,Intel(R) Celeron(R) Processor G4930,3.20 ,54 W +Intel,Coffee Lake,Intel(R) Celeron(R) Processor G4950,3.30 ,54 W +Intel,Coffee Lake,Intel(R) Core(TM) i3-9300 Processor,3.70 ,62 W +Intel,Coffee Lake,Intel(R) Core(TM) i5-9400T Processor,1.80 ,35 W +Intel,Coffee Lake,Intel(R) Core(TM) i5-9500 Processor,3.00 ,65 W +Intel,Coffee Lake,Intel(R) Core(TM) i5-9600K Processor,3.70 ,95 W +Intel,Coffee Lake,Intel(R) Core(TM) i5-9400 Processor,2.90 ,65 W +Intel,Coffee Lake,Intel(R) Core(TM) i5-9600 Processor,3.10,65 W +Intel,Coffee Lake,Intel(R) Pentium(R) Gold G5600T Processor,3.30 ,35 W +Intel,Coffee Lake,Intel(R) Pentium(R) Gold G5420T Processor,3.20 ,35 W +Intel,Coffee Lake,Intel(R) Pentium(R) Gold G5420 Processor,3.80 ,54 W +Intel,Coffee Lake,Intel(R) Pentium(R) Gold G5620 Processor,4.00 ,54 W +Intel,Coffee Lake,Intel(R) Core(TM) i7+8700 Processor,3.20 ,65 W +Intel,Coffee Lake,Intel(R) Core(TM) i5+8500 Processor ,3.00 ,65 W +Intel,Coffee Lake,Intel(R) Core(TM) i5+8400 Processor ,2.80 ,65 W +Intel,Coffee Lake,Intel(R) Core(TM) i7-8086K Processor,4.00 ,95 W +Intel,Coffee Lake,Intel(R) Core(TM) i7-9700K Processor,3.60 ,95 W +Intel,Coffee Lake,Intel(R) Core(TM) i9-9900K Processor,3.60 ,95 W +Intel,Coffee Lake,Intel(R) Core(TM) i3-9350K Processor,4.00 ,91 W +Intel,Coffee Lake,Intel(R) Core(TM) i5-9400F Processor,2.90 ,65 W +Intel,Coffee Lake,Intel(R) Core(TM) i5-9600KF Processor,3.70 ,95 W +Intel,Coffee Lake,Intel(R) Core(TM) i7-9700KF Processor,3.60 ,95 W +Intel,Coffee Lake,Intel(R) Core(TM) i3-9100F Processor,3.60 ,65 W +Intel,Coffee Lake,Intel(R) Core(TM) i9-9900KF Processor,3.60 ,95 W +Intel,Coffee Lake,Intel(R) Core(TM) i5-9500F Processor,3.00 ,65 W +Intel,Coffee Lake,Intel(R) Core(TM) i9-9900T Processor,2.10,35 W +Intel,Coffee Lake,Intel(R) Core(TM) i7-9700T Processor,2.00 ,35 W +Intel,Coffee Lake,Intel(R) Core(TM) i5-9600T Processor,2.30 ,35 W +Intel,Coffee Lake,Intel(R) Core(TM) i5-9500T Processor,2.20 ,35 W +Intel,Coffee Lake,Intel(R) Core(TM) i3-9350KF Processor,4.00 ,91 W +Intel,Coffee Lake,Intel(R) Core(TM) i9-9900 Processor,3.10,65 W +Intel,Coffee Lake,Intel(R) Core(TM) i7-9700 Processor,3.00 ,65 W +Intel,Coffee Lake,Intel(R) Core(TM) i3-9320 Processor,3.70 ,62 W +Intel,Coffee Lake,Intel(R) Core(TM) i9-9900KS Processor,4.00 ,127 W +Intel,Coffee Lake,Intel(R) Core(TM) i7-9700F Processor,3.00 ,65 W +Intel,Coffee Lake,Intel(R) Core(TM) i7-8700 Processor,3.20 ,65 W +Intel,Coffee Lake,Intel(R) Core(TM) i3-8100 Processor,3.60 ,65 W +Intel,Coffee Lake,Intel(R) Celeron(R) G4900 Processor,3.10,54 W +Intel,Coffee Lake,Intel(R) Core(TM) i5-8500 Processor,3.00 ,65 W +Intel,Coffee Lake,Intel(R) Core(TM) i5-8500T Processor,2.10,35 W +Intel,Coffee Lake,Intel(R) Core(TM) i3-8100T Processor,3.10,35 W +Intel,Coffee Lake,Intel(R) Core(TM) i7-8700T Processor,2.40 ,35 W +Intel,Coffee Lake,Intel(R) Pentium(R) Gold G5400T Processor,3.10,35 W +Intel,Coffee Lake,Intel(R) Pentium(R) Gold G5400 Processor,3.70 ,58 W +Intel,Coffee Lake,Intel(R) Celeron(R) G4900T Processor,2.90 ,35 W +Intel,Coffee Lake,Intel(R) Xeon(R) E-2124G Processor,3.40 ,71 W +Intel,Coffee Lake,Intel(R) Xeon(R) E-2176G Processor,3.70 ,80 W +Intel,Coffee Lake,Intel(R) Xeon(R) E-2176M Processor,2.70 ,45 W +Intel,Coffee Lake,Intel(R) Core(TM) i5-8400H Processor,2.50 ,45 W +Intel,Coffee Lake,Intel(R) Core(TM) i7-8850H Processor,2.60 ,45 W +Intel,Coffee Lake,Intel(R) Core(TM) i3-8100H Processor,3.00 ,45 W +Intel,Coffee Lake,Intel(R) Xeon(R) E-2254ME Processor,2.60 ,45 W +Intel,Coffee Lake,Intel(R) Xeon(R) E-2276ME Processor,2.80 ,45 W +Intel,Coffee Lake,Intel(R) Xeon(R) E-2254ML Processor,1.70 ,25 W +Intel,Coffee Lake,Intel(R) Xeon(R) E-2276ML Processor,2.00 ,25 W +Intel,Coffee Lake,Intel(R) Core(TM) i3-9100E Processor,3.10,65 W +Intel,Coffee Lake,Intel(R) Core(TM) i3-9100HL Processor,1.60 ,25 W +Intel,Coffee Lake,Intel(R) Core(TM) i3-9100TE Processor,2.20 ,35 W +Intel,Coffee Lake,Intel(R) Core(TM) i7-9700E Processor,2.60 ,65 W +Intel,Coffee Lake,Intel(R) Xeon(R) E-2278GEL Processor,2.00 ,35 W +Intel,Coffee Lake,Intel(R) Core(TM) i5-9500E Processor,3.00 ,65 W +Intel,Coffee Lake,Intel(R) Xeon(R) E-2226GE Processor,3.40 ,80 W +Intel,Coffee Lake,Intel(R) Core(TM) i5-9500TE Processor,2.20 ,35 W +Intel,Coffee Lake,Intel(R) Core(TM) i7-9850HE Processor,2.70 ,45 W +Intel,Coffee Lake,Intel(R) Core(TM) i7-9700TE Processor,1.80 ,35 W +Intel,Coffee Lake,Intel(R) Celeron(R) Processor G4932E,1.90 ,25 W +Intel,Coffee Lake,Intel(R) Celeron(R) Processor G4930E,2.40 ,35 W +Intel,Coffee Lake,Intel(R) Core(TM) i7-9850HL Processor,1.90 ,25 W +Intel,Coffee Lake,Intel(R) Xeon(R) E-2278GE Processor,3.30 ,80 W +Intel,Coffee Lake,Intel(R) Xeon(R) E-2186M Processor,2.90 ,45 W +Intel,Coffee Lake,Intel(R) Xeon(R) E-2176M Processor,2.70 ,45 W +Intel,Coffee Lake,Intel(R) Core(TM) i5-8300H Processor,2.30 ,45 W +Intel,Coffee Lake,Intel(R) Core(TM) i5-8400H Processor,2.50 ,45 W +Intel,Coffee Lake,Intel(R) Core(TM) i5-8400B Processor,2.80 ,65 W +Intel,Coffee Lake,Intel(R) Core(TM) i5-8500B Processor,3.00 ,65 W +Intel,Coffee Lake,Intel(R) Core(TM) i7-8850H Processor,2.60 ,45 W +Intel,Coffee Lake,Intel(R) Core(TM) i9-8950HK Processor,2.90 ,45 W +Intel,Coffee Lake,Intel(R) Core(TM) i7-8700B Processor,3.20 ,65 W +Intel,Coffee Lake,Intel(R) Core(TM) i7-8750H Processor,2.20 ,45 W +Intel,Coffee Lake,Intel(R) Core(TM) i5-8259U Processor,2.30 ,28 W +Intel,Coffee Lake,Intel(R) Core(TM) i3-8109U Processor,3.00 ,28 W +Intel,Coffee Lake,Intel(R) Core(TM) i7-8559U Processor,2.70 ,28 W +Intel,Coffee Lake,Intel(R) Core(TM) i5-8269U Processor,2.60 ,28 W +Intel,Coffee Lake,Intel(R) Core(TM) i3-8100H Processor,3.00 ,45 W +Intel,Coffee Lake,Intel(R) Core(TM) i3-8100B Processor,3.60 ,65 W +Intel,Coffee Lake,Intel(R) Core(TM) i7-9750H Processor,2.60 ,45 W +Intel,Coffee Lake,Intel(R) Core(TM) i7-9750HF Processor,2.60 ,45 W +Intel,Coffee Lake,Intel(R) Core(TM) i7-9850H Processor,2.60 ,45 W +Intel,Coffee Lake,Intel(R) Core(TM) i7-8569U Processor,2.80 ,28 W +Intel,Coffee Lake,Intel(R) Core(TM) i5-8257U Processor,1.40 ,15 W +Intel,Coffee Lake,Intel(R) Core(TM) i5-8279U Processor,2.40 ,28 W +Intel,Coffee Lake,Intel(R) Core(TM) i5-9300H Processor,2.40 ,45 W +Intel,Coffee Lake,Intel(R) Core(TM) i5-9400H Processor,2.50 ,45 W +Intel,Coffee Lake,Intel(R) Core(TM) i5-9300HF Processor,2.40 ,45 W +Intel,Coffee Lake,Intel(R) Core(TM) i9-9880H Processor,2.30 ,45 W +Intel,Coffee Lake,Intel(R) Xeon(R) E-2276M Processor,2.80 ,45 W +Intel,Coffee Lake,Intel(R) Core(TM) i9-9980HK Processor,2.40 ,45 W +Intel,Coffee Lake,Intel(R) Xeon(R) E-2286M Processor,2.40 ,45 W +Intel,Coffee Lake,Intel(R) Core(TM) i7-8557U Processor,1.70 ,15 W +Intel,Coffee Lake,Intel(R) Core(TM) i5-8260U Processor,1.60 ,15 W +Intel,Coffee Lake,Intel(R) Core(TM) i3-8140U Processor,2.10,15 W +Intel,Coffee Lake,Intel(R) Xeon(R) E-2124G Processor,3.40 ,71 W +Intel,Coffee Lake,Intel(R) Xeon(R) E-2186G Processor,3.80 ,95 W +Intel,Coffee Lake,Intel(R) Xeon(R) E-2124 Processor,3.30 ,71 W +Intel,Coffee Lake,Intel(R) Xeon(R) E-2136 Processor,3.30 ,80 W +Intel,Coffee Lake,Intel(R) Xeon(R) E-2134 Processor,3.50 ,71 W +Intel,Coffee Lake,Intel(R) Xeon(R) E-2176G Processor,3.70 ,80 W +Intel,Coffee Lake,Intel(R) Xeon(R) E-2144G Processor,3.60 ,71 W +Intel,Coffee Lake,Intel(R) Xeon(R) E-2126G Processor,3.30 ,80 W +Intel,Coffee Lake,Intel(R) Xeon(R) E-2174G Processor,3.80 ,71 W +Intel,Coffee Lake,Intel(R) Xeon(R) E-2146G Processor,3.50 ,80 W +Intel,Coffee Lake,Intel(R) Xeon(R) E-2286G Processor,4.00 ,95 W +Intel,Coffee Lake,Intel(R) Xeon(R) E-2276G Processor,3.80 ,80 W +Intel,Coffee Lake,Intel(R) Xeon(R) E-2224 Processor,3.40 ,71 W +Intel,Coffee Lake,Intel(R) Xeon(R) E-2224G Processor,3.50 ,71 W +Intel,Coffee Lake,Intel(R) Xeon(R) E-2226G Processor,3.40 ,80 W +Intel,Coffee Lake,Intel(R) Xeon(R) E-2234 Processor,3.60 ,71 W +Intel,Coffee Lake,Intel(R) Xeon(R) E-2236 Processor,3.40 ,80 W +Intel,Coffee Lake,Intel(R) Xeon(R) E-2244G Processor,3.80 ,71 W +Intel,Coffee Lake,Intel(R) Xeon(R) E-2274G Processor,4.00 ,83 W +Intel,Coffee Lake,Intel(R) Xeon(R) E-2246G Processor,3.60 ,80 W +Intel,Coffee Lake,Intel(R) Xeon(R) E-2288G Processor,3.70 ,95 W +Intel,Coffee Lake,Intel(R) Xeon(R) E-2278G Processor,3.40 ,80 W +Intel,Whiskey lake,Intel(R) Core(TM) i5-8265U Processor,1.60 ,15 W +Intel,Whiskey lake,Intel(R) Core(TM) i3-8145U Processor,2.10,15 W +Intel,Whiskey lake,Intel(R) Core(TM) i7-8565U Processor,1.80 ,15 W +Intel,Whiskey lake,Intel(R) Celeron(R) Processor 4205U,1.80 ,15 W +Intel,Whiskey lake,Intel(R) Pentium(R) Gold 5405U Processor,2.30 ,15 W +Intel,Whiskey lake,Intel(R) Core(TM) i7-8665UE Processor,1.70 ,15 W +Intel,Whiskey lake,Intel(R) Core(TM) i5-8365U Processor,1.60 ,15 W +Intel,Whiskey lake,Intel(R) Core(TM) i5-8365UE Processor,1.60 ,15 W +Intel,Whiskey lake,Intel(R) Core(TM) i3-8145UE Processor,2.20 ,15 W +Intel,Whiskey lake,Intel(R) Celeron(R) Processor 4305U,2.20 ,15 W +Intel,Whiskey lake,Intel(R) Core(TM) i7-8665U Processor,1.90 ,15 W +Intel,Whiskey lake,Intel(R) Celeron(R) Processor 4305UE,2.00 ,15 W +Intel,Amber Lake,Intel(R) Core(TM) i5-8200Y Processor,1.30 ,5 W +Intel,Amber Lake,Intel(R) Core(TM) i7-8500Y Processor,1.50 ,5 W +Intel,Amber Lake,Intel(R) Core(TM) m3-8100Y Processor,1.10,5 W +Intel,Amber Lake,Intel(R) Core(TM) i5-8210Y Processor,1.60 ,7 W +Intel,Amber Lake,Intel(R) Core(TM) i5-8310Y Processor,1.60 ,7 W +Intel,Amber Lake,Intel(R) Core(TM) i7-10510Y Processor,1.20 ,7 W +Intel,Amber Lake,Intel(R) Core(TM) i5-10310Y Processor,1.10,7 W +Intel,Amber Lake,Intel(R) Core(TM) i5-10210Y Processor,1.00 ,7 W +Intel,Amber Lake,Intel(R) Core(TM) i3-10110Y Processor,1.00 ,7 W +Intel,Amber Lake,Intel(R) Core(TM) i3-10100Y Processor,1.30 ,5 W +Intel,Amber Lake,Intel(R) Pentium(R) Gold 6500Y Processor,1.10,5 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 6230 Processor,2.10,125 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 6238T Processor,1.90 ,125 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 6242 Processor,2.80 ,150 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 6244 Processor,3.60 ,150 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 6240 Processor,2.60 ,150 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 5218 Processor,2.30 ,125 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 5222 Processor,3.80 ,105 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 6248 Processor,2.50 ,150 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 6252 Processor,2.10,150 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 6240Y Processor,2.60 ,150 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 6230N Processor,2.30 ,125 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 6254 Processor,3.10,200 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 6210U Processor,2.50 ,150 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 6212U Processor,2.40 ,165 W +Intel,Cascade lake,Intel(R) Xeon(R) Platinum 8253 Processor,2.20 ,125 W +Intel,Cascade lake,Intel(R) Xeon(R) Platinum 8256 Processor,3.80 ,105 W +Intel,Cascade lake,Intel(R) Xeon(R) Platinum 8276 Processor,2.20 ,165 W +Intel,Cascade lake,Intel(R) Xeon(R) Platinum 8280L Processor,2.70 ,205 W +Intel,Cascade lake,Intel(R) Xeon(R) Platinum 8260 Processor,2.40 ,165 W +Intel,Cascade lake,Intel(R) Xeon(R) Platinum 8276L Processor,2.20 ,165 W +Intel,Cascade lake,Intel(R) Xeon(R) Platinum 8260L Processor,2.40 ,165 W +Intel,Cascade lake,Intel(R) Xeon(R) Platinum 8280 Processor,2.70 ,205 W +Intel,Cascade lake,Intel(R) Xeon(R) Platinum 8260Y Processor,2.40 ,165 W +Intel,Cascade lake,Intel(R) Xeon(R) Platinum 8268 Processor,2.90 ,205 W +Intel,Cascade lake,Intel(R) Xeon(R) Platinum 8270 Processor,2.70 ,205 W +Intel,Cascade lake,Intel(R) Xeon(R) Bronze 3204 Processor,1.90 ,85 W +Intel,Cascade lake,Intel(R) Xeon(R) Silver 4209T Processor,2.20 ,70 W +Intel,Cascade lake,Intel(R) Xeon(R) Silver 4210 Processor,2.20 ,85 W +Intel,Cascade lake,Intel(R) Xeon(R) Silver 4214 Processor,2.20 ,85 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 5220 Processor,2.20 ,125 W +Intel,Cascade lake,Intel(R) Xeon(R) Silver 4215 Processor,2.50 ,85 W +Intel,Cascade lake,Intel(R) Xeon(R) Silver 4208 Processor,2.10,85 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 5215 Processor,2.50 ,85 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 5215L Processor,2.50 ,85 W +Intel,Cascade lake,Intel(R) Xeon(R) Silver 4216 Processor,2.10,100 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 5217 Processor,3.00 ,115 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 5218N Processor,2.30 ,110 W +Intel,Cascade lake,Intel(R) Xeon(R) Silver 4214Y Processor,2.20 ,85 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 5218B Processor,2.30 ,125 W +Intel,Cascade lake,Intel(R) Xeon(R) W-3223 Processor,3.50 ,160 W +Intel,Cascade lake,Intel(R) Xeon(R) W-3265 Processor,2.70 ,205 W +Intel,Cascade lake,Intel(R) Xeon(R) W-3225 Processor,3.70 ,160 W +Intel,Cascade lake,Intel(R) Xeon(R) W-3235 Processor,3.30 ,180 W +Intel,Cascade lake,Intel(R) Xeon(R) W-3265M Processor,2.70 ,205 W +Intel,Cascade lake,Intel(R) Xeon(R) W-3245M Processor,3.20 ,205 W +Intel,Cascade lake,Intel(R) Xeon(R) W-3275 Processor,2.50 ,205 W +Intel,Cascade lake,Intel(R) Xeon(R) W-3245 Processor,3.20 ,205 W +Intel,Cascade lake,Intel(R) Xeon(R) W-3275M Processor,2.50 ,205 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 6238 Processor,2.10,140 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 5220S Processor,2.70 ,125 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 6252N Processor,2.30 ,150 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 6230T Processor,2.10,125 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 5218T Processor,2.10,105 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 6234 Processor,3.30 ,130 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 6226 Processor,2.70 ,125 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 6238L Processor,2.10,140 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 6240L Processor,2.60 ,150 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 6222V Processor,1.80 ,115 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 6246 Processor,3.30 ,165 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 5220T Processor,1.90 ,105 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 6209U Processor,2.10,125 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 6262V Processor,1.90 ,135 W +Intel,Cascade lake,Intel(R) Xeon(R) Platinum 9242 Processor,2.30 ,350 W +Intel,Cascade lake,Intel(R) Xeon(R) Platinum 9282 Processor,2.60 ,400 W +Intel,Cascade lake,Intel(R) Xeon(R) Platinum 9221 Processor,2.30 ,250 W +Intel,Cascade lake,Intel(R) Xeon(R) Platinum 9222 Processor,2.30 ,250 W +Intel,Cascade lake,Intel(R) Xeon(R) Silver 4210R Processor,2.40 ,100 W +Intel,Cascade lake,Intel(R) Xeon(R) Silver 4214R Processor,2.40 ,100 W +Intel,Cascade lake,Intel(R) Xeon(R) Bronze 3206R Processor,1.90 ,85 W +Intel,Cascade lake,Intel(R) Xeon(R) W-2275 Processor,3.30 ,165 W +Intel,Cascade lake,Intel(R) Xeon(R) W-2295 Processor,3.00 ,165 W +Intel,Cascade lake,Intel(R) Core(TM) i9-10920X X-series Processor,3.50 ,165 W +Intel,Cascade lake,Intel(R) Core(TM) i9-10940X X-series Processor,3.30 ,165 W +Intel,Cascade lake,Intel(R) Xeon(R) W-2265 Processor,3.50 ,165 W +Intel,Cascade lake,Intel(R) Xeon(R) W-2255 Processor,3.70 ,165 W +Intel,Cascade lake,Intel(R) Core(TM) i9-10980XE Extreme Edition Processor,3.00 ,165 W +Intel,Cascade lake,Intel(R) Xeon(R) W-2223 Processor,3.60 ,120 W +Intel,Cascade lake,Intel(R) Core(TM) i9-10900X X-series Processor,3.70 ,165 W +Intel,Cascade lake,Intel(R) Xeon(R) W-2235 Processor,3.80 ,130 W +Intel,Cascade lake,Intel(R) Xeon(R) W-2245 Processor,3.90 ,155 W +Intel,Cascade lake,Intel(R) Xeon(R) W-2225 Processor,4.10,105 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 6250 Processor,3.90 ,185 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 6256 Processor,3.60 ,205 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 5218R Processor,2.10,125 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 6240R Processor,2.40 ,165 W +Intel,Cascade lake,Intel(R) Xeon(R) Silver 4210T Processor,2.30 ,95 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 6238R Processor,2.20 ,165 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 6230R Processor,2.10,150 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 6226R Processor,2.90 ,150 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 6208U Processor,2.90 ,150 W +Intel,Cascade lake,Intel(R) Xeon(R) Silver 4215R Processor,3.20 ,130 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 6258R Processor,2.70 ,205 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 6248R Processor,3.00 ,205 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 6242R Processor,3.10,205 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 6246R Processor,3.40 ,205 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 5220R Processor,2.20 ,150 W +Intel,Cascade lake,Intel(R) Xeon(R) Gold 6250L Processor,3.90 ,185 W +Intel,Comet lake,Intel(R) Core(TM) i5-10210U Processor,1.60 ,15 W +Intel,Comet lake,Intel(R) Core(TM) i7-10710U Processor,1.10,15 W +Intel,Comet lake,Intel(R) Core(TM) i7-10510U Processor,1.80 ,15 W +Intel,Comet lake,Intel(R) Core(TM) i3-10110U Processor,2.10,15 W +Intel,Comet lake,Intel(R) Pentium(R) Gold 6405U Processor,2.40 ,15 W +Intel,Comet lake,Intel(R) Celeron(R) Processor 5305U,2.30 ,15 W +Intel,Comet lake,Intel(R) Celeron(R) Processor 5205U,1.90 ,15 W +Intel,Comet lake,Intel(R) Celeron(R) Processor G5900,3.40 ,58 W +Intel,Comet lake,Intel(R) Celeron(R) Processor G5900T,3.20 ,35 W +Intel,Comet lake,Intel(R) Core(TM) i5-10400 Processor,2.90 ,65 W +Intel,Comet lake,Intel(R) Core(TM) i5-10600 Processor,3.30 ,65 W +Intel,Comet lake,Intel(R) Xeon(R) W-1250 Processor,3.30 ,80 W +Intel,Comet lake,Intel(R) Core(TM) i5-10500T Processor,2.30 ,35 W +Intel,Comet lake,Intel(R) Core(TM) i5-10400T Processor,2.00 ,35 W +Intel,Comet lake,Intel(R) Core(TM) i5-10500 Processor,3.10,65 W +Intel,Comet lake,Intel(R) Core(TM) i5-10400F Processor,2.90 ,65 W +Intel,Comet lake,Intel(R) Core(TM) i5-10600T Processor,2.40 ,35 W +Intel,Comet lake,Intel(R) Core(TM) i3-10320 Processor,3.80 ,65 W +Intel,Comet lake,Intel(R) Core(TM) i3-10300 Processor,3.70 ,65 W +Intel,Comet lake,Intel(R) Core(TM) i3-10300T Processor,3.00 ,35 W +Intel,Comet lake,Intel(R) Core(TM) i3-10100 Processor,3.60 ,65 W +Intel,Comet lake,Intel(R) Core(TM) i3-10100T Processor,3.00 ,35 W +Intel,Comet lake,Intel(R) Pentium(R) Gold G6600 Processor,4.20 ,58 W +Intel,Comet lake,Intel(R) Pentium(R) Gold G6500T Processor,3.50 ,35 W +Intel,Comet lake,Intel(R) Pentium(R) Gold G6500 Processor,4.10,58 W +Intel,Comet lake,Intel(R) Pentium(R) Gold G6400 Processor,4.00 ,58 W +Intel,Comet lake,Intel(R) Pentium(R) Gold G6400T Processor,3.40 ,35 W +Intel,Comet lake,Intel(R) Celeron(R) Processor G5920,3.50 ,58 W +Intel,Comet lake,Intel(R) Core(TM) i5-10600K Processor,4.10,125 W +Intel,Comet lake,Intel(R) Core(TM) i7-10700T Processor,2.00 ,35 W +Intel,Comet lake,Intel(R) Core(TM) i5-10600KF Processor,4.10,125 W +Intel,Comet lake,Intel(R) Core(TM) i7-10700 Processor,2.90 ,65 W +Intel,Comet lake,Intel(R) Core(TM) i7-10700F Processor,2.90 ,65 W +Intel,Comet lake,Intel(R) Core(TM) i9-10900T Processor,1.90 ,35 W +Intel,Comet lake,Intel(R) Core(TM) i7-10700KF Processor,3.80 ,125 W +Intel,Comet lake,Intel(R) Core(TM) i9-10900 Processor,2.80 ,65 W +Intel,Comet lake,Intel(R) Core(TM) i9-10900F Processor,2.80 ,65 W +Intel,Comet lake,Intel(R) Core(TM) i9-10900KF Processor,3.70 ,125 W +Intel,Comet lake,Intel(R) Core(TM) i9-10900K Processor,3.70 ,125 W +Intel,Comet lake,Intel(R) Core(TM) i7-10700K Processor,3.80 ,125 W +Intel,Comet lake,Intel(R) Xeon(R) W-1290P Processor,3.70 ,125 W +Intel,Comet lake,Intel(R) Xeon(R) W-1290 Processor,3.20 ,80 W +Intel,Comet lake,Intel(R) Xeon(R) W-1270P Processor,3.80 ,125 W +Intel,Comet lake,Intel(R) Xeon(R) W-1270 Processor,3.40 ,80 W +Intel,Comet lake,Intel(R) Xeon(R) W-1250P Processor,4.10,125 W +Intel,Comet lake,Intel(R) Xeon(R) W-1290T Processor,1.90 ,35 W +Intel,Comet lake,Intel(R) Core(TM) i7-10750H Processor,2.60 ,45 W +Intel,Comet lake,Intel(R) Core(TM) i9-10980HK Processor,2.40 ,45 W +Intel,Comet lake,Intel(R) Core(TM) i5-10300H Processor,2.50 ,45 W +Intel,Comet lake,Intel(R) Celeron(R) Processor G5905T,3.30 ,35 W +Intel,Comet lake,Intel(R) Celeron(R) Processor G5925,3.60 ,58 W +Intel,Comet lake,Intel(R) Core(TM) i7-10810U Processor,1.10,15 W +Intel,Comet lake,Intel(R) Core(TM) i3-10325 Processor,3.90 ,65 W +Intel,Comet lake,Intel(R) Core(TM) i3-10105T Processor,3.00 ,35 W +Intel,Comet lake,Intel(R) Core(TM) i5-10505 Processor,3.20 ,65 W +Intel,Comet lake,Intel(R) Core(TM) i5-10310U Processor,1.70 ,15 W +Intel,Comet lake,Intel(R) Core(TM) i3-10305 Processor,3.80 ,65 W +Intel,Comet lake,Intel(R) Core(TM) i3-10105 Processor,3.70 ,65 W +Intel,Comet lake,Intel(R) Core(TM) i5-10400H Processor,2.60 ,45 W +Intel,Comet lake,Intel(R) Core(TM) i7-10610U Processor,1.80 ,15 W +Intel,Comet lake,Intel(R) Core(TM) i7-10850H Processor,2.70 ,45 W +Intel,Comet lake,Intel(R) Core(TM) i3-10305T Processor,3.00 ,35 W +Intel,Comet lake,Intel(R) Celeron(R) Processor G5905,3.50 ,58 W +Intel,Comet lake,Intel(R) Pentium(R) Gold G6405T Processor,3.50 ,35 W +Intel,Comet lake,Intel(R) Pentium(R) Gold G6405 Processor,4.10,58 W +Intel,Comet lake,Intel(R) Pentium(R) Gold G6505 Processor,4.20 ,58 W +Intel,Comet lake,Intel(R) Pentium(R) Gold G6505T Processor,3.60 ,35 W +Intel,Comet lake,Intel(R) Pentium(R) Gold G6605 Processor,4.30 ,58 W +Intel,Comet lake,Intel(R) Core(TM) i5-10500H Processor,2.50 ,45 W +Intel,Comet lake,Intel(R) Xeon(R) W-10855M Processor,2.80 ,45 W +Intel,Comet lake,Intel(R) Xeon(R) W-10885M Processor,2.40 ,45 W +Intel,Comet lake,Intel(R) Core(TM) i7-10875H Processor,2.30 ,45 W +Intel,Comet lake,Intel(R) Core(TM) i3-10100F Processor,3.60 ,65 W +Intel,Comet lake,Intel(R) Core(TM) i3-10105F Processor,3.70 ,65 W +Intel,Comet lake,Intel(R) Core(TM) i9-10885H Processor,2.40 ,45 W +Intel,Comet lake,Intel(R) Celeron(R) Processor G5900TE,3.00 ,35 W +Intel,Comet lake,Intel(R) Core(TM) i5-10500TE Processor,2.30 ,35 W +Intel,Comet lake,Intel(R) Xeon(R) W-1250E Processor,3.50 ,80 W +Intel,Comet lake,Intel(R) Core(TM) i5-10500E Processor,3.10,65 W +Intel,Comet lake,Intel(R) Pentium(R) Gold G6400TE Processor,3.20 ,35 W +Intel,Comet lake,Intel(R) Xeon(R) W-1250TE Processor,2.40 ,35 W +Intel,Comet lake,Intel(R) Pentium(R) Gold G6400E Processor,3.80 ,58 W +Intel,Comet lake,Intel(R) Celeron(R) Processor G5900E,3.20 ,58 W +Intel,Comet lake,Intel(R) Core(TM) i3-10100E Processor,3.20 ,65 W +Intel,Comet lake,Intel(R) Core(TM) i3-10100TE Processor,2.30 ,35 W +Intel,Comet lake,Intel(R) Core(TM) i9-10900TE Processor,1.80 ,35 W +Intel,Comet lake,Intel(R) Core(TM) i9-10900E Processor,2.80 ,65 W +Intel,Comet lake,Intel(R) Xeon(R) W-1290E Processor,3.50 ,95 W +Intel,Comet lake,Intel(R) Xeon(R) W-1270E Processor,3.40 ,80 W +Intel,Comet lake,Intel(R) Xeon(R) W-1270TE Processor,2.00 ,35 W +Intel,Comet lake,Intel(R) Core(TM) i7-10700TE Processor,2.00 ,35 W +Intel,Comet lake,Intel(R) Xeon(R) W-1290TE Processor,1.80 ,35 W +Intel,Comet lake,Intel(R) Core(TM) i7-10700E Processor,2.90 ,65 W +Intel,Comet lake,Intel(R) Core(TM) i9-10850K Processor,3.60 ,125 W +Intel,Comet lake,Intel(R) Core(TM) i5-10200H Processor,2.40 ,45 W +Intel,Comet lake,Intel(R) Core(TM) i7-10870H Processor,2.20 ,45 W +Intel,Rocket lake,Intel(R) Core(TM) i7-11700K Processor,3.60 ,125 W +Intel,Rocket lake,Intel(R) Core(TM) i7-11700KF Processor,3.60 ,125 W +Intel,Rocket lake,Intel(R) Core(TM) i7-11700T Processor,1.40 ,35 W +Intel,Rocket lake,Intel(R) Core(TM) i9-11900 Processor,2.50 ,65 W +Intel,Rocket lake,Intel(R) Xeon(R) E-2336 Processor,2.90 ,65 W +Intel,Rocket lake,Intel(R) Core(TM) i9-11900F Processor,2.50 ,65 W +Intel,Rocket lake,Intel(R) Xeon(R) E-2324G Processor,3.10,65 W +Intel,Rocket lake,Intel(R) Core(TM) i9-11900T Processor,1.50 ,35 W +Intel,Rocket lake,Intel(R) Xeon(R) E-2356G Processor,3.20 ,80 W +Intel,Rocket lake,Intel(R) Xeon(R) E-2334 Processor,3.40 ,65 W +Intel,Rocket lake,Intel(R) Xeon(R) E-2314 Processor,2.80 ,65 W +Intel,Rocket lake,Intel(R) Xeon(R) E-2374G Processor,3.70 ,80 W +Intel,Rocket lake,Intel(R) Xeon(R) E-2378 Processor,2.60 ,65 W +Intel,Rocket lake,Intel(R) Xeon(R) E-2378G Processor,2.80 ,80 W +Intel,Rocket lake,Intel(R) Xeon(R) W-1350 Processor,3.30 ,80 W +Intel,Rocket lake,Intel(R) Xeon(R) W-1350P Processor,4.00 ,125 W +Intel,Rocket lake,Intel(R) Xeon(R) W-1370 Processor,2.90 ,80 W +Intel,Rocket lake,Intel(R) Xeon(R) W-1370P Processor,3.60 ,125 W +Intel,Rocket lake,Intel(R) Xeon(R) W-1390 Processor,2.80 ,80 W +Intel,Rocket lake,Intel(R) Xeon(R) W-1390P Processor,3.50 ,125 W +Intel,Rocket lake,Intel(R) Xeon(R) W-1390T Processor,1.50 ,35 W +Intel,Rocket lake,Intel(R) Core(TM) i5-11400 Processor,2.60 ,65 W +Intel,Rocket lake,Intel(R) Core(TM) i5-11400F Processor,2.60 ,65 W +Intel,Rocket lake,Intel(R) Core(TM) i5-11500T Processor,1.50 ,35 W +Intel,Rocket lake,Intel(R) Core(TM) i5-11400T Processor,1.30 ,35 W +Intel,Rocket lake,Intel(R) Core(TM) i5-11600 Processor,2.80 ,65 W +Intel,Rocket lake,Intel(R) Core(TM) i5-11600K Processor,3.90 ,125 W +Intel,Rocket lake,Intel(R) Core(TM) i5-11600KF Processor,3.90 ,125 W +Intel,Rocket lake,Intel(R) Core(TM) i5-11500 Processor,2.70 ,65 W +Intel,Rocket lake,Intel(R) Core(TM) i5-11600T Processor,1.70 ,35 W +Intel,Rocket lake,Intel(R) Core(TM) i7-11700 Processor,2.50 ,65 W +Intel,Rocket lake,Intel(R) Core(TM) i7-11700F Processor,2.50 ,65 W +Intel,Rocket lake,Intel(R) Core(TM) i9-11900KF Processor,3.50 ,125 W +Intel,Rocket lake,Intel(R) Core(TM) i9-11900K Processor,3.50 ,125 W +Intel,Rocket lake,Intel(R) Xeon(R) E-2388G Processor,3.20 ,95 W +Intel,Rocket lake,Intel(R) Xeon(R) E-2386G Processor,3.50 ,95 W +AMD,Zen 5,AMD Ryzen(TM) AI 9 HX PRO 375,2,28W +AMD,Zen 5,AMD Ryzen(TM) AI 9 HX PRO 370,2,28W +AMD,Zen 4,AMD Ryzen(TM) AI 7 PRO 360,2,28W +AMD,Zen 5,AMD Ryzen(TM) AI 9 HX 375,,28W +AMD,Zen 5,AMD Ryzen(TM) AI 9 HX 370,,28W +AMD,Zen 5,AMD Ryzen(TM) AI 9 365,2,28W +AMD,Zen 5,AMD Ryzen(TM) 9 9950X,4.3,170W +AMD,Zen 5,AMD Ryzen(TM) 9 9900X,4.4,120W +AMD,Zen 5,AMD Ryzen(TM) 7 9700X,3.8,65W +AMD,Zen 5,AMD Ryzen(TM) 5 9600X,3.9,65W +AMD,Zen 4,AMD Ryzen(TM) 9 PRO 8945HS,4,45W +AMD,Zen 4,AMD Ryzen(TM) 7 PRO 8840U,3.3,28W +AMD,Zen 4,AMD Ryzen(TM) 7 PRO 8845HS,3.8,45W +AMD,Zen 4,AMD Ryzen(TM) 7 PRO 8840HS,3.3,28W +AMD,Zen 4,AMD Ryzen(TM) 7 PRO 8700GE,3.6,35W +AMD,Zen 4,AMD Ryzen(TM) 7 PRO 8700G,4.2,65W +AMD,Zen 4,AMD Ryzen(TM) 5 PRO 8640U,3.5,28W +AMD,Zen 4,AMD Ryzen(TM) 5 PRO 8645HS,4.3,45W +AMD,Zen 4,AMD Ryzen(TM) 5 PRO 8640HS,3.5,28W +AMD,Zen 4,AMD Ryzen(TM) 5 PRO 8600GE,3.9,35W +AMD,Zen 4,AMD Ryzen(TM) 5 PRO 8600G,4.3,65W +AMD,Zen 4,AMD Ryzen(TM) 5 PRO 8540U,3.2,28W +AMD,Zen 4,AMD Ryzen(TM) 5 PRO 8500GE,3.4,35W +AMD,Zen 4,AMD Ryzen(TM) 5 PRO 8500G,3.5,65W +AMD,Zen 4,AMD Ryzen(TM) 9 8945HS,4,45W +AMD,Zen 4,AMD Ryzen(TM) 3 PRO 8300GE,3.5,35W +AMD,Zen 4,AMD Ryzen(TM) 3 PRO 8300G,3.4,65W +AMD,Zen 4,AMD Ryzen(TM) 7 8845HS,3.8,45W +AMD,Zen 4,AMD Ryzen(TM) 7 8840U,3.3,28W +AMD,Zen 4,AMD Ryzen(TM) 7 8840HS,3.3,28W +AMD,Zen 4,AMD Ryzen(TM) 7 8700G,4.2,65W +AMD,Zen 4,AMD Ryzen(TM) 7 8700F,4.1,65W +AMD,Zen 4,AMD Ryzen(TM) 5 8645HS,4.3,45W +AMD,Zen 4,AMD Ryzen(TM) 5 8640U,3.5,28W +AMD,Zen 4,AMD Ryzen(TM) 5 8640HS,3.5,28W +AMD,Zen 4,AMD Ryzen(TM) 5 8600G,4.3,65W +AMD,Zen 4,AMD Ryzen(TM) 5 8540U,3.2,28W +AMD,Zen 4,AMD Ryzen(TM) 5 8500GE,3.4,35W +AMD,Zen 4,AMD Ryzen(TM) 5 8500G,3.5,65W +AMD,Zen 4,AMD Ryzen(TM) 3 8440U,3,28W +AMD,Zen 4,AMD Ryzen(TM) 3 8300GE,3.5,35W +AMD,Zen 4,AMD Ryzen(TM) 5 8400F,4.2,65W +AMD,Zen 4,AMD Ryzen(TM) 3 8300G,3.4,65W +AMD,Zen 4,AMD Ryzen(TM) Threadripper(TM) PRO 7995WX,2.5,350W +AMD,Zen 4,AMD Ryzen(TM) Threadripper(TM) PRO 7985WX,3.2,350W +AMD,Zen 4,AMD Ryzen(TM) Threadripper(TM) PRO 7975WX,4,350W +AMD,Zen 4,AMD Ryzen(TM) Threadripper(TM) PRO 7965WX,4.2,350W +AMD,Zen 4,AMD Ryzen(TM) Threadripper(TM) PRO 7955WX,4.5,350W +AMD,Zen 4,AMD Ryzen(TM) Threadripper(TM) PRO 7945WX,4.7,350W +AMD,Zen 4,AMD Ryzen(TM) Threadripper(TM) 7980X,3.2,350W +AMD,Zen 4,AMD Ryzen(TM) Threadripper(TM) 7970X,4,350W +AMD,Zen 4,AMD Ryzen(TM) Threadripper(TM) 7960X,4.2,350W +AMD,Zen 4,AMD Ryzen(TM) 9 PRO 7945,3.7,65W +AMD,Zen 4,AMD Ryzen(TM) 9 PRO 7940HS,4, +AMD,Zen 4,AMD Ryzen(TM) 7 PRO 7840U,3.3, +AMD,Zen 4,AMD Ryzen(TM) 7 PRO 7840HS,3.8, +AMD,Zen 4,AMD Ryzen(TM) 7 PRO 7745,3.8,65W +AMD,Zen 4,AMD Ryzen(TM) 7 PRO 7735U,2.7, +AMD,Zen 4,AMD Ryzen(TM) 7 PRO 7730U,2,15W +AMD,Zen 4,AMD Ryzen(TM) 5 PRO 7645,3.8,65W +AMD,Zen 4,AMD Ryzen(TM) 5 PRO 7640U,3.5, +AMD,Zen 4,AMD Ryzen(TM) 5 PRO 7640HS,4.3, +AMD,Zen 4,AMD Ryzen(TM) 5 PRO 7545U,3.2, +AMD,Zen 4,AMD Ryzen(TM) 5 PRO 7540U,3.2, +AMD,Zen 4,AMD Ryzen(TM) 5 PRO 7535U,2.9, +AMD,Zen 4,AMD Ryzen(TM) 5 PRO 7530U,2,15W +AMD,Zen 4,AMD Ryzen(TM) 3 PRO 7335U,3, +AMD,Zen 4,AMD Ryzen(TM) 3 PRO 7330U,2.3,15W +AMD,Zen 4,AMD Ryzen(TM) 9 7950X3D,4.2,120W +AMD,Zen 4,AMD Ryzen(TM) 9 7950X,4.5,170W +AMD,Zen 4,AMD Ryzen(TM) 9 7945HX3D,2.3,55W +AMD,Zen 4,AMD Ryzen(TM) 9 7945HX,2.5,55W +AMD,Zen 4,AMD Ryzen(TM) 9 7940HS,4,54W +AMD,Zen 4,AMD Ryzen(TM) 9 7940HX,2.4,55W +AMD,Zen 4,AMD Ryzen(TM) 9 7900X3D,4.4,120W +AMD,Zen 4,AMD Ryzen(TM) 9 7900X,4.7,170W +AMD,Zen 4,AMD Ryzen(TM) 9 7900,3.7,65W +AMD,Zen 4,AMD Ryzen(TM) 9 7845HX,3,55W +AMD,Zen 4,AMD Ryzen(TM) 7 7840U,3.3,28W +AMD,Zen 4,AMD Ryzen(TM) 7 7840HX,2.9,55W +AMD,Zen 4,AMD Ryzen(TM) 7 7840HS,3.8,54W +AMD,Zen 4,AMD Ryzen(TM) 7 7800X3D,4.2,120W +AMD,Zen 4,AMD Ryzen(TM) 7 7745HX,3.6,55W +AMD,Zen 4,AMD Ryzen(TM) 7 7736U​,2.7, +AMD,Zen 4,AMD Ryzen(TM) 7 7735U,2.7,28W +AMD,Zen 4,AMD Ryzen(TM) 7 7735HS,3.2,54W +AMD,Zen 4,AMD Ryzen(TM) 7 7730U,2,15W +AMD,Zen 4,AMD Ryzen(TM) 7 7700X,4.5,105W +AMD,Zen 4,AMD Ryzen(TM) 7 7700,3.8,65W +AMD,Zen 4,AMD Ryzen(TM) 5 7645HX,4,55W +AMD,Zen 4,AMD Ryzen(TM) 5 7640U,3.5,28W +AMD,Zen 4,AMD Ryzen(TM) 5 7640HS,4.3,54W +AMD,Zen 4,AMD Ryzen(TM) 5 7600X3D,4.1,65W +AMD,Zen 4,AMD Ryzen(TM) 5 7600X,4.7,105W +AMD,Zen 4,AMD Ryzen(TM) 5 7600,3.8,65W +AMD,Zen 4,AMD Ryzen(TM) 5 7545U,3.2,28W +AMD,Zen 4,AMD Ryzen(TM) 5 7540U,3.2,28W +AMD,Zen 4,AMD Ryzen(TM) 5 7535U,2.9,28W +AMD,Zen 4,AMD Ryzen(TM) 5 7535HS,3.3,54W +AMD,Zen 4,AMD Ryzen(TM) 5 7530U,2,15W +AMD,Zen 4,AMD Ryzen(TM) 5 7520U,2.8,15W +AMD,Zen 4,AMD Ryzen(TM) 5 7520C,2.8,15W +AMD,Zen 4,AMD Ryzen(TM) 5 7500F,3.7,65W +AMD,Zen 4,AMD Ryzen(TM) 3 7440U,3,28W +AMD,Zen 4,AMD Ryzen(TM) 7 7435HS,3.1,45W +AMD,Zen 4,AMD Ryzen(TM) 5 7430U,2.3,15W +AMD,Zen 4,AMD Ryzen(TM) 3 7335U,3,28W +AMD,Zen 4,AMD Ryzen(TM) 3 7330U,2.3,15W +AMD,Zen 4,AMD Ryzen(TM) 3 7320U,2.4,15W +AMD,Zen 4,AMD Ryzen(TM) 3 7320C,2.4,15W +AMD,Zen 4,AMD Ryzen(TM) 5 7235HS,3.2,45W +AMD,Zen 4,AMD Ryzen(TM) Z1 Extreme,3.3, +AMD,Zen 4,AMD Ryzen(TM) Z1,3.2, +AMD,Zen 4,AMD Athlon(TM) Gold 7220U,2.4,15W +AMD,Zen 4,AMD Athlon(TM) Gold 7220C,2.4,15W +AMD,Zen 4,AMD Athlon(TM) Silver 7120U,2.4,15W +AMD,Zen 4,AMD Athlon(TM) Silver 7120C,2.4,15W +AMD,Zen 3+,AMD Ryzen(TM) 9 PRO 6950HS,3.3,35W +AMD,Zen 3+,AMD Ryzen(TM) 9 PRO 6950H,3.3,45W +AMD,Zen 3+,AMD Ryzen(TM) 7 PRO 6860Z,2.7,28W +AMD,Zen 3+,AMD Ryzen(TM) 7 PRO 6850U,2.7, +AMD,Zen 3+,AMD Ryzen(TM) 7 PRO 6850HS,3.2,35W +AMD,Zen 3+,AMD Ryzen(TM) 7 PRO 6850H,3.2,45W +AMD,Zen 3+,AMD Ryzen(TM) 5 PRO 6650U,2.9, +AMD,Zen 3+,AMD Ryzen(TM) 5 PRO 6650HS,3.3,35W +AMD,Zen 3+,AMD Ryzen(TM) 5 PRO 6650H,3.3,45W +AMD,Zen 3+,AMD Ryzen(TM) 9 6980HX,3.3,45W +AMD,Zen 3+,AMD Ryzen(TM) 9 6980HS,3.3,35W +AMD,Zen 3+,AMD Ryzen(TM) 9 6900HX,3.3,45W +AMD,Zen 3+,AMD Ryzen(TM) 9 6900HS​,3.3,35W +AMD,Zen 3+,AMD Ryzen(TM) 7 6800U​,2.7, +AMD,Zen 3+,AMD Ryzen(TM) 7 6800HS,3.2,35W +AMD,Zen 3+,AMD Ryzen(TM) 7 6800H,3.2,45W +AMD,Zen 3+,AMD Ryzen(TM) 5 6600U,2.9, +AMD,Zen 3+,AMD Ryzen(TM) 5 6600HS​,3.3,35W +AMD,Zen 3+,AMD Ryzen(TM) 5 6600H,3.3,45W +AMD,Zen 3,AMD Ryzen(TM) Threadripper(TM) PRO 5995WX,2.7,280W +AMD,Zen 3,AMD Ryzen(TM) Threadripper(TM) PRO 5975WX,3.6,280W +AMD,Zen 3,AMD Ryzen(TM) Threadripper(TM) PRO 5965WX,3.8,280W +AMD,Zen 3,AMD Ryzen(TM) Threadripper(TM) PRO 5955WX,4,280W +AMD,Zen 3,AMD Ryzen(TM) Threadripper(TM) PRO 5945WX,4.1,280W +AMD,Zen 3,AMD Ryzen(TM) 9 PRO 5945,3,65W +AMD,Zen 3,AMD Ryzen(TM) 7 PRO 5875U,2,15W +AMD,Zen 3,AMD Ryzen(TM) 7 PRO 5850U,1.9,15W +AMD,Zen 3,AMD Ryzen(TM) 7 PRO 5845,3.4,65W +AMD,Zen 3,AMD Ryzen(TM) 7 PRO 5755GE,3.2,35W +AMD,Zen 3,AMD Ryzen(TM) 7 PRO 5755G,3.8,65W +AMD,Zen 3,AMD Ryzen(TM) 7 PRO 5750GE,3.2,35W +AMD,Zen 3,AMD Ryzen(TM) 7 PRO 5750G,3.8,65W +AMD,Zen 3,AMD Ryzen(TM) 5 PRO 5675U,2.3,15W +AMD,Zen 3,AMD Ryzen(TM) 5 PRO 5655GE,3.4,35W +AMD,Zen 3,AMD Ryzen(TM) 5 PRO 5655G,3.9,65W +AMD,Zen 3,AMD Ryzen(TM) 5 PRO 5650U,2.3,15W +AMD,Zen 3,AMD Ryzen(TM) 5 PRO 5650GE,3.4,35W +AMD,Zen 3,AMD Ryzen(TM) 5 PRO 5650G,3.9,65W +AMD,Zen 3,AMD Ryzen(TM) 5 PRO 5645,3.7,65W +AMD,Zen 3,AMD Ryzen(TM) 3 PRO 5475U,2.7,15W +AMD,Zen 3,AMD Ryzen(TM) 3 PRO 5450U,2.6,15W +AMD,Zen 3,AMD Ryzen(TM) 3 PRO 5355GE,3.6,35W +AMD,Zen 3,AMD Ryzen(TM) 3 PRO 5355G,4,65W +AMD,Zen 3,AMD Ryzen(TM) 3 PRO 5350GE,3.6,35W +AMD,Zen 3,AMD Ryzen(TM) 3 PRO 5350G,4,65W +AMD,Zen 3,AMD Ryzen(TM) 9 5980HX,3.3,45W +AMD,Zen 3,AMD Ryzen(TM) 9 5980HS,3,35W +AMD,Zen 3,AMD Ryzen(TM) 9 5950X,3.4,105W +AMD,Zen 3,AMD Ryzen(TM) 9 5900XT,3.3,105W +AMD,Zen 3,AMD Ryzen(TM) 9 5900X,3.7,105W +AMD,Zen 3,AMD Ryzen(TM) 9 5900HX,3.3,45W +AMD,Zen 3,AMD Ryzen(TM) 9 5900HS,3,35W +AMD,Zen 3,AMD Ryzen(TM) 9 5900 (OEM Only),3,65W +AMD,Zen 3,AMD Ryzen(TM) 7 5825U,2,15W +AMD,Zen 3,AMD Ryzen(TM) 7 5825C,2,15W +AMD,Zen 3,AMD Ryzen(TM) 7 5800X3D,3.4,105W +AMD,Zen 3,AMD Ryzen(TM) 7 5800XT,3.8,105W +AMD,Zen 3,AMD Ryzen(TM) 7 5800X,3.8,105W +AMD,Zen 3,AMD Ryzen(TM) 7 5800U,1.9,15W +AMD,Zen 3,AMD Ryzen(TM) 7 5800HS,2.8,35W +AMD,Zen 3,AMD Ryzen(TM) 7 5800H,3.2,45W +AMD,Zen 3,AMD Ryzen(TM) 7 5800 (OEM Only),3.4,65W +AMD,Zen 3,AMD Ryzen(TM) 7 5700X3D,3,105W +AMD,Zen 3,AMD Ryzen(TM) 7 5700X,3.4,65W +AMD,Zen 3,AMD Ryzen(TM) 7 5700U,1.8,15W +AMD,Zen 3,AMD Ryzen(TM) 7 5700GE,3.2,35W +AMD,Zen 3,AMD Ryzen(TM) 7 5700G,3.8,65W +AMD,Zen 3,AMD Ryzen(TM) 7 5700,3.7,65W +AMD,Zen 3,AMD Ryzen(TM) 5 5625U,2.3,15W +AMD,Zen 3,AMD Ryzen(TM) 5 5625C,2.3,15W +AMD,Zen 3,AMD Ryzen(TM) 5 5600X3D,3.3,105W +AMD,Zen 3,AMD Ryzen(TM) 5 5600X,3.7,65W +AMD,Zen 3,AMD Ryzen(TM) 5 5600U,2.3,15W +AMD,Zen 3,AMD Ryzen(TM) 5 5600HS,3,35W +AMD,Zen 3,AMD Ryzen(TM) 5 5600H,3.3,45W +AMD,Zen 3,AMD Ryzen(TM) 5 5600GT,3.6,65W +AMD,Zen 3,AMD Ryzen(TM) 5 5600GE,3.4,35W +AMD,Zen 3,AMD Ryzen(TM) 5 5600G,3.9,65W +AMD,Zen 3,AMD Ryzen(TM) 5 5600,3.5,65W +AMD,Zen 3,AMD Ryzen(TM) 5 5560U,2.3,15W +AMD,Zen 3,AMD Ryzen(TM) 5 5500U,2.1,15W +AMD,Zen 3,AMD Ryzen(TM) 5 5500H,3.3,45W +AMD,Zen 3,AMD Ryzen(TM) 5 5500GT,3.6,65W +AMD,Zen 3,AMD Ryzen(TM) 5 5500,3.6,65W +AMD,Zen 3,AMD Ryzen(TM) 3 5425U,2.7,15W +AMD,Zen 3,AMD Ryzen(TM) 3 5425C,2.7,15W +AMD,Zen 3,AMD Ryzen(TM) 3 5400U,2.6,15W +AMD,Zen 3,AMD Ryzen(TM) 3 5300U,2.6,15W +AMD,Zen 3,AMD Ryzen(TM) 3 5300GE (OEM Only),3.6,35W +AMD,Zen 3,AMD Ryzen(TM) 3 5300G (OEM Only),4,65W +AMD,Zen 3,AMD Ryzen(TM) 3 5125C,3,15W +AMD,Zen 2,AMD Ryzen(TM) 7 PRO 4750U,1.7,15W +AMD,Zen 2,AMD Ryzen(TM) 7 PRO 4750GE,3.1,35W +AMD,Zen 2,AMD Ryzen(TM) 7 PRO 4750G,3.6,65W +AMD,Zen 2,AMD Ryzen(TM) 5 PRO 4655GE,3.3,35W +AMD,Zen 2,AMD Ryzen(TM) 5 PRO 4655G,3.7,65W +AMD,Zen 2,AMD Ryzen(TM) 5 PRO 4650U,2.1,15W +AMD,Zen 2,AMD Ryzen(TM) 5 PRO 4650GE,3.3,35W +AMD,Zen 2,AMD Ryzen(TM) 5 PRO 4650G,3.7,65W +AMD,Zen 2,AMD Ryzen(TM) 3 PRO 4450U,2.5,15W +AMD,Zen 2,AMD Ryzen(TM) 3 PRO 4355GE,3.5,35W +AMD,Zen 2,AMD Ryzen(TM) 3 PRO 4355G,3.8,65W +AMD,Zen 2,AMD Ryzen(TM) 3 PRO 4350GE,3.5,35W +AMD,Zen 2,AMD Ryzen(TM) 3 PRO 4350G,3.8,65W +AMD,Zen 2,AMD Ryzen(TM) 9 4900HS,3,35W +AMD,Zen 2,AMD Ryzen(TM) 9 4900H,3.3,35-54W +AMD,Zen 2,AMD Ryzen(TM) 7 4980U Microsoft Surface(R) Edition,2,15W +AMD,Zen 2,AMD Ryzen(TM) 7 4800U,1.8,15W +AMD,Zen 2,AMD Ryzen(TM) 7 4800HS,2.9,45W +AMD,Zen 2,AMD Ryzen(TM) 7 4800H,2.9,45W +AMD,Zen 2,AMD Ryzen(TM) 7 4700U,2,15W +AMD,Zen 2,AMD Ryzen(TM) 7 4700GE (OEM Only),3.1,35W +AMD,Zen 2,AMD Ryzen(TM) 7 4700G (OEM Only),3.6,65W +AMD,Zen 2,AMD Ryzen(TM) 5 4680U Microsoft Surface(R) Edition,2.2,15W +AMD,Zen 2,AMD Ryzen(TM) 5 4600U,2.1,15W +AMD,Zen 2,AMD Ryzen(TM) 5 4600H,3,45W +AMD,Zen 2,AMD Ryzen(TM) 5 4600GE (OEM Only),3.3,35W +AMD,Zen 2,AMD Ryzen(TM) 5 4600G,3.7,65W +AMD,Zen 2,AMD Ryzen(TM) 5 4500U,2.3,15W +AMD,Zen 2,AMD Ryzen(TM) 5 4500,3.6,65W +AMD,Zen 2,AMD Ryzen(TM) 3 4300U,2.7,15W +AMD,Zen 2,AMD Ryzen(TM) 3 4300GE (OEM Only),3.5,35W +AMD,Zen 2,AMD Ryzen(TM) 3 4300G (OEM Only),3.8,65W +AMD,Zen 2,AMD Ryzen(TM) 3 4100,3.8,65W +AMD,Zen 2,AMD Ryzen(TM) Threadripper(TM) PRO 3995WX,2.7,280W +AMD,Zen 2,AMD Ryzen(TM) Threadripper(TM) PRO 3975WX,3.5,280W +AMD,Zen 2,AMD Ryzen(TM) Threadripper(TM) PRO 3955WX,3.9,280W +AMD,Zen 2,AMD Ryzen(TM) Threadripper(TM) PRO 3945WX,4,280W +AMD,Zen 2,AMD Ryzen(TM) Threadripper(TM) 3990X,2.9,280W +AMD,Zen 2,AMD Ryzen(TM) Threadripper(TM) 3970X,3.7,280W +AMD,Zen 2,AMD Ryzen(TM) Threadripper(TM) 3960X,3.8,280W +AMD,Zen 2,AMD Ryzen(TM) 9 PRO 3900,3.1,65W +AMD,Zen 2,AMD Ryzen(TM) 7 PRO 3700U,2.3,15W +AMD,Zen 2,AMD Ryzen(TM) 7 PRO 3700,3.6,65W +AMD,Zen 2,AMD Ryzen(TM) 5 PRO 3600,3.6,65W +AMD,Zen 2,AMD Ryzen(TM) 5 PRO 3500U,2.1,15W +AMD,Zen 2,AMD Ryzen(TM) 5 PRO 3400GE,3.3,35W +AMD,Zen 2,AMD Ryzen(TM) 5 PRO 3400G,3.7,65W +AMD,Zen 2,AMD Ryzen(TM) 5 PRO 3350GE,3.3,35W +AMD,Zen 2,AMD Ryzen(TM) 5 PRO 3350G,3.6,65W +AMD,Zen 2,Zen +,2.1,15W +AMD,Zen 2,AMD Ryzen(TM) 3 PRO 3200GE,3.3,35W +AMD,Zen 2,AMD Ryzen(TM) 3 PRO 3200G,3.6,65W +AMD,Zen 2,AMD Ryzen(TM) 9 3950X,3.5,105W +AMD,Zen 2,AMD Ryzen(TM) 9 3900XT,3.8,105W +AMD,Zen 2,AMD Ryzen(TM) 9 3900X,3.8,105W +AMD,Zen 2,AMD Ryzen(TM) 9 3900 Processor (OEM Only),3.1,65W +AMD,Zen 2,AMD Ryzen(TM) 7 3800XT,3.9,105W +AMD,Zen 2,AMD Ryzen(TM) 7 3800X,3.9,105W +AMD,Zen 2,AMD Ryzen(TM) 7 3780U Microsoft Surface(R) Edition,2.3,15W +AMD,Zen 2,AMD Ryzen(TM) 7 3750H,2.3,35W +AMD,Zen 2,AMD Ryzen(TM) 7 3700X,3.6,65W +AMD,Zen 2,AMD Ryzen(TM) 7 3700U,2.3,15W +AMD,Zen 2,AMD Ryzen(TM) 7 3700C,2.3,15W +AMD,Zen 2,AMD Ryzen(TM) 5 3600XT,3.8,95W +AMD,Zen 2,AMD Ryzen(TM) 5 3600X,3.8,95W +AMD,Zen 2,AMD Ryzen(TM) 5 3600,3.6,65W +AMD,Zen +,AMD Ryzen(TM) 5 3580U Microsoft Surface(R) Edition,2.1,15W +AMD,Zen +,AMD Ryzen(TM) 5 3550H,2.1,35W +AMD,Zen +,AMD Ryzen(TM) 5 3500U,2.1,15W +AMD,Zen +,AMD Ryzen(TM) 5 3500C,2.1,15W +AMD,Zen 2,AMD Ryzen(TM) 5 3500 Processor (OEM Only),3.6,65W +AMD,Zen +,AMD Ryzen(TM) 5 3450U,2.1,15W +AMD,Zen +,AMD Ryzen(TM) 5 3400GE (OEM Only),3.3,35W +AMD,Zen +,AMD Ryzen(TM) 5 3400G with Radeon(TM) RX Vega 11 Graphics,3.7,65W +AMD,Zen +,AMD Ryzen(TM) 3 3350U,2.1,15W +AMD,Zen 2,AMD Ryzen(TM) 3 3300X,3.8,65W +AMD,Zen +,AMD Ryzen(TM) 3 3300U,2.1,15W +AMD,Zen,AMD Ryzen(TM) 3 3250U,2.6,15W +AMD,Zen,AMD Ryzen(TM) 3 3250C,2.6,15W +AMD,Zen,AMD Ryzen(TM) 3 3200U,2.6,15W +AMD,Zen +,AMD Ryzen(TM) 3 3200GE (OEM Only),3.3,35W +AMD,Zen +,AMD Ryzen(TM) 3 3200G with Radeon(TM) Vega 8 Graphics,3.6,65W +AMD,zen 2,AMD Ryzen(TM) 3 3100,3.6,65W +AMD,Zen +,AMD Athlon(TM) Gold PRO 3150GE,3.3,35W +AMD,Zen +,AMD Athlon(TM) Gold PRO 3150G,3.5,65W +AMD,Zen +,AMD Athlon(TM) Silver PRO 3125GE,3.4,35W +AMD,Zen +,AMD Athlon(TM) Gold 3150GE (OEM Only),3.3,35W +AMD,Zen +,AMD Athlon(TM) Gold 3150G (OEM Only),3.5,65W +AMD,Zen,AMD Athlon(TM) Silver 3050U,2.3,15W +AMD,Zen +,AMD Athlon(TM) Silver 3050GE (OEM Only),3.4,35W +AMD,Zen,AMD Athlon(TM) 3000G,3.5,35W +AMD,Zen,AMD Athlon(TM) 300U,2.4,15W +AMD,Zen,AMD Athlon(TM) PRO 200U Mobile Processor with Radeon(TM) Vega 3 Graphics,2.3,15W +AMD,Zen,AMD Athlon(TM) PRO 200GE,3.2,35W +AMD,Zen,AMD Athlon(TM) 240GE,3.5,35W +AMD,Zen,AMD Athlon(TM) 220GE,3.4,35W +AMD,Zen,AMD Athlon(TM) 200GE,3.2,35W +AMD,Zen +,AMD Ryzen(TM) Threadripper(TM) 2990WX,3,250W +AMD,Zen +,AMD Ryzen(TM) Threadripper(TM) 2970WX,3,250W +AMD,Zen +,AMD Ryzen(TM) Threadripper(TM) 2950X,3.5,180W +AMD,Zen +,AMD Ryzen(TM) Threadripper(TM) 2920X,3.5,180W +AMD,Zen +,AMD Ryzen(TM) 7 PRO 2700X,3.6,95W +AMD,Zen,AMD Ryzen(TM) 7 PRO 2700U,2.2,15W +AMD,Zen +,AMD Ryzen(TM) 7 PRO 2700,3.2,65W +AMD,Zen,AMD Ryzen(TM) 5 PRO 2600,3.4,65W +AMD,Zen,AMD Ryzen(TM) 5 PRO 2500U,2,15W +AMD,Zen,AMD Ryzen(TM) 5 PRO 2400GE with Radeon(TM) Vega 11 Graphics,3.2,35W +AMD,Zen,AMD Ryzen(TM) 5 PRO 2400G with Radeon(TM) Vega 11 Graphics,3.6,65W +AMD,Zen,AMD Ryzen(TM) 3 PRO 2300U,2,15W +AMD,Zen,AMD Ryzen(TM) 3 PRO 2200GE with Radeon(TM) Vega 8 Graphics,3.2,35W +AMD,Zen,AMD Ryzen(TM) 3 PRO 2200G with Radeon(TM) Vega 8 Graphics,3.5,65W +AMD,Zen,AMD Ryzen(TM) 7 2800H,3.3,45W +AMD,Zen +,AMD Ryzen(TM) 7 2700X,3.7,105W +AMD,Zen,AMD Ryzen(TM) 7 2700U,2.2,15W +AMD,Zen +,AMD Ryzen(TM) 7 2700E Processor,2.8,45W +AMD,Zen +,AMD Ryzen(TM) 7 2700,3.2,65W +AMD,Zen +,AMD Ryzen(TM) 5 2600X,3.6,95W +AMD,Zen,AMD Ryzen(TM) 5 2600H,3.2,45W +AMD,Zen +,AMD Ryzen(TM) 5 2600E,3.1,45W +AMD,Zen +,AMD Ryzen(TM) 5 2600,3.4,65W +AMD,Zen +,AMD Ryzen(TM) 5 2500X,3.6,65W +AMD,Zen,AMD Ryzen(TM) 5 2500U,2,15W +AMD,Zen,AMD Ryzen(TM) 5 2400GE with Radeon(TM) RX Vega 11 Graphics,3.2,35W +AMD,Zen,AMD Ryzen(TM) 5 2400G with Radeon(TM) RX Vega 11 Graphics,3.6,65W +AMD,Zen,AMD Ryzen(TM) 3 2300X,3.5,65W +AMD,Zen,AMD Ryzen(TM) 3 2300U,2,15W +AMD,Zen,AMD Ryzen(TM) 3 2200U,2.5,15W +AMD,Zen,AMD Ryzen(TM) 3 2200GE with Radeon(TM) Vega 8 Graphics,3.2,35W +AMD,Zen,AMD Ryzen(TM) 3 2200G with Radeon(TM) Vega 8 Graphics,3.5,65W +AMD,Zen,AMD Ryzen(TM) Threadripper(TM) 1950X,3.4,180W +AMD,Zen,AMD Ryzen(TM) Threadripper(TM) 1920X,3.5,180W +AMD,Zen,AMD Ryzen(TM) Threadripper(TM) 1900X,3.8,180W +AMD,Zen,AMD Ryzen(TM) 7 PRO 1700X Processor,3.4,95W +AMD,Zen,AMD Ryzen(TM) 7 PRO 1700,3,65W +AMD,Zen,AMD Ryzen(TM) 5 PRO 1600,3.2,65W +AMD,Zen,AMD Ryzen(TM) 5 PRO 1500,3.5,65W +AMD,Zen,AMD Ryzen(TM) 3 PRO 1300,3.5,65W +AMD,Zen,AMD Ryzen(TM) 3 PRO 1200,3.1,65W +AMD,Zen,AMD Ryzen(TM) 7 1800X,3.6,95W +AMD,Zen,AMD Ryzen(TM) 7 1700X,3.4,95W +AMD,Zen,AMD Ryzen(TM) 7 1700 Processor,3,65W +AMD,Zen,AMD Ryzen(TM) 5 1600X,3.6,95W +AMD,Zen +,AMD Ryzen(TM) 5 1600 (AF),3.2,65W +AMD,Zen,AMD Ryzen(TM) 5 1600,3.2,65W +AMD,Zen,AMD Ryzen(TM) 5 1500X,3.5,65W +AMD,Zen,AMD Ryzen(TM) 5 1400,3.2,65W +AMD,Zen,AMD Ryzen(TM) 3 1300X,3.5,65W +AMD,Zen,AMD Ryzen(TM) 3 1200,3.1,65W +AMD,Not out yet,AMD EPYC(TM) 9965,2.25 ,500W +AMD,Not out yet,AMD EPYC(TM) 9845,2.1,390W +AMD,Not out yet,AMD EPYC(TM) 9825,2.2,390W +AMD,Not out yet,AMD EPYC(TM) 9755,2.7,500W +AMD,Not out yet,AMD EPYC(TM) 9745,2.4,400W +AMD,Not out yet,AMD EPYC(TM) 9655P,2.6,400W +AMD,Not out yet,AMD EPYC(TM) 9655,2.6,400W +AMD,Not out yet,AMD EPYC(TM) 9645,2.3,320W +AMD,Not out yet,AMD EPYC(TM) 9575F,3.3,400W +AMD,Not out yet,AMD EPYC(TM) 9565,3.15 ,400W +AMD,Not out yet,AMD EPYC(TM) 9555P,3.2,360W +AMD,Not out yet,AMD EPYC(TM) 9555,3.2,360W +AMD,Not out yet,AMD EPYC(TM) 9535,2.4,300W +AMD,Not out yet,AMD EPYC(TM) 9475F,3.65 ,400W +AMD,Not out yet,AMD EPYC(TM) 9455P,3.15 ,300W +AMD,Not out yet,AMD EPYC(TM) 9455,3.15 ,300W +AMD,Not out yet,AMD EPYC(TM) 9375F,3.85 ,320W +AMD,Not out yet,AMD EPYC(TM) 9365,3.4,300W +AMD,Not out yet,AMD EPYC(TM) 9355P,3.55 ,280W +AMD,Not out yet,AMD EPYC(TM) 9355,3.55 ,280W +AMD,Not out yet,AMD EPYC(TM) 9335,3,210W +AMD,Not out yet,AMD EPYC(TM) 9275F,4.1,320W +AMD,Not out yet,AMD EPYC(TM) 9255,3.25 ,200W +AMD,Not out yet,AMD EPYC(TM) 9175F,4.2,320W +AMD,Not out yet,AMD EPYC(TM) 9135,3.65 ,200W +AMD,Not out yet,AMD EPYC(TM) 9115,2.6,125W +AMD,Not out yet,AMD EPYC(TM) 9015,3.6,125W +AMD,Zen 4,AMD EPYC(TM) 9754S,2.25 ,360W +AMD,Zen 4,AMD EPYC(TM) 9754,2.25 ,360W +AMD,Zen 4,AMD EPYC(TM) 9734,2.2,340W +AMD,Zen 4,AMD EPYC(TM) 9684X,2.55 ,400W +AMD,Zen 4,AMD EPYC(TM) 9654P,2.4,360W +AMD,Zen 4,AMD EPYC(TM) 9654,2.4,360W +AMD,Zen 4,AMD EPYC(TM) 9634,2.25 ,290W +AMD,Zen 4,AMD EPYC(TM) 9554P,3.1,360W +AMD,Zen 4,AMD EPYC(TM) 9554,3.1,360W +AMD,Zen 4,AMD EPYC(TM) 9534,2.45 ,280W +AMD,Zen 4,AMD EPYC(TM) 9474F,3.6,360W +AMD,Zen 4,AMD EPYC(TM) 9454P,2.75 ,290W +AMD,Zen 4,AMD EPYC(TM) 9454,2.75 ,290W +AMD,Zen 4,AMD EPYC(TM) 9384X,3.1,320W +AMD,Zen 4,AMD EPYC(TM) 9374F,3.85 ,320W +AMD,Zen 4,AMD EPYC(TM) 9354P,3.25 ,280W +AMD,Zen 4,AMD EPYC(TM) 9354,3.25 ,280W +AMD,Zen 4,AMD EPYC(TM) 9334,2.7,210W +AMD,Zen 4,AMD EPYC(TM) 9274F,04.05,320W +AMD,Zen 4,AMD EPYC(TM) 9254,2.9,200W +AMD,Zen 4,AMD EPYC(TM) 9224,2.5,200W +AMD,Zen 4,AMD EPYC(TM) 9184X,3.55 ,320W +AMD,Zen 4,AMD EPYC(TM) 9174F,4.1,320W +AMD,Zen 4,AMD EPYC(TM) 9124,3,200W +AMD,Zen 4,AMD EPYC(TM) 8534PN,2,175W +AMD,Zen 4,AMD EPYC(TM) 8534P,2.3,200W +AMD,Zen 4,AMD EPYC(TM) 8434PN,2,155W +AMD,Zen 4,AMD EPYC(TM) 8434P,2.5,200W +AMD,Zen 4,AMD EPYC(TM) 8324PN,02.05,130W +AMD,Zen 4,AMD EPYC(TM) 8324P,2.65 ,180W +AMD,Zen 4,AMD EPYC(TM) 8224PN,2,120W +AMD,Zen 4,AMD EPYC(TM) 8224P,2.55 ,160W +AMD,Zen 4,AMD EPYC(TM) 8124PN,2,100W +AMD,Zen 4,AMD EPYC(TM) 8124P,2.45 ,125W +AMD,Zen 4,AMD EPYC(TM) 8024PN,02.05,80W +AMD,Zen 4,AMD EPYC(TM) 8024P,2.4,90W +AMD,Zen 3,AMD EPYC(TM) 7203P,2.8,120W +AMD,Zen 3,AMD EPYC(TM) 7203,2.8,120W +AMD,Zen 3,AMD EPYC(TM) 7303P,2.4,130W +AMD,Zen 3,AMD EPYC(TM) 7303,2.4,130W +AMD,Zen 3,AMD EPYC(TM) 7643P,2.3,225W +AMD,Zen 3,AMD EPYC(TM) 7773X,2.2,280W +AMD,Zen 3,AMD EPYC(TM) 7763,2.45 ,280W +AMD,Zen 3,AMD EPYC(TM) 7713P,2,225W +AMD,Zen 3,AMD EPYC(TM) 7713,2,225W +AMD,Zen 3,AMD EPYC(TM) 7663,2,240W +AMD,Zen 3,AMD EPYC(TM) 7643,2.3,225W +AMD,Zen 3,AMD EPYC(TM) 7663P,2,240W +AMD,Zen 3,AMD EPYC(TM) 75F3,2.95 ,280W +AMD,Zen 3,AMD EPYC(TM) 7573X,2.8,280W +AMD,Zen 3,AMD EPYC(TM) 7543P,2.8,225W +AMD,Zen 3,AMD EPYC(TM) 7543,2.8,225W +AMD,Zen 3,AMD EPYC(TM) 7513,2.6,200W +AMD,Zen 3,AMD EPYC(TM) 74F3,3.2,240W +AMD,Zen 3,AMD EPYC(TM) 7473X,2.8,240W +AMD,Zen 3,AMD EPYC(TM) 7453,2.75 ,225W +AMD,Zen 3,AMD EPYC(TM) 7443P,2.85 ,200W +AMD,Zen 3,AMD EPYC(TM) 7443,2.85 ,200W +AMD,Zen 3,AMD EPYC(TM) 7413,2.65 ,180W +AMD,Zen 3,AMD EPYC(TM) 73F3,3.5,240W +AMD,Zen 3,AMD EPYC(TM) 7373X,03.05,240W +AMD,Zen 3,AMD EPYC(TM) 7343,3.2,190W +AMD,Zen 3,AMD EPYC(TM) 7313P,3,155W +AMD,Zen 3,AMD EPYC(TM) 7313,3,155W +AMD,Zen 3,AMD EPYC(TM) 72F3,3.7,180W +AMD,Zen 2,AMD EPYC(TM) 7H12,2.6,280W +AMD,Zen 2,AMD EPYC(TM) 7F72,3.2,240W +AMD,Zen 2,AMD EPYC(TM) 7F52,3.5,240W +AMD,Zen 2,AMD EPYC(TM) 7F32,3.7,180W +AMD,Zen 2,AMD EPYC(TM) 7742,2.25 ,225W +AMD,Zen 2,AMD EPYC(TM) 7702P,2,200W +AMD,Zen 2,AMD EPYC(TM) 7702,2,200W +AMD,Zen 2,AMD EPYC(TM) 7662,2,225W +AMD,Zen 2,AMD EPYC(TM) 7642,2.3,225W +AMD,Zen 2,AMD EPYC(TM) 7552,2.2,200W +AMD,Zen 2,AMD EPYC(TM) 7542,2.9,225W +AMD,Zen 2,AMD EPYC(TM) 7532,2.4,200W +AMD,Zen 2,AMD EPYC(TM) 7502P,2.5,180W +AMD,Zen 2,AMD EPYC(TM) 7502,2.5,180W +AMD,Zen 2,AMD EPYC(TM) 7452,2.35 ,155W +AMD,Zen 2,AMD EPYC(TM) 7402P,2.8,180W +AMD,Zen 2,AMD EPYC(TM) 7402,2.8,180W +AMD,Zen 2,AMD EPYC(TM) 7352,2.3,155W +AMD,Zen 2,AMD EPYC(TM) 7302P,3,155W +AMD,Zen 2,AMD EPYC(TM) 7302,3,155W +AMD,Zen 2,AMD EPYC(TM) 7282,2.8,120W +AMD,Zen 2,AMD EPYC(TM) 7272,2.9,120W +AMD,Zen 2,AMD EPYC(TM) 7262,3.2,155W +AMD,Zen 2,AMD EPYC(TM) 7252,3.1,120W +AMD,Zen 2,AMD EPYC(TM) 7232P,3.1,120W +AMD,Zen,AMD EPYC(TM) 7601,2.2,180W +AMD,Zen,AMD EPYC(TM) 7551P,2,180W +AMD,Zen,AMD EPYC(TM) 7551,2,180W +AMD,Zen,AMD EPYC(TM) 7501,2,170W +AMD,Zen,AMD EPYC(TM) 7451,2.3,180W +AMD,Zen,AMD EPYC(TM) 7401P,2,170W +AMD,Zen,AMD EPYC(TM) 7401,2,170W +AMD,Zen,AMD EPYC(TM) 7371,3.1,200W +AMD,Zen,AMD EPYC(TM) 7351P,2.4,170W +AMD,Zen,AMD EPYC(TM) 7351,2.4,170W +AMD,Zen,AMD EPYC(TM) 7301,2.2,170W +AMD,Zen,AMD EPYC(TM) 7281,2.1,170W +AMD,Zen,AMD EPYC(TM) 7261,2.5,170W +AMD,Zen,AMD EPYC(TM) 7251,2.1,120W +AMD,Zen 4,AMD EPYC(TM) 4584PX,4.2,120W +AMD,Zen 4,AMD EPYC(TM) 4564P,4.5,170W +AMD,Zen 4,AMD EPYC(TM) 4484PX,4.4,120W +AMD,Zen 4,AMD EPYC(TM) 4464P,3.7,65W +AMD,Zen 4,AMD EPYC(TM) 4364P,4.5,105W +AMD,Zen 4,AMD EPYC(TM) 4344P,3.8,65W +AMD,Zen 4,AMD EPYC(TM) 4244P,3.8,65W +AMD,Zen 4,AMD EPYC(TM) 4124P,3.8,65W +AMD,Zen 4,AMD EPYC(TM) Embedded 9654P,2.4,360W +AMD,Zen 4,AMD EPYC(TM) Embedded 9654,2.4,360W +AMD,Zen 4,AMD EPYC(TM) Embedded 9554P,3.1,360W +AMD,Zen 4,AMD EPYC(TM) Embedded 9554,3.1,360W +AMD,Zen 4,AMD EPYC(TM) Embedded 9534,2.45 ,280W +AMD,Zen 4,AMD EPYC(TM) Embedded 9454P,2.75 ,290W +AMD,Zen 4,AMD EPYC(TM) Embedded 9454,2.75 ,290W +AMD,Zen 4,AMD EPYC(TM) Embedded 9354P,3.25 ,280W +AMD,Zen 4,AMD EPYC(TM) Embedded 9354,3.25 ,280W +AMD,Zen 4,AMD EPYC(TM) Embedded 9254,2.9,200W +AMD,Zen 4,AMD EPYC(TM) Embedded 9124,3,200W +AMD,Zen 4c,AMD EPYC(TM) Embedded 8534P,2.3,200W +AMD,Zen 4c,AMD EPYC(TM) Embedded 8434P,2.5,200W +AMD,Zen 4,AMD Ryzen(TM) Embedded 8845HS,3.8,35-54W +AMD,Zen 4c,AMD EPYC(TM) Embedded 8324P,2.65 ,180W +AMD,Zen 4,AMD Ryzen(TM) Embedded 8840U,3.3,15-30W +AMD,Zen 4c,AMD EPYC(TM) Embedded 8224P,2.55 ,180W +AMD,Zen 4,AMD Ryzen(TM) Embedded 8645HS,4.3,35-54W +AMD,Zen 4c,AMD EPYC(TM) Embedded 8124P,2.45 ,125W +AMD,Zen 4,AMD Ryzen(TM) Embedded 8640U,3.5,15-30W +AMD,Zen 4c,AMD EPYC(TM) Embedded 8C24P,2.45 ,100W +AMD,Zen 4,AMD Ryzen(TM) Embedded 7945,3.7,65W +AMD,Zen 4,AMD Ryzen(TM) Embedded 7745,3.8,65W +AMD,Zen 4,AMD Ryzen(TM) Embedded 7700X,4.5,105W +AMD,Zen 4,AMD Ryzen(TM) Embedded 7645,3.8,65W +AMD,Zen 4,AMD Ryzen(TM) Embedded 7600X,4.7,105W +AMD,Zen 3,AMD EPYC(TM) Embedded 7713P,2,225W +AMD,Zen 3,AMD EPYC(TM) Embedded 7713,2,225W +AMD,Zen 3,AMD EPYC(TM) Embedded 7643,2.3,225W +AMD,Zen 3,AMD EPYC(TM) Embedded 7543P,2.8,225W +AMD,Zen 3,AMD EPYC(TM) Embedded 7543,2.8,225W +AMD,Zen 3,AMD EPYC(TM) Embedded 7443P,2.85 ,200W +AMD,Zen 3,AMD EPYC(TM) Embedded 7443,2.85 ,200W +AMD,Zen 3,AMD EPYC(TM) Embedded 7413,2.65 ,180W +AMD,Zen 3,AMD EPYC(TM) Embedded 7313P,3,155W +AMD,Zen 3,AMD EPYC(TM) Embedded 7313,3,155W +AMD,Zen 3,AMD Ryzen(TM) Embedded 5950E Processor,03.05,105W +AMD,Zen 3,AMD Ryzen(TM) Embedded 5900E Processor,3.35 ,105W +AMD,Zen 3,AMD Ryzen(TM) Embedded 5800E Processor,3.4,65-100W +AMD,Zen 3,AMD Ryzen(TM) Embedded 5600E Processor,3.3,65W +AMD,Zen 3,AMD Ryzen(TM) Embedded V3C48,3.3,45W +AMD,Zen 3,AMD Ryzen(TM) Embedded V3C44,3.5,45W +AMD,Zen 3,AMD Ryzen(TM) Embedded V3C18I,1.9,15W +AMD,Zen 3,AMD Ryzen(TM) Embedded V3C16,2,15W +AMD,Zen 3,AMD Ryzen(TM) Embedded V3C14,2.3,15W +AMD,Zen 2,AMD EPYC(TM) Embedded 7742,2.25 ,225W +AMD,Zen 2,AMD EPYC(TM) Embedded 7662,2,225W +AMD,Zen 2,AMD EPYC(TM) Embedded 7642,2.3,225W +AMD,Zen 2,AMD EPYC(TM) Embedded 7552,2.2,200W +AMD,Zen 2,AMD EPYC(TM) Embedded 7542,2.9,225W +AMD,Zen 2,AMD EPYC(TM) Embedded 7502P,2.5,180W +AMD,Zen 2,AMD EPYC(TM) Embedded 7502,2.5,180W +AMD,Zen 2,AMD EPYC(TM) Embedded 7452,2.35 ,155W +AMD,Zen 2,AMD EPYC(TM) Embedded 7402P,2.8,180W +AMD,Zen 2,AMD EPYC(TM) Embedded 7402,2.8,180W +AMD,Zen 2,AMD EPYC(TM) Embedded 7352,2.3,155W +AMD,Zen 2,AMD EPYC(TM) Embedded 7302P,3,155W +AMD,Zen 2,AMD EPYC(TM) Embedded 7302,3,155W +AMD,Zen 2,AMD EPYC(TM) Embedded 7282,2.8,120W +AMD,Zen 2,AMD EPYC(TM) Embedded 7272,2.9,120W +AMD,Zen 2,AMD EPYC(TM) Embedded 7262,3.2,155W +AMD,Zen 2,AMD EPYC(TM) Embedded 7252,3.1,120W +AMD,Zen 2,AMD EPYC(TM) Embedded 7232P,3.1,120W +AMD,Zen 2,AMD Ryzen(TM) Embedded V2A46 with Radeon(TM) Graphics,3,35-54W +AMD,Zen 2,AMD Ryzen(TM) Embedded V2748 with Radeon(TM) Graphics,2.9,35-54W +AMD,Zen 2,AMD Ryzen(TM) Embedded V2718 with Radeon(TM) Graphics,1.7,10-25W +AMD,Zen 2,AMD Ryzen(TM) Embedded V2546 with Radeon(TM) Graphics,3,35-54W +AMD,Zen 2,AMD Ryzen(TM) Embedded V2516 with Radeon(TM) Graphics,2.1,10-25W +AMD,Zen+,AMD Ryzen(TM) Embedded R2314 with Radeon(TM) Graphics,2.1,12-35W +AMD,Zen+,AMD Ryzen(TM) Embedded R2312 with Radeon(TM) Graphics,2.7,12-25W +AMD,Zen,AMD EPYC(TM) Embedded 7601,2.2,180W +AMD,Zen,AMD EPYC(TM) Embedded 755P,2,180W +AMD,Zen,AMD EPYC(TM) Embedded 7551,2,180W +AMD,Zen,AMD EPYC(TM) Embedded 7501,2,155W +AMD,Zen,AMD EPYC(TM) Embedded 7451,2.3,180W +AMD,Zen,AMD EPYC(TM) Embedded 740P,2,155W +AMD,Zen,AMD EPYC(TM) Embedded 7401,2,155W +AMD,Zen,AMD EPYC(TM) Embedded 7371,3.1,200W +AMD,Zen,AMD EPYC(TM) Embedded 735P,2.4,155W +AMD,Zen,AMD EPYC(TM) Embedded 7351,2.4,155W +AMD,Zen,AMD EPYC(TM) Embedded 7301,2.2,155W +AMD,Zen,AMD EPYC(TM) Embedded 7281,2.1,155W +AMD,Zen,AMD EPYC(TM) Embedded 7261,2.5,155W +AMD,Zen,AMD EPYC(TM) Embedded 7251,2.1,120W +AMD,Zen,AMD EPYC(TM) Embedded 3451,2.14 ,80-100W +AMD,Zen,AMD EPYC(TM) Embedded 3351,1.9,60-80W +AMD,Zen,AMD EPYC(TM) Embedded 3255,2.5,25-55W +AMD,Zen,AMD EPYC(TM) Embedded 3251,2.5,55W +AMD,Zen,AMD EPYC(TM) Embedded 3201,1.5,30W +AMD,Zen,AMD EPYC(TM) Embedded 3151,2.7,45W +AMD,Zen,AMD EPYC(TM) Embedded 3101,2.1,35W +AMD,Zen,AMD Ryzen(TM) Embedded V1807B with Radeon(TM) Vega 11 Graphics,3.35 ,35-54W +AMD,Zen,AMD Ryzen(TM) Embedded V1780B,3.35 ,35-54W +AMD,Zen,AMD Ryzen(TM) Embedded V1756B with Radeon(TM) Vega 8 Graphics,3.25 ,35-54W +AMD,Zen,AMD Ryzen(TM) Embedded V1605B with Radeon(TM) Vega 8 Graphics,2,12-25W +AMD,Zen,AMD Ryzen(TM) Embedded V1500B,2.2,12-25W +AMD,Zen,AMD Ryzen(TM) Embedded V1202B with Radeon(TM) Vega 3 Graphics,2.3,12-25W +AMD,Zen,AMD Ryzen(TM) Embedded R1606G with Radeon(TM) Vega 3 Graphics,2.6,12-25W +AMD,Zen,AMD Ryzen(TM) Embedded R1600,2.6,12--25W +AMD,Zen,AMD Ryzen(TM) Embedded R1505G with Radeon(TM) Vega 3 Graphics,2.4,12-25W +AMD,Zen,AMD Ryzen(TM) Embedded R1305G with Radeon(TM) Vega 3 Graphics,1.5,8-10W +AMD,Zen,AMD Ryzen(TM) Embedded R1102G with Radeon(TM) Vega 3 Graphics,1.2,6W \ No newline at end of file diff --git a/docs/script/getting_started/formula/smartwatts-mongodb-csv.json b/docs/script/getting_started/formula/smartwatts-mongodb-csv.json index cefbd37..73c6e24 100644 --- a/docs/script/getting_started/formula/smartwatts-mongodb-csv.json +++ b/docs/script/getting_started/formula/smartwatts-mongodb-csv.json @@ -2,25 +2,26 @@ "verbose": false, "stream": true, "input": { - "puller_mongodb": { - "model": "HWPCReport", - "type": "mongodb", - "name": "puller_mongodb", - "uri": "mongodb://mongodb:27017", - "db": "db_sensor", - "collection": "prep" - } + "puller_mongodb": { + "model": "HWPCReport", + "type": "mongodb", + "name": "puller_mongodb", + "uri": "mongodb://mongodb:27017", + "db": "db_sensor", + "collection": "prep" + } }, "output": { - "pusher_csv": { - "model": "PowerReport", - "type": "csv", - "name": "pusher_csv", - "directory": "/tmp/csv" - } + "pusher_csv": { + "model": "PowerReport", + "type": "csv", + "name": "pusher_csv", + "directory": "/tmp/csv" + } }, "cpu-base-freq": 1900, "cpu-error-threshold": 2.0, "disable-dram-formula": true, - "sensor-reports-frequency": 1000 - } + "sensor-reports-frequency": 1000, + "cpu-tdp": 15 +} \ No newline at end of file diff --git a/docs/script/getting_started/sensor/hwpc-mongodb.json b/docs/script/getting_started/sensor/hwpc-mongodb.json index 4ee5f0b..7e2a318 100644 --- a/docs/script/getting_started/sensor/hwpc-mongodb.json +++ b/docs/script/getting_started/sensor/hwpc-mongodb.json @@ -1,13 +1,12 @@ { "name": "sensor", "verbose": true, - "frequency": 1000, - "cgroup_basepath": "/sys/fs/cgroup/", + "frequency": 500, "output": { "type": "mongodb", - "uri": "mongodb://mongodb:27017", + "uri": "mongodb://127.0.0.1", "database": "db_sensor", - "collection": "prep" + "collection": "report_0" }, "system": { "rapl": { @@ -33,5 +32,6 @@ "INSTRUCTIONS_RETIRED" ] } - } + }, + "cgroup_basepath": "/sys/fs/cgroup/" } \ No newline at end of file diff --git a/docs/script/getting_started/start.py b/docs/script/getting_started/start.py index 4f4b4a9..9181e06 100644 --- a/docs/script/getting_started/start.py +++ b/docs/script/getting_started/start.py @@ -26,6 +26,9 @@ # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +import re + +import csv import os import sys import signal @@ -37,75 +40,105 @@ # If an arch is added, the case statement in the # start_demo function should be updated accordingly with the proper core events # https://powerapi.org/reference/sensors/hwpc-sensor/ -list_arch = ["0 - Intel Sandy Bridge, Comet Lake", - "1 - Intel Skylake, Whiskey Lake, Coffee Lake", - "2 - AMD Zen 2", - "3 - AMD Zen 3"] +arch_tab = [["Sandy bridge", "Ivy bridge", "Haswell", "Broadwell", "Comet lake"], + ["Skylake", "Cascade lake", "Kaby Lake R", "Kaby Lake", "Coffee Lake", "Amber Lake", "Rocket lake", "Whiskey lake"], + ["Zen", "Zen+", "Zen 2"], + ["Zen 3", "Zen 4"]] + def signal_handler(sig, frame): print('You sent SIGINT signal, stoping docker compose stack') call("./stop.sh") +def load_data(): + """ + Load CSV files from the specified directory and return the data as a list of dictionaries. + """ + data = [] + with open("./cpu.csv", mode='r', newline='', encoding='UTF-8') as f: + data.extend(csv.DictReader(f)) + return data + + +def find_cpu(data): + """ + Find the cpu in the list of compatible cpu + """ + option = [] + line = "cat /proc/cpuinfo | grep 'model name'" + result = subprocess.check_output(line, shell=True, text=True).split("\n") + print(result[0]) + parse = parse_processor_name(result[0]) + print(parse) + for row in data : + if parse[0] in row["Name"]: + option.append(row) + + if len(option) == 0: + print("It looks like you cpu is not supported by power API") + sys.exit() + elif len(option) == 1: + print("Your cpu is supported by power API") + cpu = option[0] + else: + print("Please select your cpu from this list") + for i in range(len(option)): + print(str(i) + " - " + option[i]["Name"]) + choice = int(input()) + print("You have selected : " + option[choice]["Name"]) + cpu = option[choice] + return cpu + + +def parse_processor_name(name): + if "Intel" in name: + brand = "Intel" + elif "AMD" in name: + brand = "AMD" + else : + brand = "Unknown" + id_pattern = r"\b\d{3,4}[A-Z0-9]*\b" + + id_res = re.search(id_pattern, name) + + return id_res.group(), brand + + def start_demo(): """ Start the demo by selecting the processor architecture this will update the sensor configuration file """ - print("Enter the number associated with your processor architecture, " - "please note that the sensor isn't available " - "for Intel Tiger Lake and newer: \n" + list_arch[0] + - "\n" + list_arch[1] + - "\n" + list_arch[2] + - "\n" + list_arch[3] + - "\n") - - signal.signal(signal.SIGINT, signal_handler) - val = "" - choice = True - while choice: - try: - val = input() - val = int(val) - if val < 0 or val >= len(list_arch): - print("Invalid input, please enter a valid number or exit") - else: - choice = False - except ValueError: - if val == "exit": - print("Exiting...") - sys.exit() - else: - print("Invalid input, please enter a valid number or exit") - - print("You have selected: " + list_arch[val] + "\n") + cpu = find_cpu(load_data()) + print(cpu) # Update core events in the sensor configuration # file based on the selected processor architecture with open('sensor/hwpc-mongodb.json', encoding='UTF-8') as f: data = json.load(f) - if val == 0: + if cpu["Family"] in arch_tab[0]: data['container']['core']['events'] = [ "CPU_CLK_UNHALTED:REF_P", "CPU_CLK_UNHALTED:THREAD_P", "LLC_MISSES", "INSTRUCTIONS_RETIRED" ] - elif val == 1: + elif cpu["Family"] in arch_tab[1]: data['container']['core']['events'] = [ "CPU_CLK_THREAD_UNHALTED:REF_P", "CPU_CLK_THREAD_UNHALTED:THREAD_P", "LLC_MISSES", "INSTRUCTIONS_RETIRED" ] - elif val == 2: + elif cpu["Family"] in arch_tab[2]: data['container']['core']['events'] = [ "CYCLES_NOT_IN_HALT", "RETIRED_INSTRUCTIONS", "RETIRED_UOPS" ] - elif val == 3: + elif cpu["Family"] in arch_tab[3]: data['container']['core']['events'] = [ "CYCLES_NOT_IN_HALT", "RETIRED_INSTRUCTIONS", @@ -129,6 +162,19 @@ def start_demo(): with open('sensor/hwpc-mongodb.json', 'w', encoding='UTF-8') as f: json.dump(data, f, indent=4) + # Update parameters in the formula configuration + with open('formula/smartwatts-mongodb-csv.json', encoding='UTF-8') as f: + formula_config = json.load(f) + + if cpu["Base frequency"] != '': + formula_config["cpu-base-freq"] = int(float(cpu["Base frequency"])*1000) + + if cpu["TDP"] != '': + formula_config["cpu-tdp"] = int(cpu["TDP"][:-1]) + + with open('formula/smartwatts-mongodb-csv.json', 'w', encoding='UTF-8') as f: + json.dump(formula_config, f, indent=4) + print("Starting the demo...") print("The demo will run for approximately 2 minutes\n") From 707e7275e17753257981dffe23c765d3923fa442 Mon Sep 17 00:00:00 2001 From: chachignot Date: Tue, 29 Oct 2024 15:36:08 +0100 Subject: [PATCH 035/108] docs(getting-started): Removed start.sh a,d stop.sh Removed start.sh and stop.sh and adapte start.py --- docs/script/getting_started/start.py | 63 +++++++++++++++++++++------- docs/script/getting_started/start.sh | 11 ----- docs/script/getting_started/stop.sh | 5 --- 3 files changed, 49 insertions(+), 30 deletions(-) delete mode 100755 docs/script/getting_started/start.sh delete mode 100755 docs/script/getting_started/stop.sh diff --git a/docs/script/getting_started/start.py b/docs/script/getting_started/start.py index 9181e06..dd90c81 100644 --- a/docs/script/getting_started/start.py +++ b/docs/script/getting_started/start.py @@ -26,8 +26,8 @@ # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import re +import re import csv import os import sys @@ -51,6 +51,20 @@ def signal_handler(sig, frame): call("./stop.sh") +def docker_start(time): + os.system("docker-compose up -d") + os.system("docker compose logs sensor -f &") + os.system("docker compose logs formula -f &") + os.system("sleep " + str(time)) + docker_stop() + + +def docker_stop(): + os.system("set -ueo pipefail") + os.system("set +x") + os.system("docker-compose down") + + def load_data(): """ Load CSV files from the specified directory and return the data as a list of dictionaries. @@ -68,21 +82,20 @@ def find_cpu(data): option = [] line = "cat /proc/cpuinfo | grep 'model name'" result = subprocess.check_output(line, shell=True, text=True).split("\n") - print(result[0]) + print("The CPU found is" + result[0].split(":")[1] ) parse = parse_processor_name(result[0]) - print(parse) for row in data : - if parse[0] in row["Name"]: + if parse[0] in row["Name"] and row["Manufacturer"] == parse[1]: option.append(row) if len(option) == 0: - print("It looks like you cpu is not supported by power API") + print("It looks like you cpu is not supported by PowerAPI") sys.exit() elif len(option) == 1: - print("Your cpu is supported by power API") + print("Your CPU should be supported by PowerAPI") cpu = option[0] else: - print("Please select your cpu from this list") + print("Please select your CPU from this list") for i in range(len(option)): print(str(i) + " - " + option[i]["Name"]) choice = int(input()) @@ -96,7 +109,7 @@ def parse_processor_name(name): brand = "Intel" elif "AMD" in name: brand = "AMD" - else : + else: brand = "Unknown" id_pattern = r"\b\d{3,4}[A-Z0-9]*\b" @@ -110,9 +123,12 @@ def start_demo(): Start the demo by selecting the processor architecture this will update the sensor configuration file """ + print("PowerAPI demo") + print("=" * 80) cpu = find_cpu(load_data()) - print(cpu) + print("\nSetting up configuration files...") + print("-" * 80) # Update core events in the sensor configuration # file based on the selected processor architecture with open('sensor/hwpc-mongodb.json', encoding='UTF-8') as f: @@ -151,7 +167,6 @@ def start_demo(): # the sensor configuration file accordingly cgroup = subprocess.run(["stat", "-fc", "%T", "/sys/fs/cgroup/"], text=True, capture_output=True, check=True) - print(cgroup.stdout) if cgroup.stdout == "cgroup2fs\n": data["cgroup_basepath"] = "/sys/fs/cgroup/" else: @@ -168,17 +183,37 @@ def start_demo(): if cpu["Base frequency"] != '': formula_config["cpu-base-freq"] = int(float(cpu["Base frequency"])*1000) + print("Base frequency updated") if cpu["TDP"] != '': formula_config["cpu-tdp"] = int(cpu["TDP"][:-1]) + print("TDP updated\n") with open('formula/smartwatts-mongodb-csv.json', 'w', encoding='UTF-8') as f: json.dump(formula_config, f, indent=4) - print("Starting the demo...") - print("The demo will run for approximately 2 minutes\n") - - call("./start.sh") + print("Please enter the number of second you want the demo to run for (minimum 30) or exit to quit:") + waiting = True + while waiting: + try: + val = input() + val = int(val) + if val < 30: + print("Invalid input, please enter a valid number or exit to quit") + else: + waiting = False + except ValueError: + if val == "exit": + print("Exiting...") + sys.exit() + else: + print("Invalid input, please enter a valid number or exit to quit") + + print("\nStarting the demo...") + print("-" * 80) + print("The demo will run for " + val + "\n") + + docker_start(val) verification = 0 diff --git a/docs/script/getting_started/start.sh b/docs/script/getting_started/start.sh deleted file mode 100755 index 63a489e..0000000 --- a/docs/script/getting_started/start.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -docker compose up -d - -docker compose logs sensor -f & - -docker compose logs formula -f & - -sleep 120 - -./stop.sh diff --git a/docs/script/getting_started/stop.sh b/docs/script/getting_started/stop.sh deleted file mode 100755 index c31b6bd..0000000 --- a/docs/script/getting_started/stop.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash -set -ueo pipefail -set +x - -docker compose down From 451f50438eb35443fe4fbf88bed5e93a9005facd Mon Sep 17 00:00:00 2001 From: Kellian Leveque Date: Tue, 29 Oct 2024 16:16:08 +0100 Subject: [PATCH 036/108] docs(reference): Update smartwatts.md - Fix formatting and spacing issues in the documentation --- docs/reference/formulas/smartwatts.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/reference/formulas/smartwatts.md b/docs/reference/formulas/smartwatts.md index 9c23c2b..b66c4bb 100644 --- a/docs/reference/formulas/smartwatts.md +++ b/docs/reference/formulas/smartwatts.md @@ -36,11 +36,11 @@ The different images can be found on the [Docker Hub](https://hub.docker.com/r/p ???+ info "Pre-Requisites" As SmartWatts is a formula, it needs to consume compatible usage Reports from a [Sensor](../sensors/hwpc-sensor.md). Make sure you have Reports made available in a supported storage option. -For running the SmartWatts Formula you need: +For running the SmartWatts Formula you need: -- a valid configuration -- an input storage -- a output storage containing compatible `HWPCReports` +- a valid configuration +- an input storage +- a output storage containing compatible `HWPCReports` ### Parameters @@ -215,6 +215,7 @@ Table below depicts the different parameters for FileDB type output: #### Running the Formula You will find below different examples in order to run a Formula, depending on your use case: + - [Command-Line Interface](#running-the-formula-via-cli-parameters) - [Environment variables](#running-the-formula-via-environment-variables) - [Configuration File](#running-the-formula-via-configuration-file) From b4079f6d05bcae32ff5d608290cbf4fb57836a8e Mon Sep 17 00:00:00 2001 From: Kellian Leveque Date: Tue, 29 Oct 2024 16:27:00 +0100 Subject: [PATCH 037/108] docs(reference): Update hwpc-sensor.md - Add architecture generation next to their names - Wrap some parts in a `details` to shorten the page length - Change some collapsible blocks for non-collapsible blocks - Fix lines highlights of CSV output - Center infos in tables --- docs/reference/sensors/hwpc-sensor.md | 140 +++++++++++++------------- 1 file changed, 71 insertions(+), 69 deletions(-) diff --git a/docs/reference/sensors/hwpc-sensor.md b/docs/reference/sensors/hwpc-sensor.md index d2c8871..56b7cf3 100644 --- a/docs/reference/sensors/hwpc-sensor.md +++ b/docs/reference/sensors/hwpc-sensor.md @@ -3,27 +3,30 @@ HardWare Performance Counter (HWPC) Sensor is a tool that monitors the Intel CPU performance counter and the power consumption of CPU. -The figure below depicts how Sensor works in general : +The figure below depicts how Sensor works in general : + + ![HWPC Sensor Overview](../../assets/images/reference/sensors/PowerAPI_HWPCSensorOverview.drawio.svg){ width="1000px"} HWPC Sensor uses the RAPL (Running Average Power Limit) technology to monitor CPU power consumption. The following table gives a glimpse of RAPL support regarding most common architectures: -???+ info "HWPC Sensor PreRequisites" +!!! tip "CPU architecture" `lscpu` will give you the necessary information about your CPU Architecture | Architecture | RAPL Supported | |--------------|----------------| -| Intel Tiger Lake | :material-close: Not Supported | -| Intel Alder Lake | :material-close: Not Supported | -| Intel Raptor Lake | :material-close: Not Supported | +| Intel Tiger Lake (11th Gen) | :material-close: Not Supported | +| Intel Alder Lake (12th Gen) | :material-close: Not Supported | +| Intel Raptor Lake (13th & 14th Gen) | :material-close: Not Supported | | Power / ARM / RISCV | :material-close: Not Supported | | AMD Zen (1, 2, 3, 4) | :material-check: Supported | | Intel Sandy Bridge and [newer](https://en.wikipedia.org/wiki/List_of_Intel_Core_processors#Core_i_(2nd_gen)) (except for above mentions) | :material-check: Supported | -???+ info "HWPC Sensor PreRequisites" +!!! note "HWPC Sensor PreRequisites" In addition of a supported architecture, there is some pre-requisites: + - Using a Linux distribution exposing the [perf](https://perf.wiki.kernel.org/index.php/Main_Page) api - Using Cgroup version 1 when using version 1.2 or older. See [this section](../cgroup/cgroup_v1_activation.md) about its configuration - Deploying on a physical device as the HWPC Sensor must have access to the real CPU register @@ -49,82 +52,82 @@ Here is a sample to deploy the latest image version available. The following tabs gives a complete overview of available parameters, along with their default values and description. -### Global parameters +??? info "Global Parameters" -The table below shows the different parameters related to the Sensor global configuration, nested objects (system, container, output) are described in dedicated sections below: + The table below shows the different parameters related to the Sensor global configuration, nested objects (system, container, output) are described in dedicated sections below: -| Parameter | Type | CLI shortcut | Default Value | Description | -| ------------- | ----- | ------------- | ------------- | ------------------------------------ | -|`verbose` | `bool` (flag) | `v` | `false` | Verbose or quiet mode | -|`frequency` | `int` | `f` | `1000` | The time in milliseconds between two reports | -|`name` | `string` | `n` | - | Name of the sensor | -|`cgroup_basepath` | `string` | `p` | `/sys/fs/cgroup` (`cgroup` V2) | The base path for `cgroups`. To use `cgroup` V1 `/sys/fs/cgroup/perf_event` needs to be used as value | -|`system` | `dict` | `s` | - | A system group with a monitoring type and a list of system events (cf. [`system` Group Parameters](hwpc-sensor.md#system-and-container-groups-parameters)) | -|`container` | `dict` | `c` | - | A group with a monitoring type and a list of events (cf. [`system` Group Parameters](hwpc-sensor.md#system-and-container-groups-parameters)) | -|`output` | `dict`| `r` | { "type": "csv", "directory": "." } | The [output information](hwpc-sensor.md#output), the Sensor only supports [MongoDB](./hwpc-sensor.md#mongodb-output) (`mongodb`), [CSV](./hwpc-sensor.md#csv-output) (`csv`) and [socket](./hwpc-sensor.md#socket-output) as output. | + | Parameter | Type | CLI shortcut | Default Value | Description | + | ------------- | ----- | :-------------: | :-------------: | ------------------------------------ | + |`verbose` | `bool` (flag) | `v` | `false` | Verbose or quiet mode | + |`frequency` | `int` | `f` | `1000` | The time in milliseconds between two reports | + |`name` | `string` | `n` | - | Name of the sensor | + |`cgroup_basepath` | `string` | `p` | `/sys/fs/cgroup` (`cgroup` V2) | The base path for `cgroups`. To use `cgroup` V1 `/sys/fs/cgroup/perf_event` needs to be used as value | + |`system` | `dict` | `s` | - | A system group with a monitoring type and a list of system events (cf. [`system` Group Parameters](hwpc-sensor.md#system-and-container-groups-parameters)) | + |`container` | `dict` | `c` | - | A group with a monitoring type and a list of events (cf. [`system` Group Parameters](hwpc-sensor.md#system-and-container-groups-parameters)) | + |`output` | `dict`| `r` | { "type": "csv", "directory": "." } | The [output information](hwpc-sensor.md#output), the Sensor only supports [MongoDB](./hwpc-sensor.md#mongodb-output) (`mongodb`), [CSV](./hwpc-sensor.md#csv-output) (`csv`) and [socket](./hwpc-sensor.md#socket-output) as output. | -### `system` and `container` Groups Parameters +??? info "Group Parameters (`system` and `container`)" -The table below shows the different parameters related to the Sensor `system` and `container` configuration fields: + The table below shows the different parameters related to the Sensor `system` and `container` configuration fields: -| Parameter | Type | CLI shortcut | Default Value | Description | -| ------------- | ----- | ------------- | ------------- | ------------------------------------ | -|`events` | `string` | `e` | - | List of events to be monitored. As CLI parameter, each event is indicated with `e`. The structure of events is given [below](hwpc-sensor.md#events) | -|`monitoring_type` | `string` ( **one of** `MONITOR_ONE_CPU_PER_SOCKET` **or** `MONITOR_ALL_CPU_PER_SOCKET` ) | `o` (flag) | `MONITOR_ALL_CPU_PER_SOCKET` | The monitoring type. If `o` is specified as CLI parameter, `MONITOR_ONE_CPU_PER_SOCKET` is used as type | + | Parameter | Type | CLI shortcut | Default Value | Description | + | ------------- | ----- | :-------------: | :-------------: | ------------------------------------ | + |`events` | `string` | `e` | - | List of events to be monitored. As CLI parameter, each event is indicated with `e`. The structure of events is given [below](hwpc-sensor.md#events) | + |`monitoring_type` | `string` ( **one of** `MONITOR_ONE_CPU_PER_SOCKET` **or** `MONITOR_ALL_CPU_PER_SOCKET` ) | `o` (flag) | `MONITOR_ALL_CPU_PER_SOCKET` | The monitoring type. If `o` is specified as CLI parameter, `MONITOR_ONE_CPU_PER_SOCKET` is used as type | -### Events +??? info "Group Events" -Table below depicts the different group events for compatible Intel and AMD architectures. + Table below depicts the different group events for compatible Intel and AMD architectures. -| Architectures | Group | Events | -| ------------- | ----- | ------------- | -|Intel Sandy Bridge and newer, AMD Zen 2 | `rapl` | `RAPL_ENERGY_PKG`, `RAPL_ENERGY_DRAM`| -|Intel Sandy Bridge and newer, AMD Zen 2 | `msr` | `TSC`, `APERF`, `MPERF`| -|Intel Skylake, Whiskey Lake, Coffee Lake| `core` | `CPU_CLK_THREAD_UNHALTED:REF_P`, `CPU_CLK_THREAD_UNHALTED:THREAD_P`, `LLC_MISSES`,`INSTRUCTIONS_RETIRED`| -|Intel Sandy Bridge, Comet Lake | `core` | `CPU_CLK_UNHALTED:REF_P`, `CPU_CLK_UNHALTED:THREAD_P`, `LLC_MISSES`,`INSTRUCTIONS_RETIRED`| -|AMD Zen 2 | `core`| `CYCLES_NOT_IN_HALT`, `RETIRED_INSTRUCTIONS` , `RETIRED_UOPS`| -|AMD Zen 3 | `core`| `CYCLES_NOT_IN_HALT`, `RETIRED_INSTRUCTIONS` , `RETIRED_OPS`| + | Architectures | Group | Events | + | ------------- | ----- | ------------- | + |Intel Sandy Bridge and newer, AMD Zen 2 | `rapl` | `RAPL_ENERGY_PKG`, `RAPL_ENERGY_DRAM`| + |Intel Sandy Bridge and newer, AMD Zen 2 | `msr` | `TSC`, `APERF`, `MPERF`| + |Intel Skylake, Whiskey Lake, Coffee Lake| `core` | `CPU_CLK_THREAD_UNHALTED:REF_P`, `CPU_CLK_THREAD_UNHALTED:THREAD_P`, `LLC_MISSES`,`INSTRUCTIONS_RETIRED`| + |Intel Sandy Bridge, Comet Lake | `core` | `CPU_CLK_UNHALTED:REF_P`, `CPU_CLK_UNHALTED:THREAD_P`, `LLC_MISSES`,`INSTRUCTIONS_RETIRED`| + |AMD Zen 2 | `core`| `CYCLES_NOT_IN_HALT`, `RETIRED_INSTRUCTIONS` , `RETIRED_UOPS`| + |AMD Zen 3 | `core`| `CYCLES_NOT_IN_HALT`, `RETIRED_INSTRUCTIONS` , `RETIRED_OPS`| ### Output -As precised, two kinds of outputs are supported, MongoDB and CSV files. +As precised, three kinds of outputs are supported: Socket, MongoDB and CSV files. -#### MongoDB Output +??? info "MongoDB Output" -Table below depicts the different parameters for MongoDB type output with HWPC Sensor: + Table below depicts the different parameters for MongoDB type output with HWPC Sensor: -| Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | -| ------------- | ----- | ------------- | ------------- | ---------- | ------------------------------------ | -| `uri` | string | `U` | - | Yes | The IP address of your MongoDB instance | -| `database` | string | `D` | - | Yes | The name of your database | -| `collection` | string | `C` | - | Yes | The name of the collection inside `db` | + | Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | + | ------------- | ----- | :-------------: | :-------------: | :----------: | ------------------------------------ | + | `uri` | string | `U` | - | Yes | The IP address of your MongoDB instance | + | `database` | string | `D` | - | Yes | The name of your database | + | `collection` | string | `C` | - | Yes | The name of the collection inside `db` | -#### CSV Output +??? info "CSV Output" -Table below depicts the different parameters for CSV type output: + Table below depicts the different parameters for CSV type output: -| Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | -| ------------- | ----- | ------------- | ------------- | ----------| ------------------------------------ | -| `directory` | string | `U` | "." (Current directory) | No |The directory where output CSV files will be written | + | Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | + | ------------- | ----- | :-------------: | :-------------: | :----------:| ------------------------------------ | + | `directory` | string | `U` | "." (Current directory) | No |The directory where output CSV files will be written | -#### Socket Output +??? info "Socket Output" -Table below depicts the different parameters for Socket type output: + Table below depicts the different parameters for Socket type output: -| Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | -| ------------- | ----- | ------------- | ------------- | ----------| ------------------------------------ | -| `uri` | string | `U` | - | Yes | The IP address of the machine running the socket | -| `port` | int | `P` | - | Yes | The port of communication | + | Parameter | Type | CLI shortcut | Default Value | Mandatory | DQuoteescription | + | ------------- | ----- | :-------------: | :-------------: | :----------:| ------------------------------------ | + | `uri` | string | `U` | - | Yes | The IP address of the machine running the socket | + | `port` | int | `P` | - | Yes | The port of communication | ### Running the Sensor with a Configuration File The following snippets describe the configuration file of an HWPC Sensor instance, two examples are provided for both possible outputs: -???+ example "Examples using a Configuration File" +!!! example "Examples using a Configuration File" === "MongoDB Output" - ```json hl_lines="7 8 9 10" title="config_file.json" + ```json hl_lines="5-10" title="config_file.json" { "name": "sensor", "verbose": true, @@ -159,7 +162,7 @@ The following snippets describe the configuration file of an HWPC Sensor instanc === "CSV Output" - ```json hl_lines="6 7" title="config_file.json" + ```json hl_lines="5-8" title="config_file.json" { "name": "sensor", "verbose": true, @@ -194,24 +197,24 @@ The following CLI command shows how to use this configuration file in the deploy === "Docker" - ```sh hl_lines="9 10" + ```sh docker run --rm \ - --net=host \ - --privileged \ - --pid=host \ - -v /sys:/sys \ - -v /var/lib/docker/containers:/var/lib/docker/containers:ro \ - -v /tmp/powerapi-sensor-reporting:/reporting \ - -v $(pwd):/srv \ - -v $(pwd)/config_file.json:/config_file.json \ - powerapi/hwpc-sensor --config-file /config_file.json + --net=host \ + --privileged \ + --pid=host \ + -v /sys:/sys \ + -v /var/lib/docker/containers:/var/lib/docker/containers:ro \ + -v /tmp/powerapi-sensor-reporting:/reporting \ + -v $(pwd):/srv \ + -v $(pwd)/config_file.json:/config_file.json \ + powerapi/hwpc-sensor --config-file /config_file.json ``` ### Running the Sensor via CLI parameters The following CLI command shows how to launch an instance of HWPC Sensor with the same configuration as [above](hwpc-sensor.md#running-the-sensor-with-a-configuration-file), again two example are provided for both possible output: -???+ example "Examples using a CLI Parameters" +!!! example "Examples using a CLI Parameters" === "CLI with MongoDB Output" @@ -251,9 +254,8 @@ The following CLI command shows how to launch an instance of HWPC Sensor with th -c "core" -e "CPU_CLK_THREAD_UNHALTED:REF_P" -e "CPU_CLK_THREAD_UNHALTED:THREAD_P" -e "LLC_MISSES" -e "INSTRUCTIONS_RETIRED" ``` - -???+ info "Reports' Storage" +!!! note "Reports' Storage" Your [`HWPCReports`](../reports/reports.md#hwpc-reports) will be stored on MongoDB or in the `hwpc_reports.d`directory regarding the output type selected. -???+ tip "CLI parameters' names" +!!! tip "CLI parameters' names" You can only use shortcuts. From 30d99ad7925d3a582a90f96f5ea908c1af78649e Mon Sep 17 00:00:00 2001 From: Inkedstinct Date: Wed, 30 Oct 2024 08:41:41 +0100 Subject: [PATCH 038/108] docs(hwpc-sensor): Fix sensor overview background for dark mode --- .../reference/sensors/PowerAPI_HWPCSensorOverview.drawio.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/assets/images/reference/sensors/PowerAPI_HWPCSensorOverview.drawio.svg b/docs/assets/images/reference/sensors/PowerAPI_HWPCSensorOverview.drawio.svg index f272ee4..35e27de 100644 --- a/docs/assets/images/reference/sensors/PowerAPI_HWPCSensorOverview.drawio.svg +++ b/docs/assets/images/reference/sensors/PowerAPI_HWPCSensorOverview.drawio.svg @@ -1,4 +1,4 @@ -
Local server
Storing sensor's reports
Scrapping program perfomance metrics
(Optionnal)
Remote server
PowerAPI Sensor
Local Filesystem
Local program(s)
monitored
Remote database
Sensor Reports
\ No newline at end of file +
Local server
Local serv...
Storing sensor's reports
Storing sensor's reports
Scrapping program perfomance metrics
Scrapping program perfomance metrics
(Optionnal)
Remote server
(Optionnal...
PowerAPI Sensor
PowerAPI Sensor
Local Filesystem
Local Filesystem
Local program(s)
monitored
Local program(s...
Remote database
Remote database
Sensor Reports
Sensor Reports
\ No newline at end of file From 9ee947694952ea87a45fb06169f5be979bc12d29 Mon Sep 17 00:00:00 2001 From: Inkedstinct Date: Wed, 30 Oct 2024 08:53:28 +0100 Subject: [PATCH 039/108] docs(getting-started): Add archive option for getting-started --- docs/script/getting_started.tar.gz | Bin 0 -> 4874 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/script/getting_started.tar.gz diff --git a/docs/script/getting_started.tar.gz b/docs/script/getting_started.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..eaebd26d1574ffccbff3fa95378b9853f0340251 GIT binary patch literal 4874 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 literal 0 HcmV?d00001 From 577f40e0a323c4e00402dc8736609e095e0a2693 Mon Sep 17 00:00:00 2001 From: Inkedstinct Date: Wed, 30 Oct 2024 08:59:19 +0100 Subject: [PATCH 040/108] docs(getting-started): Add CLI commands for archive option in getting-started --- docs/getting_started.md | 52 +++++++---------------------------------- 1 file changed, 9 insertions(+), 43 deletions(-) diff --git a/docs/getting_started.md b/docs/getting_started.md index f80848b..93a666b 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -11,49 +11,8 @@ A few things are required before we start : - A python installation ready - Docker & Docker-Compose ready - Root access +- Optionnal : Git to proceed by clonning the repository -The first step of the tutorial will be to define the elements to monitor. -In the testing archive, we will be able to see the consumption of the docker container by the default. -So feel free to skip directly to the [preparation part](#preparation) if you don't want to monitor a specific process. - -## Define elements to monitor - -!!! tip "Optionnal abstraction for fine-grain analysis" - This part is optionnal, it allows the definition of cgroups which can group chosen processes that make sense to you. - If you skip it, the next steps will work against all current process grouped. - -PowerAPI being a monitoring tool for energy consumption, we can define logic grouping of elements to monitor. -To do so we can use the Linux abstraction of [cGroups](https://www.redhat.com/sysadmin/cgroups-part-one). -Kernel supports 2 versions of cGroups : v1 and v2. -To know which one your kernel supports, you can run : -```sh -mount | grep '^cgroup' | awk '{print $1}' | uniq -``` - -*Both versions can be supported at the same time*. - -??? "Create a cGroup" - - In order to create a cGroup, you can use: - - - cgroup v1 : [this doc](https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/cgroups.html#usage-examples-and-syntax) - - cgroup v2 : [this doc](https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#mounting) - - - -??? "Add processes to the group" - - Once the cgroup created, we need to fill it with processes to be monitored. - To do so, you can use: - - - cgroup v1 : [this doc](https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/cgroups.html#attaching-processes). - - cgroup v2 : [this doc](https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/cgroups.html#attaching-processes). - -??? warning "Installing a process to monitor" - - [stress-ng](https://wiki.ubuntu.com/Kernel/Reference/stress-ng) can be used to - generate load on one's system. **Be carefull** as it can be configured to be quite agressive. - ## Which components to get a complete stack If you wish to get started as soon as possible, the following archive will allow you to deploy the following elements : @@ -73,12 +32,19 @@ quick glimpse ## Preparation -Clone the repository and get ready: + +1. Clone the repository: ```sh git clone https://github.com/powerapi-ng/powerapi-ng.github.io.git cd powerapi-ng.github.io/docs/script/getting-started ``` +2. Download the archive: +``` +wget -c https://raw.githubusercontent.com/powerapi-ng/powerapi-ng.github.io/refs/heads/master/docs/script/getting_started.tar.gz -O - | tar -xz +cd getting_started +``` + From this archive, you will have all the necessary files to get started, let us break down each element. ### Archive content From 10f422775f8e768138b0dbd76590f08ceb25d299 Mon Sep 17 00:00:00 2001 From: Inkedstinct Date: Wed, 30 Oct 2024 09:05:42 +0100 Subject: [PATCH 041/108] docs(getting-started): Fix dead links --- docs/getting_started.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/getting_started.md b/docs/getting_started.md index 93a666b..0bf7fe0 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -21,13 +21,13 @@ If you wish to get started as soon as possible, the following archive will allow Reports 3. An [HWPC-Sensor](./reference/sensors/hwpc-sensor.md) that outputs its -[HWPCReports](./reference/reports/report.md#HWPCReport) in a MongoDB Database, +[HWPCReports](./reference/reports/reports.md#hwpc-reports) in a MongoDB Database, within the HWPCReport Collection 4. A [SmartWatts](./reference/formulas/smartwatts.md) that streams the -[HWPCReports](./reference/reports/report.md#HWPCReport) from the MongoDB +[HWPCReports](./reference/reports/reports.md#hwpc-reports) from the MongoDB Database Collection, processes it and outputs its -[PowerReports](./reference/reports/report.md#PowerReports) as CSV files for a +[PowerReports](./reference/reports/reports.md#power-reports) as CSV files for a quick glimpse ## Preparation @@ -82,7 +82,7 @@ python3 start.py ``` After the 2 minutes of monitoring, you will be able to see the result inside the **csv** directory. -If you have trouble understanding the output, you can read the [Power Report documentation](./reference/reports/reports.md#power-Reports). +If you have trouble understanding the output, you can read the [Power Report documentation](./reference/reports/reports.md#power-reports). !!! info "Quick results overview" Only in the context of this testing archive, after the monitoring, you can use the following command to get a pretty print of the result directly inside the terminal. From 622ae686aa21b606186b6847b5cea89a15dbe88f Mon Sep 17 00:00:00 2001 From: Inkedstinct Date: Wed, 30 Oct 2024 09:53:43 +0100 Subject: [PATCH 042/108] docs(getting-started): Add Docker install documentation --- docs/getting_started.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/getting_started.md b/docs/getting_started.md index 0bf7fe0..9d61bc4 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -9,9 +9,9 @@ A few things are required before we start : * For [Intel Xeon Processor](https://en.wikipedia.org/wiki/List_of_Intel_Xeon_processors) * For [AMD Processor](https://en.wikipedia.org/wiki/Table_of_AMD_processors) - A python installation ready -- Docker & Docker-Compose ready +- Docker & Docker-Compose ready (refer to [this official documentation](https://docs.docker.com/engine/install/) and the [post-install steps](https://docs.docker.com/engine/install/linux-postinstall/) if needed !) - Root access -- Optionnal : Git to proceed by clonning the repository +- Optionnal : [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) to proceed by clonning the repository ## Which components to get a complete stack From 9e8b2db2c355da51196d7fa1a9252f558c1c782c Mon Sep 17 00:00:00 2001 From: Inkedstinct Date: Wed, 30 Oct 2024 09:58:10 +0100 Subject: [PATCH 043/108] docs(references): Remove configuration-file part for Formulas, currently described in the formula doc directly --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index d4ea3d8..d32e688 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -84,7 +84,7 @@ nav: - Formulas: #- RAPL: reference/formulas/rapl.md - SmartWatts: reference/formulas/smartwatts.md - - Configuration Files: reference/formulas/configuration_files.md + #- Configuration Files: reference/formulas/configuration_files.md # - Sources/Destinations: # - Description: reference/database/sources_destinations.md - Processors: From a2121d05dc3d24bef7dcd066a0c8354f524eedba Mon Sep 17 00:00:00 2001 From: Inkedstinct Date: Wed, 30 Oct 2024 10:02:02 +0100 Subject: [PATCH 044/108] docs(overview): Fix SmartWatts reports unit --- docs/reference/overview.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/overview.md b/docs/reference/overview.md index c324ad8..db23f6c 100644 --- a/docs/reference/overview.md +++ b/docs/reference/overview.md @@ -14,7 +14,7 @@ The diagram below illustrates the overall architecture of a PowerMeter within th A PowerMeter consists of two essential components: - a [Sensor](#Sensor), which collects system usage metrics and generates usage reports. -- a [Formula](#Formula), which applies a computational model to the usage reports, producing power consumption data (in mJ). +- a [Formula](#Formula), which applies a computational model to the usage reports, producing power consumption data (in watts). Additionally, [Preprocessors](./overview.md#Preprocessors) can be utilized to modify usage reports before they are processed by the Formula. From 7909f0809b39740422a4d5c7f67d3ce4592d6552 Mon Sep 17 00:00:00 2001 From: Inkedstinct Date: Wed, 30 Oct 2024 10:02:28 +0100 Subject: [PATCH 045/108] docs(getting-started): Add UID / GUID information --- docs/script/getting_started/start.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/script/getting_started/start.sh b/docs/script/getting_started/start.sh index 63a489e..bc25fdd 100755 --- a/docs/script/getting_started/start.sh +++ b/docs/script/getting_started/start.sh @@ -1,6 +1,6 @@ #!/bin/bash -docker compose up -d +UID="$(id -u)" GUID="$(id -g) " docker compose up -d docker compose logs sensor -f & From fcfb3766b8175da9defe8dfcbd3804db18cd58d6 Mon Sep 17 00:00:00 2001 From: Inkedstinct Date: Wed, 30 Oct 2024 10:14:43 +0100 Subject: [PATCH 046/108] docs(hwpc-sensor): Add Socket output as example + minor fixes --- docs/reference/sensors/hwpc-sensor.md | 50 ++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 9 deletions(-) diff --git a/docs/reference/sensors/hwpc-sensor.md b/docs/reference/sensors/hwpc-sensor.md index 56b7cf3..544b388 100644 --- a/docs/reference/sensors/hwpc-sensor.md +++ b/docs/reference/sensors/hwpc-sensor.md @@ -9,7 +9,8 @@ The figure below depicts how Sensor works in general : ![HWPC Sensor Overview](../../assets/images/reference/sensors/PowerAPI_HWPCSensorOverview.drawio.svg){ width="1000px"} HWPC Sensor uses the RAPL (Running Average Power Limit) technology to monitor CPU -power consumption. The following table gives a glimpse of RAPL support regarding +power consumption. Have in mind that this sensor is mainly developped for "server" architectures. +The following table gives a glimpse of RAPL support regarding most common architectures: !!! tip "CPU architecture" @@ -17,14 +18,14 @@ most common architectures: | Architecture | RAPL Supported | |--------------|----------------| +| AMD Zen (1, 2, 3, 4) | :material-check: Supported | +| Intel Sandy Bridge and [newer](https://en.wikipedia.org/wiki/List_of_Intel_Core_processors#Core_i_(2nd_gen)) (except for below mentions) | :material-check: Supported | | Intel Tiger Lake (11th Gen) | :material-close: Not Supported | | Intel Alder Lake (12th Gen) | :material-close: Not Supported | | Intel Raptor Lake (13th & 14th Gen) | :material-close: Not Supported | | Power / ARM / RISCV | :material-close: Not Supported | -| AMD Zen (1, 2, 3, 4) | :material-check: Supported | -| Intel Sandy Bridge and [newer](https://en.wikipedia.org/wiki/List_of_Intel_Core_processors#Core_i_(2nd_gen)) (except for above mentions) | :material-check: Supported | -!!! note "HWPC Sensor PreRequisites" +!!! note "HWPC Sensor pre-requisites" In addition of a supported architecture, there is some pre-requisites: - Using a Linux distribution exposing the [perf](https://perf.wiki.kernel.org/index.php/Main_Page) api @@ -121,9 +122,9 @@ As precised, three kinds of outputs are supported: Socket, MongoDB and CSV files ### Running the Sensor with a Configuration File -The following snippets describe the configuration file of an HWPC Sensor instance, two examples are provided for both possible outputs: +The following snippets describe the configuration file of an HWPC Sensor instance, two examples are provided for possible outputs: -!!! example "Examples using a Configuration File" +!!! example "Examples for an Intel Processor, using a Configuration File" === "MongoDB Output" @@ -192,6 +193,40 @@ The following snippets describe the configuration file of an HWPC Sensor instanc } } ``` + + === "Socket Output" + + ```json hl_lines="5-9" title="config_file.json" + { + "name": "sensor", + "verbose": true, + "frequency": 500, + "output": { + "type": "socket", + "uri": "http://127.0.0.1", + "port": "9876" + }, + "system": { + "rapl": { + "events": ["RAPL_ENERGY_PKG"], + "monitoring_type": "MONITOR_ONE_CPU_PER_SOCKET" + }, + "msr": { + "events": ["TSC", "APERF", "MPERF"] + } + }, + "container": { + "core": { + "events": [ + "CPU_CLK_THREAD_UNHALTED:REF_P", + "CPU_CLK_THREAD_UNHALTED:THREAD_P", + "LLC_MISSES", + "INSTRUCTIONS_RETIRED" + ] + } + } + } + ``` The following CLI command shows how to use this configuration file in the deployment of an HWPC Sensor instance as a Docker container : @@ -254,8 +289,5 @@ The following CLI command shows how to launch an instance of HWPC Sensor with th -c "core" -e "CPU_CLK_THREAD_UNHALTED:REF_P" -e "CPU_CLK_THREAD_UNHALTED:THREAD_P" -e "LLC_MISSES" -e "INSTRUCTIONS_RETIRED" ``` -!!! note "Reports' Storage" - Your [`HWPCReports`](../reports/reports.md#hwpc-reports) will be stored on MongoDB or in the `hwpc_reports.d`directory regarding the output type selected. - !!! tip "CLI parameters' names" You can only use shortcuts. From b05e74e95dc1bd9dd617ead00793d37f177acb27 Mon Sep 17 00:00:00 2001 From: Inkedstinct Date: Wed, 30 Oct 2024 10:16:15 +0100 Subject: [PATCH 047/108] docs(getting-started): Add info on how to stop script --- docs/script/getting_started/start.py | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/script/getting_started/start.py b/docs/script/getting_started/start.py index 4f4b4a9..624af2c 100644 --- a/docs/script/getting_started/start.py +++ b/docs/script/getting_started/start.py @@ -131,6 +131,7 @@ def start_demo(): print("Starting the demo...") print("The demo will run for approximately 2 minutes\n") + print("If you wish to stop it, Ctrl-C will do so and stop the docker compose stack\n") call("./start.sh") From dedfdf2b2afa1b95318413a588e6a433fad9de02 Mon Sep 17 00:00:00 2001 From: Inkedstinct Date: Wed, 30 Oct 2024 10:19:33 +0100 Subject: [PATCH 048/108] docs(all): Fix HWPCReports -> HWPC Reports, same for PowerReports --- docs/getting_started.md | 8 +-- .../database/sources_destinations.md | 14 ++-- .../reference/formulas/configuration_files.md | 2 +- docs/reference/formulas/rapl.md | 20 +++--- docs/reference/formulas/smartwatts.md | 68 +++++++++---------- docs/reference/formulas/smartwatts.md.save | 16 ++--- docs/reference/grafana/grafana.md | 2 +- docs/reference/processors/processors.md | 22 +++--- docs/reference/reports/reports.md | 8 +-- .../formula/smartwatts-mongodb-csv.json | 4 +- docs/script/smartwatts_auto_config.md | 2 +- 11 files changed, 83 insertions(+), 83 deletions(-) diff --git a/docs/getting_started.md b/docs/getting_started.md index 9d61bc4..8563179 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -21,13 +21,13 @@ If you wish to get started as soon as possible, the following archive will allow Reports 3. An [HWPC-Sensor](./reference/sensors/hwpc-sensor.md) that outputs its -[HWPCReports](./reference/reports/reports.md#hwpc-reports) in a MongoDB Database, -within the HWPCReport Collection +[HWPC Reports](./reference/reports/reports.md#hwpc-reports) in a MongoDB Database, +within the HWPC Report Collection 4. A [SmartWatts](./reference/formulas/smartwatts.md) that streams the -[HWPCReports](./reference/reports/reports.md#hwpc-reports) from the MongoDB +[HWPC Reports](./reference/reports/reports.md#hwpc-reports) from the MongoDB Database Collection, processes it and outputs its -[PowerReports](./reference/reports/reports.md#power-reports) as CSV files for a +[Power Reports](./reference/reports/reports.md#power-reports) as CSV files for a quick glimpse ## Preparation diff --git a/docs/reference/database/sources_destinations.md b/docs/reference/database/sources_destinations.md index 98be258..a8f482a 100644 --- a/docs/reference/database/sources_destinations.md +++ b/docs/reference/database/sources_destinations.md @@ -34,7 +34,7 @@ The list of accepted parameters are: |`db` (`database` for `HWPCSensor`) | string | `d` (`D` for `HWPCSensor`) | N/A | Yes | The name of your database | |`collection` | string | `c` (`C` for `HWPCSensor`) | N/A | Yes | The name of the collection inside `db` | |`name` | string | `n` | `"puller_mongodb"` (Source), `pusher_mongodb` (Destination)| No | The related puller/pusher name. This parameter is not used by `HWPCSensor` | -|`model` | string | `m` | `"HWPCReport"` (Source), `PowerReport` (Destination) | No | The Report type stored by the database | +|`model` | string | `m` | `"HWPC Report"` (Source), `Power Report` (Destination) | No | The Report type stored by the database | ### JSON File Excerpt @@ -68,7 +68,7 @@ The list of accepted parameters are: |`org` | string | `g` | N/A | Yes | The name of the organization associated to the bucket | |`tags` | string | `t` | N/A | No | List of metadata keys of the report separated by `,` that will be kept. `sensor` and `target` are always kept as report metadata | |`name` | string | `n` | `"pusher_influxdb2"` | No | The related pusher name | -|`model` | string | `m` | `"PowerReport"` | No | The Report type stored by the database | +|`model` | string | `m` | `"Power Report"` | No | The Report type stored by the database | InfluxDB2 can only be used as a Destination. @@ -79,7 +79,7 @@ Below you find an example of configuration excerpt for this kind of Destination. ```json { - "model": "PowerReport", + "model": "Power Report", "type": "influxdb2", "uri": "http://127.0.0.1", "port": 8086, @@ -104,7 +104,7 @@ The list of accepted parameters are: |`files`(Source)| string | `f` | Empty list | No | The list of input CSV files with the format file1,file2,file3... | |`directory` (Destination and `HWPCSensor`)| string |`d` (`U` for `HWPCSensor`) | Current directory | No |The directory where output CSV files will be written | |`name` | string | `n` | `"puller_csv"` (Source), `"pusher_csv"` (Destination)| No | The related puller/pusher name. This parameter is not used by `HWPCSensor` | -|`model` | string | `m` | `"HWPCReport"` (Source), `"PowerReport"` (Destination) | No | The Report type stored in CSV files. This parameter is not used by `HWPCSensor` | +|`model` | string | `m` | `"HWPC Report"` (Source), `"Power Report"` (Destination) | No | The Report type stored in CSV files. This parameter is not used by `HWPCSensor` | ### JSON File Excerpt @@ -132,7 +132,7 @@ The list of accepted parameters are: |`port` | int | `P` | N/A | Yes | The port of communication | |`uri`/ `host` | int | `U` | N/A | Yes | The IP address of the machine running the socket | |`name` | string | `n` | `"puller_socket"`| No | The related puller name | -|`model` | string | `m` | `"HWPCReport"` | No | The Report type managed by the socket | +|`model` | string | `m` | `"HWPC Report"` | No | The Report type managed by the socket | ### JSON File Excerpt @@ -162,7 +162,7 @@ The list of accepted parameters are: | ------------- | ----- | ------------- | -------------| ---------- | ------------------------------------ | |`filename` | int | `f` | N/A | Yes | The name of the file | |`name` | string | `n` | `"pusher_filedb"` | No | The related pusher name | -|`model` | string | `m` | `"HWPCReport"` (Source) `"PowerReport"` (Destination)| No | The Report type stored in the file | +|`model` | string | `m` | `"HWPC Report"` (Source) `"Power Report"` (Destination)| No | The Report type stored in the file | ### JSON File Excerpt @@ -192,7 +192,7 @@ The list of accepted parameters are: |`metric-name` | string | `M` | N/A | Yes | The exposed metric name | |`metric-description` | string | `d` | `"energy consumption"` | No | The exposed metric description | |`name` | string | `n` | `"pusher_prom"` | No | The related pusher name | -|`model` | string | `m` | `"PowerReport"` | No | The Report type exposed by Prometheus | +|`model` | string | `m` | `"Power Report"` | No | The Report type exposed by Prometheus | Prometheus can only be used as a Destination that monitors reports but they will be not stored by this service. diff --git a/docs/reference/formulas/configuration_files.md b/docs/reference/formulas/configuration_files.md index ec2518e..a80a758 100644 --- a/docs/reference/formulas/configuration_files.md +++ b/docs/reference/formulas/configuration_files.md @@ -12,7 +12,7 @@ The table below shows basic parameters. | `stream` | `bool` (flag) | `s` |`False` | Real time or post-mortem mode | | `sensor-report-sampling-interval` | `int` | N/A | `1000` | The time in milliseconds between two reports (`stream` = `True`) | | `input` | `string` | N/A | N/A | SmartWatts input, shall match an existing Sensor output and contain HPWCReports. See [here](./smartwatts.md#smartwatts-inputs) | -| `output` | `string` | N/A | N/A | SmartWatts output to store PowerReport. See [here](./smartwatts.md#smartwatts-outputs) | +| `output` | `string` | N/A | N/A | SmartWatts output to store Power Report. See [here](./smartwatts.md#smartwatts-outputs) | | `pre-processor` | `string` | N/A | N/A | Pre-Processor to modify reports generated by a sensor. More information about Processors and their related parameters can be found [here](../processors/processors.md) | | `post-processor` | `string` | N/A | N/A | Post-Processor to modify reports generated by a formula. More information about Processors and their related parameters can be found [here](../processors/processors.md) | diff --git a/docs/reference/formulas/rapl.md b/docs/reference/formulas/rapl.md index e9f9fa2..4496c7a 100644 --- a/docs/reference/formulas/rapl.md +++ b/docs/reference/formulas/rapl.md @@ -7,7 +7,7 @@ The RAPL Formula is designed to mesure power consumption of domains (CPU or RAM) in real time. The RAPL Formula takes HWPC Report with RAPL event for each domains. It then -returns the mesured power in a PowerReport for each domain. +returns the mesured power in a Power Report for each domain. This Formula does not perform any other computation as its goal is only to track global power consumption in a more readable way than raw RAPL. @@ -29,7 +29,7 @@ You can use [the following script](../script/rapl_install.sh) to install RAPL Fo ## Usage -For running the RAPL Formula you need: a Source and a Destination, a Sensor that provides `HWPCReports` and a configuration. +For running the RAPL Formula you need: a Source and a Destination, a Sensor that provides `HWPC Reports` and a configuration. ### Source and Destination @@ -42,7 +42,7 @@ docker run -d --name mongo_source_destination -p 27017:27017 mongo ``` ### Sensor -[HWPC Sensor](../sensors/hwpc-sensor.md) is used in order to get `HWPCReports`. Start by installing the HWPC Sensor (see +[HWPC Sensor](../sensors/hwpc-sensor.md) is used in order to get `HWPC Reports`. Start by installing the HWPC Sensor (see [here](../sensors/hwpc-sensor.md#installation)) and start it (see [here](../sensors/hwpc-sensor.md#usage)). @@ -69,7 +69,7 @@ Below an example is provided by using MongoDB as Source and Destination. "stream": true, "input": { "puller": { - "model": "HWPCReport", + "model": "HWPC Report", "type": "mongodb", "uri": "mongodb://127.0.0.1", "db": "test", @@ -79,7 +79,7 @@ Below an example is provided by using MongoDB as Source and Destination. "output": { "pusher_power": { "type": "mongodb", - "model": "PowerReport", + "model": "Power Report", "type": "mongodb", "uri": "mongodb://127.0.0.1", "db": "test", @@ -124,8 +124,8 @@ the installation you use: docker run -t \ --net=host \ powerapi/powerapi --verbose \ - --input mongodb --model HWPCReport --uri mongodb://127.0.0.1 --db test --collection prep \ - --output mongodb --model PowerReport --uri mongodb://127.0.0.1 --db test --collection results \ + --input mongodb --model HWPC Report --uri mongodb://127.0.0.1 --db test --collection prep \ + --output mongodb --model Power Report --uri mongodb://127.0.0.1 --db test --collection results \ --disable-dram-formula \ --sensor-report-sampling-interval 500 ``` @@ -134,14 +134,14 @@ the installation you use: ```sh python -m powerapi --verbose \ - --input mongodb --model HWPCReport --uri mongodb://127.0.0.1 --db test --collection prep \ - --output mongodb --model PowerReport --uri mongodb://127.0.0.1 --db test --collection results \ + --input mongodb --model HWPC Report --uri mongodb://127.0.0.1 --db test --collection prep \ + --output mongodb --model Power Report --uri mongodb://127.0.0.1 --db test --collection results \ --disable-dram-formula \ --sensor-report-sampling-interval 500 ``` ???+ info "Estimations' Storage" - Your `PowerReports` will be stored on MongoDB. + Your `Power Reports` will be stored on MongoDB. ???+ tip "Using shortcuts for parameters' names" You use `-` instead of `--`. diff --git a/docs/reference/formulas/smartwatts.md b/docs/reference/formulas/smartwatts.md index b66c4bb..f28d7d2 100644 --- a/docs/reference/formulas/smartwatts.md +++ b/docs/reference/formulas/smartwatts.md @@ -1,7 +1,7 @@ # SmartWatts Formula SmartWatts is a configurable Formula that can estimate the power consumption of software in real-time. -SmartWatts needs to receive several metrics available in Sensor's Reports, [HWPCReports](../reports/reports.md#HWPC-reports) is a compatible Report type produced by HWPC-Sensor (i.e making the necessary metrics available in HWPC Reports): +SmartWatts needs to receive several metrics available in Sensor's Reports, [HWPC Reports](../reports/reports.md#HWPC-reports) is a compatible Report type produced by HWPC-Sensor (i.e making the necessary metrics available in HWPC Reports): - The Running Average Power Limit (`RAPL`) - `msr` events (`TSC`, `APERF`, `MPERF`) @@ -40,7 +40,7 @@ For running the SmartWatts Formula you need: - a valid configuration - an input storage -- a output storage containing compatible `HWPCReports` +- a output storage containing compatible `HWPC Reports` ### Parameters @@ -57,7 +57,7 @@ This table resumes the parameters needed for any Formula (and thus, for SmartWat | `stream` | `bool` (flag) | `s` |`False` | Real time or post-mortem mode | | `sensor-report-sampling-interval` | `int` | - | `1000` | The time in milliseconds between two reports (`stream` = `True`) | | `input` | `string` | - | - | SmartWatts input, shall match an existing Sensor output and contain HPWCReports. See [here](./smartwatts.md#smartwatts-inputs) | -| `output` | `string` | - | - | SmartWatts output to store PowerReport. See [here](./smartwatts.md#smartwatts-outputs) | +| `output` | `string` | - | - | SmartWatts output to store Power Report. See [here](./smartwatts.md#smartwatts-outputs) | | `pre-processor` | `string` | - | - | Pre-Processor to modify reports generated by a sensor. More information about Processors and their related parameters can be found [here](../processors/processors.md) | | `post-processor` | `string` | - | - | Post-Processor to modify reports generated by a formula. More information about Processors and their related parameters can be found [here](../processors/processors.md) | @@ -83,7 +83,7 @@ This table resumes the parameters specific to SmartWatts configuration : #### SmartWatts Inputs As any Formula, SmartWatts needs inputs. -We can choose those among the following list, depending on where your Sensor outputs its PowerReports: +We can choose those among the following list, depending on where your Sensor outputs its Power Reports: - [MongoDB](./smartwatts.md#mongodb-input) - [CSV](./smartwatts.md#csv-input) @@ -100,7 +100,7 @@ Table below depicts the different parameters for MongoDB type input: | `database` | string | `d` | - | Yes | The name of your database | | `collection` | string | `c` | - | Yes | The name of the collection inside `db` | | `name` | string | `n` | puller_mongodb | No | The related puller name | -| `model` | string | `m` | HWPCReport | No | The Report type stored by the database | +| `model` | string | `m` | HWPC Report | No | The Report type stored by the database | ##### CSV Input @@ -111,7 +111,7 @@ Table below depicts the different parameters for CSV type input: | `files` | string | `f` | "" | No | The list of input CSV files with the format "file1,file2,file3..." | | `directory` | string | `d` | "." (Current directory) | No |The directory where output CSV files will be written | | `name` | string | `n` | puller_csv | No | The related puller name | -| `model` | string | `m` | HWPCReport | No | The Report type stored by the database | +| `model` | string | `m` | HWPC Report | No | The Report type stored by the database | ##### Socket input @@ -122,7 +122,7 @@ Table below depicts the different parameters for CSV type input: | `port` | int | `P` | - | Yes | The port of communication | | `uri` | string | `U` | - | Yes | The IP address of the machine running the socket | | `name` | string | `n` | puller_socket | No | The related puller name | -| `model` | string | `m` | HWPCReport | No | The Report type stored by the database | +| `model` | string | `m` | HWPC Report | No | The Report type stored by the database | ##### FileDB input @@ -132,12 +132,12 @@ Table below depicts the different parameters for CSV type input: | ------------- | ----- | ------------- | ------------- | ----------| ------------------------------------ | | `filename` | string | `f` | - | yes | Path to the file | | `name` | string | `n` | puller_filedb| No | The related puller name | -| `model` | string | `m` | HWPCReport | No | The Report type stored by the database | +| `model` | string | `m` | HWPC Report | No | The Report type stored by the database | #### SmartWatts Outputs -On the same principle, SmartWatts needs to output its PowerReport. -We can choose it among the following list, depending on where your Sensor outputs its PowerReports: +On the same principle, SmartWatts needs to output its Power Report. +We can choose it among the following list, depending on where your Sensor outputs its Power Reports: - [MongoDB](./smartwatts.md#mongodb-output) - [CSV](./smartwatts.md#csv-output) @@ -155,7 +155,7 @@ Table below depicts the different parameters for MongoDB type output: | `database` | string | `d` | - | Yes | The name of your database | | `collection` | string | `c` | - | Yes | The name of the collection inside `db` | | `name` | string | `n` | pusher_mongodb | No | The related puller name | -| `model` | string | `m` | PowerReport | No | The Report type stored by the database | +| `model` | string | `m` | Power Report | No | The Report type stored by the database | ##### CSV output @@ -166,7 +166,7 @@ Table below depicts the different parameters for CSV type output: | `files` | string | `f` | "" | No | The list of input CSV files with the format "file1,file2,file3..." | | `directory` | string | `d` | "." (Current directory) | No |The directory where output CSV files will be written | | `name` | string | `n` | pusher_csv | No | The related puller name | -| `model` | string | `m` | PowerReport | No | The Report type stored by the database | +| `model` | string | `m` | Power Report | No | The Report type stored by the database | ##### InfluxDB output @@ -185,7 +185,7 @@ Table below depicts the different parameters for InfluxDB2 type output: |`org` | string | `g` | - | Yes | The name of the organization associated to the bucket | |`tags` | string | `t` | - | No | List of metadata keys of the report separated by `,` that will be kept. `sensor` and `target` are always kept as report metadata | |`name` | string | `n` | `"pusher_influxdb2"` | No | The related pusher name | -|`model` | string | `m` | `"PowerReport"` | No | The Report type stored by the database | +|`model` | string | `m` | `"Power Report"` | No | The Report type stored by the database | ##### Prometheus output @@ -199,7 +199,7 @@ Table below depicts the different parameters for Prometheus type output: |`metric-name` | string | `M` | - | Yes | The exposed metric name | |`metric-description` | string | `d` | `"energy consumption"` | No | The exposed metric description | |`name` | string | `n` | `"pusher_prom"` | No | The related pusher name | -|`model` | string | `m` | `"PowerReport"` | No | The Report type exposed by Prometheus | +|`model` | string | `m` | `"Power Report"` | No | The Report type exposed by Prometheus | ##### FileDB output @@ -210,7 +210,7 @@ Table below depicts the different parameters for FileDB type output: | ------------- | ----- | ------------- | ------------- | ----------| ------------------------------------ | | `filename` | string | `f` | - | Yes | Path to the file | | `name` | string | `n` | pusher_filedb | No | The related pusher name | -| `model` | string | `m` | PowerReport | No | The Report type stored by the database | +| `model` | string | `m` | Power Report | No | The Report type stored by the database | #### Running the Formula @@ -235,8 +235,8 @@ In order to run the Formula, you can execute one of the following command lines, docker run -t \ --net=host \ powerapi/smartwatts-formula --verbose \ - --input mongodb --model HWPCReport --uri mongodb://127.0.0.1 --db test --collection prep \ - --output influxdb2 --model PowerReport --uri 127.0.0.1 --port 8086 --db power_consumption --org org_test --token mytoken \ + --input mongodb --model HWPC Report --uri mongodb://127.0.0.1 --db test --collection prep \ + --output influxdb2 --model Power Report --uri 127.0.0.1 --port 8086 --db power_consumption --org org_test --token mytoken \ --cpu-base-freq 1900 \ --cpu-error-threshold 2.0 \ --disable-dram-formula \ @@ -249,8 +249,8 @@ In order to run the Formula, you can execute one of the following command lines, docker run -t \ --net=host \ powerapi/smartwatts-formula --verbose \ - --input csv --model HWPCReport --directory hwcp\_reports.d --files "hwpc\_report\_1, hwpc\_report\_2" \ - --output influxdb2 --model PowerReport --uri 127.0.0.1 --port 8086 --db power_consumption --org org_test --token mytoken \ + --input csv --model HWPC Report --directory hwcp\_reports.d --files "hwpc\_report\_1, hwpc\_report\_2" \ + --output influxdb2 --model Power Report --uri 127.0.0.1 --port 8086 --db power_consumption --org org_test --token mytoken \ --cpu-base-freq 1900 \ --cpu-error-threshold 2.0 \ --disable-dram-formula \ @@ -264,8 +264,8 @@ In order to run the Formula, you can execute one of the following command lines, ```sh hl_lines="3 4" python -m smartwatts \ --verbose \ - --input mongodb --model HWPCReport --uri mongodb://127.0.0.1 --db test --collection prep \ - --output influxdb2 --model PowerReport --uri 127.0.0.1 --port 8086 --db power_consumption --org org_test --token mytoken\ + --input mongodb --model HWPC Report --uri mongodb://127.0.0.1 --db test --collection prep \ + --output influxdb2 --model Power Report --uri 127.0.0.1 --port 8086 --db power_consumption --org org_test --token mytoken\ --cpu-base-freq 1900 \ --cpu-error-threshold 2.0 \ --disable-dram-formula \ @@ -277,8 +277,8 @@ In order to run the Formula, you can execute one of the following command lines, ```sh hl_lines="3 4" python -m smartwatts \ --verbose \ - --input csv --model HWPCReport --directory hwcp\_reports.d --name puller\_csv --files "hwpc\_report\_1.json, hwpc\_report\_2.json" \ - --output influxdb2 --model PowerReport --uri 127.0.0.1 --port 8086 --db power_consumption --org org_test --token mytoken\ + --input csv --model HWPC Report --directory hwcp\_reports.d --name puller\_csv --files "hwpc\_report\_1.json, hwpc\_report\_2.json" \ + --output influxdb2 --model Power Report --uri 127.0.0.1 --port 8086 --db power_consumption --org org_test --token mytoken\ --cpu-base-freq 1900 \ --cpu-error-threshold 2.0 \ --disable-dram-formula \ @@ -286,7 +286,7 @@ In order to run the Formula, you can execute one of the following command lines, ``` ???+ info "Estimations' Storage" - Your `PowerReports` will be stored on InfluxDB2. You can watch them in a grafana by using the [following tutorial](../grafana/grafana.md). + Your `Power Reports` will be stored on InfluxDB2. You can watch them in a grafana by using the [following tutorial](../grafana/grafana.md). #### Running the Formula with Environment Variables @@ -315,12 +315,12 @@ Below you find an example for running the Formula with Docker and Pip: -e POWERAPI_CPU_ERROR_THRESHOLD=2.0 \ -e POWERAPI_DISABLE_DRAM_FORMULA=true \ -e POWERAPI_SENSOR_REPORTS_FREQUENCY=1000 \ - -e POWERAPI_INPUT_PULLER_MODEL=HWPCReport \ + -e POWERAPI_INPUT_PULLER_MODEL=HWPC Report \ -e POWERAPI_INPUT_PULLER_TYPE=mongodb \ -e POWERAPI_INPUT_PULLER_URI=mongodb://127.0.0.1 \ -e POWERAPI_INPUT_PULLER_DB=test \ -e POWERAPI_INPUT_PULLER_COLLECTION=prep \ - -e POWERAPI_OUTPUT_PUSHER_POWER_MODEL=PowerReport \ + -e POWERAPI_OUTPUT_PUSHER_POWER_MODEL=Power Report \ -e POWERAPI_OUTPUT_PUSHER_POWER_TYPE=influxdb2 \ -e POWERAPI_OUTPUT_PUSHER_POWER_URI=127.0.0.1 \ -e POWERAPI_OUTPUT_PUSHER_POWER_PORT=8086 \ @@ -341,11 +341,11 @@ Below you find an example for running the Formula with Docker and Pip: -e POWERAPI_CPU_ERROR_THRESHOLD=2.0 \ -e POWERAPI_DISABLE_DRAM_FORMULA=true \ -e POWERAPI_SENSOR_REPORTS_FREQUENCY=1000 \ - -e POWERAPI_INPUT_PULLER_MODEL=HWPCReport \ + -e POWERAPI_INPUT_PULLER_MODEL=HWPC Report \ -e POWERAPI_INPUT_PULLER_TYPE=csv \ -e POWERAPI_INPUT_PULLER_DIRECTORY=hwpc_reports.d \ -e POWERAPI_INPUT_PULLER_files="hwpc_report_1.json, hwpc_report_2.json" \ - -e POWERAPI_OUTPUT_PUSHER_POWER_MODEL=PowerReport \ + -e POWERAPI_OUTPUT_PUSHER_POWER_MODEL=Power Report \ -e POWERAPI_OUTPUT_PUSHER_POWER_TYPE=influxdb2 \ -e POWERAPI_OUTPUT_PUSHER_POWER_URI=127.0.0.1 \ -e POWERAPI_OUTPUT_PUSHER_POWER_PORT=8086 \ @@ -367,12 +367,12 @@ Below you find an example for running the Formula with Docker and Pip: export POWERAPI_CPU_ERROR_THRESHOLD=2.0 export POWERAPI_DISABLE_DRAM_FORMULA=true export POWERAPI_SENSOR_REPORTS_FREQUENCY=1000 - export POWERAPI_INPUT_PULLER_MODEL=HWPCReport + export POWERAPI_INPUT_PULLER_MODEL=HWPC Report export POWERAPI_INPUT_PULLER_TYPE=mongodb export POWERAPI_INPUT_PULLER_URI=mongodb://127.0.0.1 export POWERAPI_INPUT_PULLER_DB=test export POWERAPI_INPUT_PULLER_COLLECTION=prep - export POWERAPI_OUTPUT_PUSHER_POWER_MODEL=PowerReport + export POWERAPI_OUTPUT_PUSHER_POWER_MODEL=Power Report export POWERAPI_OUTPUT_PUSHER_POWER_TYPE=influxdb2 export POWERAPI_OUTPUT_PUSHER_POWER_URI=127.0.0.1 export POWERAPI_OUTPUT_PUSHER_POWER_PORT=8086 @@ -391,11 +391,11 @@ Below you find an example for running the Formula with Docker and Pip: export POWERAPI_CPU_ERROR_THRESHOLD=2.0 export POWERAPI_DISABLE_DRAM_FORMULA=true export POWERAPI_SENSOR_REPORTS_FREQUENCY=1000 - export POWERAPI_INPUT_PULLER_MODEL=HWPCReport \ + export POWERAPI_INPUT_PULLER_MODEL=HWPC Report \ export POWERAPI_INPUT_PULLER_TYPE=csv \ export POWERAPI_INPUT_PULLER_DIRECTORY=hwpc_reports.d \ export POWERAPI_INPUT_PULLER_files="hwpc_report_1.json, hwpc_report_2.json" \ - export POWERAPI_OUTPUT_PUSHER_POWER_MODEL=PowerReport + export POWERAPI_OUTPUT_PUSHER_POWER_MODEL=Power Report export POWERAPI_OUTPUT_PUSHER_POWER_TYPE=influxdb2 export POWERAPI_OUTPUT_PUSHER_POWER_URI=127.0.0.1 export POWERAPI_OUTPUT_PUSHER_POWER_PORT=8086 @@ -418,7 +418,7 @@ Below you find example Configuration Files to use different input/output and how "stream": true, "input": { "puller": { - "model": "HWPCReport", + "model": "HWPC Report", "type": "mongodb", "uri": "mongodb://127.0.0.1", "db": "test", @@ -449,7 +449,7 @@ Below you find example Configuration Files to use different input/output and how "stream": true, "input": { "puller": { - "model": "HWPCReport", + "model": "HWPC Report", "type": "csv", "directory": "hwpc_reports.d", "files": "hwpc_report_1.json, hwpc_report_2.json", diff --git a/docs/reference/formulas/smartwatts.md.save b/docs/reference/formulas/smartwatts.md.save index 0b0c79d..9969a98 100644 --- a/docs/reference/formulas/smartwatts.md.save +++ b/docs/reference/formulas/smartwatts.md.save @@ -39,7 +39,7 @@ You can use [the following script](../script/smartwatts_install.sh) to install S ## Usage -For running the SmartWatts Formula you need: a Source and a Destination, a Sensor that provides `HWPCReports` and a configuration. +For running the SmartWatts Formula you need: a Source and a Destination, a Sensor that provides `HWPC Reports` and a configuration. ### Source and Destination For running SmartWatts we are using MongoDB as Source and InfluxDB as Destination as dockers containers. @@ -57,7 +57,7 @@ docker run -d --name influx_dest -p 8086:8086 influxdb:1.8 ### Sensor -[HWPC Sensor](../sensors/hwpc-sensor.md) is used in order to get `HWPCReports`. Start by installing the HWPC Sensor (see +[HWPC Sensor](../sensors/hwpc-sensor.md) is used in order to get `HWPC Reports`. Start by installing the HWPC Sensor (see [here](../sensors/hwpc-sensor.md#installation)) and start it (see [here](../sensors/hwpc-sensor.md#usage)). @@ -91,7 +91,7 @@ Below an example is provided by using MongoDB as Source and InfluxDB as Destinat "stream": true, "input": { "puller": { - "model": "HWPCReport", + "model": "HWPC Report", "type": "mongodb", "uri": "mongodb://127.0.0.1", "db": "test", @@ -148,8 +148,8 @@ the installation you used: docker run -t \ --net=host \ powerapi/smartwatts-formula --verbose \ - --input mongodb --model HWPCReport --uri mongodb://127.0.0.1 --db test --collection prep \ - --output influxdb --model PowerReport --uri 127.0.0.1 --port 8086 --db test_result \ + --input mongodb --model HWPC Report --uri mongodb://127.0.0.1 --db test --collection prep \ + --output influxdb --model Power Report --uri 127.0.0.1 --port 8086 --db test_result \ --cpu-base-freq 1900 \ --cpu-error-threshold 2.0 \ --disable-dram-formula \ @@ -161,8 +161,8 @@ the installation you used: ```sh python -m smartwatts \ --verbose \ - --input mongodb --model HWPCReport --uri mongodb://127.0.0.1 --db test --collection prep \ - --output influxdb --model PowerReport --uri 127.0.0.1 --port 8086 --db test_result \ + --input mongodb --model HWPC Report --uri mongodb://127.0.0.1 --db test --collection prep \ + --output influxdb --model Power Report --uri 127.0.0.1 --port 8086 --db test_result \ --cpu-base-freq 1900 \ --cpu-error-threshold 2.0 \ --disable-dram-formula \ @@ -172,7 +172,7 @@ the installation you used: ???+ info "Estimations' Storage" - Your `PowerReports` will be stored on InfluxDB. You can watch them in a grafana by using the [following tutorial](../grafana/grafana.md). + Your `Power Reports` will be stored on InfluxDB. You can watch them in a grafana by using the [following tutorial](../grafana/grafana.md). ???+ tip "Using shortcuts for parameters' names" You use `-` instead of `--`. diff --git a/docs/reference/grafana/grafana.md b/docs/reference/grafana/grafana.md index 8d6e733..685e70c 100644 --- a/docs/reference/grafana/grafana.md +++ b/docs/reference/grafana/grafana.md @@ -10,7 +10,7 @@ alt="viz_by_process" width="1000px"--> This screenshot shows the visualisation of power consumption of a `Firefox` web browser and tools used for monitoring (MongoDB, InfluxDB, Sensor, Formula) -In this tutorial, we describe how to connect a Formula to a Grafana instance by using InfluxDB 2.X as output for PowerReport. +In this tutorial, we describe how to connect a Formula to a Grafana instance by using InfluxDB 2.X as output for Power Report. Then, we will see how to configure Grafana to visualize the power estimation computed by the Formula. This tutorial assumes that you know how launch a Formula and a Sensor to compute power estimation and that you have an InfluxDB 2.X running on your local machine. diff --git a/docs/reference/processors/processors.md b/docs/reference/processors/processors.md index 2fd5ac8..3e35118 100644 --- a/docs/reference/processors/processors.md +++ b/docs/reference/processors/processors.md @@ -3,8 +3,8 @@ Processors enable customized filtering and/or modifications of `Reports`. There are two kinds of processors: -- `PreProcessors`: They are located between the `Puller` and the `Dispatcher`. They are supposed to pre-process the `HWPCReports` before computing estimations. -- `PostProcessors`: They are located between, the `Formula` and the `Pusher`. They process `PowerReports` before storing them on the output storage option. +- `PreProcessors`: They are located between the `Puller` and the `Dispatcher`. They are supposed to pre-process the `HWPC Reports` before computing estimations. +- `PostProcessors`: They are located between, the `Formula` and the `Pusher`. They process `Power Reports` before storing them on the output storage option. Figure below depicts where are they introduced in the architecture of a Software `PowerMeters`. @@ -60,8 +60,8 @@ As notice, a `PreProcessor` is defined inside the `pre-processor` group. In this docker run -t \ --net=host \ powerapi/smartwatts-formula --verbose \ - --input mongodb --model HWPCReport --uri mongodb://127.0.0.1 --db test --collection prep \ - --output influxdb --model PowerReport --uri 127.0.0.1 --port 8086 --db test_result \ + --input mongodb --model HWPC Report --uri mongodb://127.0.0.1 --db test --collection prep \ + --output influxdb --model Power Report --uri 127.0.0.1 --port 8086 --db test_result \ {==--pre-processor k8s --name p1 --api-mode local --puller puller==} \ --cpu-base-freq 1900 \ --cpu-error-threshold 2.0 \ @@ -74,8 +74,8 @@ As notice, a `PreProcessor` is defined inside the `pre-processor` group. In this ```sh python -m smartwatts \ --verbose \ - --input mongodb --model HWPCReport --uri mongodb://127.0.0.1 --db test --collection prep \ - --output influxdb --model PowerReport --uri 127.0.0.1 --port 8086 --db test_result \ + --input mongodb --model HWPC Report --uri mongodb://127.0.0.1 --db test --collection prep \ + --output influxdb --model Power Report --uri 127.0.0.1 --port 8086 --db test_result \ {==--pre-processor k8s --name p1 --api-mode local --puller puller==} \ --cpu-base-freq 1900 \ --cpu-error-threshold 2.0 \ @@ -96,12 +96,12 @@ As notice, a `PreProcessor` is defined inside the `pre-processor` group. In this -e POWERAPI_CPU_ERROR_THRESHOLD=2.0 \ -e POWERAPI_DISABLE_DRAM_FORMULA=true \ -e POWERAPI_SENSOR_REPORTS_FREQUENCY=1000 \ - -e POWERAPI_INPUT_PULLER_MODEL=HWPCReport \ + -e POWERAPI_INPUT_PULLER_MODEL=HWPC Report \ -e POWERAPI_INPUT_PULLER_TYPE=mongodb \ -e POWERAPI_INPUT_PULLER_URI=mongodb://127.0.0.1 \ -e POWERAPI_INPUT_PULLER_DB=test \ -e POWERAPI_INPUT_PULLER_COLLECTION=prep \ - -e POWERAPI_OUTPUT_PUSHER_POWER_MODEL=PowerReport \ + -e POWERAPI_OUTPUT_PUSHER_POWER_MODEL=Power Report \ -e POWERAPI_OUTPUT_PUSHER_POWER_TYPE=influxdb \ -e POWERAPI_OUTPUT_PUSHER_POWER_URI=127.0.0.1 \ -e POWERAPI_OUTPUT_PUSHER_POWER_PORT=8086 \ @@ -121,12 +121,12 @@ As notice, a `PreProcessor` is defined inside the `pre-processor` group. In this export POWERAPI_CPU_ERROR_THRESHOLD=2.0 export POWERAPI_DISABLE_DRAM_FORMULA=true export POWERAPI_SENSOR_REPORTS_FREQUENCY=1000 - export POWERAPI_INPUT_PULLER_MODEL=HWPCReport + export POWERAPI_INPUT_PULLER_MODEL=HWPC Report export POWERAPI_INPUT_PULLER_TYPE=mongodb export POWERAPI_INPUT_PULLER_URI=mongodb://127.0.0.1 export POWERAPI_INPUT_PULLER_DB=test export POWERAPI_INPUT_PULLER_COLLECTION=prep - export POWERAPI_OUTPUT_PUSHER_POWER_MODEL=PowerReport + export POWERAPI_OUTPUT_PUSHER_POWER_MODEL=Power Report export POWERAPI_OUTPUT_PUSHER_POWER_TYPE=influxdb export POWERAPI_OUTPUT_PUSHER_POWER_URI=127.0.0.1 export POWERAPI_OUTPUT_PUSHER_POWER_PORT=8086 @@ -148,7 +148,7 @@ Below an example is provided by using MongoDB as input and InfluxDB as output. "stream": true, "input": { "puller": { - "model": "HWPCReport", + "model": "HWPC Report", "type": "mongodb", "uri": "mongodb://127.0.0.1", "db": "test", diff --git a/docs/reference/reports/reports.md b/docs/reference/reports/reports.md index 25a1dfb..682226c 100644 --- a/docs/reference/reports/reports.md +++ b/docs/reference/reports/reports.md @@ -31,7 +31,7 @@ In the following sections we specify the `$report_specific_fields` for each type ## HWPC Reports -A `HWPCReport` is used to report performance counters and RAPL. +A `HWPC Report` is used to report performance counters and RAPL. Its specific fields are the following: - `groups`: a list of subreport that can be of three kind, `rapl`, `core` and @@ -51,7 +51,7 @@ Its specific fields are the following: } ``` -Below you can find an example of `HWPCReport`: +Below you can find an example of `HWPC Report`: ```json { @@ -120,12 +120,12 @@ Below you can find an example of `HWPCReport`: ## Power Reports -A `PowerReport` is used to transfer information about power consumption estimations. +A `Power Report` is used to transfer information about power consumption estimations. Its specific fields are the following: - `power`: a power value in Watts. -Below you find an exemple of `PowerReport`: +Below you find an exemple of `Power Report`: ```json { diff --git a/docs/script/getting_started/formula/smartwatts-mongodb-csv.json b/docs/script/getting_started/formula/smartwatts-mongodb-csv.json index cefbd37..336e5f2 100644 --- a/docs/script/getting_started/formula/smartwatts-mongodb-csv.json +++ b/docs/script/getting_started/formula/smartwatts-mongodb-csv.json @@ -3,7 +3,7 @@ "stream": true, "input": { "puller_mongodb": { - "model": "HWPCReport", + "model": "HWPC Report", "type": "mongodb", "name": "puller_mongodb", "uri": "mongodb://mongodb:27017", @@ -13,7 +13,7 @@ }, "output": { "pusher_csv": { - "model": "PowerReport", + "model": "Power Report", "type": "csv", "name": "pusher_csv", "directory": "/tmp/csv" diff --git a/docs/script/smartwatts_auto_config.md b/docs/script/smartwatts_auto_config.md index 1ea9382..dce3f47 100644 --- a/docs/script/smartwatts_auto_config.md +++ b/docs/script/smartwatts_auto_config.md @@ -17,7 +17,7 @@ echo " "stream": true, "input": { "puller": { - "model": "HWPCReport", + "model": "HWPC Report", "type": "socket", "uri": "127.0.0.1", "port": 8080, From 64e6b4aa758adc0192e942f8cc3015d022fd7f9c Mon Sep 17 00:00:00 2001 From: Inkedstinct Date: Wed, 30 Oct 2024 11:09:10 +0100 Subject: [PATCH 049/108] docs(smartwatts): Add CSV/CSV examplesw --- docs/reference/formulas/smartwatts.md | 375 ++++++++++++++++++-------- 1 file changed, 256 insertions(+), 119 deletions(-) diff --git a/docs/reference/formulas/smartwatts.md b/docs/reference/formulas/smartwatts.md index f28d7d2..3b363b8 100644 --- a/docs/reference/formulas/smartwatts.md +++ b/docs/reference/formulas/smartwatts.md @@ -18,7 +18,9 @@ Software-Defined Power Meter for Containers](https://hal.inria.fr/hal-02470128) ## Installation The default installation is done through a Docker container. -The different images can be found on the [Docker Hub](https://hub.docker.com/r/powerapi/smartwatts-formula/tags) +The different images can be found on the [Docker Hub](https://hub.docker.com/r/powerapi/smartwatts-formula/tags). + +Alternatively, this installation can be done thanks to Pypi. === "Docker" @@ -33,52 +35,54 @@ The different images can be found on the [Docker Hub](https://hub.docker.com/r/p ## Usage -???+ info "Pre-Requisites" +???+ info "SmartWatts pre-requisites" As SmartWatts is a formula, it needs to consume compatible usage Reports from a [Sensor](../sensors/hwpc-sensor.md). Make sure you have Reports made available in a supported storage option. For running the SmartWatts Formula you need: - a valid configuration -- an input storage -- a output storage containing compatible `HWPC Reports` +- an input storage containing compatible `HWPC Reports` +- an output storage ### Parameters -???+ info "Hardware dependent values" - Some parameters values depend of your hardware. In particular, `cpu-base-freq`. You can obtain this value from `CPU MHz` field by using `lscpu` command. +???+ warning "Hardware dependent values" + Some parameters values depend on your hardware. In particular, `cpu-base-freq`. You can obtain this value from `CPU MHz` field by using `lscpu` command. #### Formula global parameters This table resumes the parameters needed for any Formula (and thus, for SmartWatts configuration) : - -| Parameter | Type | CLI shortcut | Default Value | Description | -| ------------ | ----- | ------------- | ------------- | ------------------------------------ | -| `verbose` | `bool` (flag) | `v` |`NOTSET` | Verbose or quiet mode | -| `stream` | `bool` (flag) | `s` |`False` | Real time or post-mortem mode | -| `sensor-report-sampling-interval` | `int` | - | `1000` | The time in milliseconds between two reports (`stream` = `True`) | -| `input` | `string` | - | - | SmartWatts input, shall match an existing Sensor output and contain HPWCReports. See [here](./smartwatts.md#smartwatts-inputs) | -| `output` | `string` | - | - | SmartWatts output to store Power Report. See [here](./smartwatts.md#smartwatts-outputs) | -| `pre-processor` | `string` | - | - | Pre-Processor to modify reports generated by a sensor. More information about Processors and their related parameters can be found [here](../processors/processors.md) | -| `post-processor` | `string` | - | - | Post-Processor to modify reports generated by a formula. More information about Processors and their related parameters can be found [here](../processors/processors.md) | +??? info "Global parameters" + + | Parameter | Type | CLI shortcut | Default Value | Description | + | ------------ | ----- | ------------- | ------------- | ------------------------------------ | + | `verbose` | `bool` | `v` |`NOTSET` | Verbose or quiet mode | + | `stream` | `bool` | `s` |`False` | Real time or post-mortem mode | + | `sensor-report-sampling-interval` | `int` | - | `1000` | The time in milliseconds between two reports (`stream` = `True`) | + | `input` | `string` | - | - | SmartWatts input, shall match an existing Sensor output and contain HPWCReports. See [here](./smartwatts.md#smartwatts-inputs) | + | `output` | `string` | - | - | SmartWatts output to store Power Report. See [here](./smartwatts.md#smartwatts-outputs) | + | `pre-processor` | `string` | - | - | Pre-Processor to modify reports generated by a sensor. More information about Processors and their related parameters can be found [here](../processors/processors.md) | + | `post-processor` | `string` | - | - | Post-Processor to modify reports generated by a formula. More information about Processors and their related parameters can be found [here](../processors/processors.md) | #### SmartWatts specific parameters This table resumes the parameters specific to SmartWatts configuration : - -| Parameter | Type | CLI shortcut | Default Value | Description | -| ------------- | ----- | ------------- | ------------- | ------------------------------------ | -|`disable-cpu-formula` | `bool` (flag) | - | `false` | Disable CPU Formula | -|`disable-dram-formula` | `bool` (flag) | - | `false` | Disable RAM Formula | -|`cpu-rapl-ref-event` | `string` | - | `"RAPL_ENERGY_PKG"` | RAPL event used as reference for the CPU power models | -|`dram-rapl-ref-event` | `string` | - | `"RAPL_ENERGY_DRAM"` | RAPL event used as reference for the DRAM power models | -|`cpu-tdp` | `int` | - | `125` | CPU TDP (in Watt)| -|`cpu-base-clock` | `int` | - | `100` | CPU base clock (in MHz) | -|`cpu-base-freq` | `int` | - | `2100` | CPU base frequency (in MHz), depend of your hardware. You can obtain this value from `CPU MHz` field by using `lscpu` command. | -|`cpu-error-threshold` | `float` | - | `2.0` | Error threshold for the CPU power models (in Watts) | -|`dram-error-threshold` | `float` | - | `2.0` | Error threshold for the DRAM power models (in Watts) | -|`learn-min-samples-required` | `int` | - | `10` | Minimum amount of samples required before trying to learn a power model | -|`learn-history-window-size` | `int` | - | `60` | Size of the history window used to keep samples to learn from | -|`sensor-reports-frequency` | `int` | - | `1000` | The frequency with which measurements are made (in milliseconds) | +??? info "SmartWatts specific parameters" + + | Parameter | Type | CLI shortcut | Default Value | Description | + | ------------- | ----- | ------------- | ------------- | ------------------------------------ | + |`disable-cpu-formula` | `bool` | - | `false` | Disable CPU Formula | + |`disable-dram-formula` | `bool` | - | `false` | Disable RAM Formula | + |`cpu-rapl-ref-event` | `string` | - | `"RAPL_ENERGY_PKG"` | RAPL event used as reference for the CPU power models | + |`dram-rapl-ref-event` | `string` | - | `"RAPL_ENERGY_DRAM"` | RAPL event used as reference for the DRAM power models | + |`cpu-tdp` | `int` | - | `125` | CPU TDP (in Watt)| + |`cpu-base-clock` | `int` | - | `100` | CPU base clock (in MHz) | + |`cpu-base-freq` | `int` | - | `2100` | CPU base frequency (in MHz), depend of your hardware. You can obtain this value from `CPU MHz` field by using `lscpu` command. | + |`cpu-error-threshold` | `float` | - | `2.0` | Error threshold for the CPU power models (in Watts) | + |`dram-error-threshold` | `float` | - | `2.0` | Error threshold for the DRAM power models (in Watts) | + |`learn-min-samples-required` | `int` | - | `10` | Minimum amount of samples required before trying to learn a power model | + |`learn-history-window-size` | `int` | - | `60` | Size of the history window used to keep samples to learn from | + |`sensor-reports-frequency` | `int` | - | `1000` | The frequency with which measurements are made (in milliseconds) | #### SmartWatts Inputs @@ -93,47 +97,51 @@ We can choose those among the following list, depending on where your Sensor out ##### MongoDB Input Table below depicts the different parameters for MongoDB type input: +??? info "MongoDB Input parameters" -| Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | -| ------------- | ----- | ------------- | ------------- | ---------- | ------------------------------------ | -| `uri` | string | `u` | - | Yes | The IP address of your MongoDB instance | -| `database` | string | `d` | - | Yes | The name of your database | -| `collection` | string | `c` | - | Yes | The name of the collection inside `db` | -| `name` | string | `n` | puller_mongodb | No | The related puller name | -| `model` | string | `m` | HWPC Report | No | The Report type stored by the database | + | Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | + | ------------- | ----- | ------------- | ------------- | ---------- | ------------------------------------ | + | `uri` | string | `u` | - | Yes | The IP address of your MongoDB instance | + | `database` | string | `d` | - | Yes | The name of your database | + | `collection` | string | `c` | - | Yes | The name of the collection inside `db` | + | `name` | string | `n` | puller_mongodb | No | The related puller name | + | `model` | string | `m` | HWPC Report | No | The Report type stored by the database | ##### CSV Input Table below depicts the different parameters for CSV type input: - -| Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | -| ------------- | ----- | ------------- | ------------- | ----------| ------------------------------------ | -| `files` | string | `f` | "" | No | The list of input CSV files with the format "file1,file2,file3..." | -| `directory` | string | `d` | "." (Current directory) | No |The directory where output CSV files will be written | -| `name` | string | `n` | puller_csv | No | The related puller name | -| `model` | string | `m` | HWPC Report | No | The Report type stored by the database | - +??? info "CSV Input parameters" + + | Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | + | ------------- | ----- | ------------- | ------------- | ----------| ------------------------------------ | + | `files` | string | `f` | "" | No | The list of input CSV files with the format "file1,file2,file3..." | + | `directory` | string | `d` | "." (Current directory) | No |The directory where output CSV files will be written | + | `name` | string | `n` | puller_csv | No | The related puller name | + | `model` | string | `m` | HWPC Report | No | The Report type stored by the database | + ##### Socket input Table below depicts the different parameters for CSV type input: - -| Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | -| ------------- | ----- | ------------- | ------------- | ----------| ------------------------------------ | -| `port` | int | `P` | - | Yes | The port of communication | -| `uri` | string | `U` | - | Yes | The IP address of the machine running the socket | -| `name` | string | `n` | puller_socket | No | The related puller name | -| `model` | string | `m` | HWPC Report | No | The Report type stored by the database | - +??? info "Socket input parameters" + + | Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | + | ------------- | ----- | ------------- | ------------- | ----------| ------------------------------------ | + | `port` | int | `P` | - | Yes | The port of communication | + | `uri` | string | `U` | - | Yes | The IP address of the machine running the socket | + | `name` | string | `n` | puller_socket | No | The related puller name | + | `model` | string | `m` | HWPC Report | No | The Report type stored by the database | + ##### FileDB input Table below depicts the different parameters for CSV type input: - -| Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | -| ------------- | ----- | ------------- | ------------- | ----------| ------------------------------------ | -| `filename` | string | `f` | - | yes | Path to the file | -| `name` | string | `n` | puller_filedb| No | The related puller name | -| `model` | string | `m` | HWPC Report | No | The Report type stored by the database | - +??? info "FileDB input parameters" + + | Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | + | ------------- | ----- | ------------- | ------------- | ----------| ------------------------------------ | + | `filename` | string | `f` | - | yes | Path to the file | + | `name` | string | `n` | puller_filedb| No | The related puller name | + | `model` | string | `m` | HWPC Report | No | The Report type stored by the database | + #### SmartWatts Outputs On the same principle, SmartWatts needs to output its Power Report. @@ -148,69 +156,75 @@ We can choose it among the following list, depending on where your Sensor output ##### MongoDB output Table below depicts the different parameters for MongoDB type output: - -| Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | -| ------------- | ----- | ------------- | ------------- | ---------- | ------------------------------------ | -| `uri` | string | `u` | - | Yes | The IP address of your MongoDB instance | -| `database` | string | `d` | - | Yes | The name of your database | -| `collection` | string | `c` | - | Yes | The name of the collection inside `db` | -| `name` | string | `n` | pusher_mongodb | No | The related puller name | -| `model` | string | `m` | Power Report | No | The Report type stored by the database | +??? info "MongoDB output parameters" + + | Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | + | ------------- | ----- | ------------- | ------------- | ---------- | ------------------------------------ | + | `uri` | string | `u` | - | Yes | The IP address of your MongoDB instance | + | `database` | string | `d` | - | Yes | The name of your database | + | `collection` | string | `c` | - | Yes | The name of the collection inside `db` | + | `name` | string | `n` | pusher_mongodb | No | The related pusher name | + | `model` | string | `m` | Power Report | No | The Report type stored by the database | ##### CSV output Table below depicts the different parameters for CSV type output: +??? info "CSV output parameters" + + | Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | + | ------------- | ----- | ------------- | ------------- | ----------| ------------------------------------ | + | `files` | string | `f` | "" | No | The list of input CSV files with the format "file1,file2,file3..." | + | `directory` | string | `d` | "." (Current directory) | No |The directory where output CSV files will be written | + | `name` | string | `n` | pusher_csv | No | The related pusher name | + | `model` | string | `m` | Power Report | No | The Report type stored by the database | + -| Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | -| ------------- | ----- | ------------- | ------------- | ----------| ------------------------------------ | -| `files` | string | `f` | "" | No | The list of input CSV files with the format "file1,file2,file3..." | -| `directory` | string | `d` | "." (Current directory) | No |The directory where output CSV files will be written | -| `name` | string | `n` | pusher_csv | No | The related puller name | -| `model` | string | `m` | Power Report | No | The Report type stored by the database | ##### InfluxDB output ???+ tip "Set up influxdb 2.X for the first time" If it is the first time that you are using `influxdb 2.X`, there are several methods (UI, CLI, API) to make a set up. Please check [here](https://docs.influxdata.com/influxdb/v2/get-started/setup/) for more information. - Table below depicts the different parameters for InfluxDB2 type output: - -| Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | -| ------------- | ----- | ------------- | ------------- | ---------- | ------------------------------------ | -|`uri` | string | `u` | - | Yes | The IP address of your Influxdb instance. It can contain the port number| -|`db` | string | `d` | - | Yes | The name of your bucket (database) | -|`port` | int | `p` | - | Yes | The port of communication. It is not mandatory if it is indicated in the `uri` | -|`token` | string | `k` | - | Yes | The token for accessing the database. The token owner must have write/read permissions on the bucket | -|`org` | string | `g` | - | Yes | The name of the organization associated to the bucket | -|`tags` | string | `t` | - | No | List of metadata keys of the report separated by `,` that will be kept. `sensor` and `target` are always kept as report metadata | -|`name` | string | `n` | `"pusher_influxdb2"` | No | The related pusher name | -|`model` | string | `m` | `"Power Report"` | No | The Report type stored by the database | +??? info "InfluxDB output parameters" + + | Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | + | ------------- | ----- | ------------- | ------------- | ---------- | ------------------------------------ | + |`uri` | string | `u` | - | Yes | The IP address of your Influxdb instance. It can contain the port number| + |`db` | string | `d` | - | Yes | The name of your bucket (database) | + |`port` | int | `p` | - | Yes | The port of communication. It is not mandatory if it is indicated in the `uri` | + |`token` | string | `k` | - | Yes | The token for accessing the database. The token owner must have write/read permissions on the bucket | + |`org` | string | `g` | - | Yes | The name of the organization associated to the bucket | + |`tags` | string | `t` | - | No | List of metadata keys of the report separated by `,` that will be kept. `sensor` and `target` are always kept as report metadata | + |`name` | string | `n` | `"pusher_influxdb2"` | No | The related pusher name | + |`model` | string | `m` | `"Power Report"` | No | The Report type stored by the database | ##### Prometheus output Table below depicts the different parameters for Prometheus type output: - -| Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | -| ------------- | ----- | ------------- | ------------- | ---------- | ------------------------------------ | -|`uri` | string | `u` | `127.0.0.1` | No | The IP address of your Prometheus instance | -|`port` | int | `p` | - | Yes | The port of communication | -|`tags` | string | `t` | - | No | List of metadata keys of the report separated by `,` that will be kept. `sensor` and `target` are always kept as report metadata | -|`metric-name` | string | `M` | - | Yes | The exposed metric name | -|`metric-description` | string | `d` | `"energy consumption"` | No | The exposed metric description | -|`name` | string | `n` | `"pusher_prom"` | No | The related pusher name | -|`model` | string | `m` | `"Power Report"` | No | The Report type exposed by Prometheus | +??? info "Prometheus output parameters" + + | Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | + | ------------- | ----- | ------------- | ------------- | ---------- | ------------------------------------ | + |`uri` | string | `u` | `127.0.0.1` | No | The IP address of your Prometheus instance | + |`port` | int | `p` | - | Yes | The port of communication | + |`tags` | string | `t` | - | No | List of metadata keys of the report separated by `,` that will be kept. `sensor` and `target` are always kept as report metadata | + |`metric-name` | string | `M` | - | Yes | The exposed metric name | + |`metric-description` | string | `d` | `"energy consumption"` | No | The exposed metric description | + |`name` | string | `n` | `"pusher_prom"` | No | The related pusher name | + |`model` | string | `m` | `"Power Report"` | No | The Report type exposed by Prometheus | ##### FileDB output Table below depicts the different parameters for FileDB type output: - -| Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | -| ------------- | ----- | ------------- | ------------- | ----------| ------------------------------------ | -| `filename` | string | `f` | - | Yes | Path to the file | -| `name` | string | `n` | pusher_filedb | No | The related pusher name | -| `model` | string | `m` | Power Report | No | The Report type stored by the database | +??? info "FileDB output parameters" + + | Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | + | ------------- | ----- | ------------- | ------------- | ----------| ------------------------------------ | + | `filename` | string | `f` | - | Yes | Path to the file | + | `name` | string | `n` | pusher_filedb | No | The related pusher name | + | `model` | string | `m` | Power Report | No | The Report type stored by the database | #### Running the Formula @@ -227,7 +241,7 @@ You will find below different examples in order to run a Formula, depending on y In order to run the Formula, you can execute one of the following command lines, depending on the installation you use: -???+ example "Examples using docker" +??? example "Examples using docker" === "Docker with MongoDB/InfluxDB2" @@ -256,8 +270,22 @@ In order to run the Formula, you can execute one of the following command lines, --disable-dram-formula \ --sensor-reports-frequency 1000 ``` + + === "Docker with CSV/CSV" + + ```sh hl_lines="4 5" + docker run -t \ + --net=host \ + powerapi/smartwatts-formula --verbose \ + --input csv --model HWPC Report --directory hwcp\_reports.d --files "hwpc\_report\_1, hwpc\_report\_2" \ + --output csv --directory power_reports.d \ + --cpu-base-freq 1900 \ + --cpu-error-threshold 2.0 \ + --disable-dram-formula \ + --sensor-reports-frequency 1000 + ``` -???+ example "Examples using Pip" +??? example "Examples using Pip" === "Pip with MongoDB/InfluxDB2" @@ -284,12 +312,25 @@ In order to run the Formula, you can execute one of the following command lines, --disable-dram-formula \ --sensor-reports-frequency 1000 ``` + + === "Pip with CSV/CSV" + + ```sh hl_lines="3 4" + python -m smartwatts \ + --verbose \ + --input csv --model HWPC Report --directory hwcp\_reports.d --name puller\_csv --files "hwpc\_report\_1.json, hwpc\_report\_2.json" \ + --output csv --directory power_reports.d\ + --cpu-base-freq 1900 \ + --cpu-error-threshold 2.0 \ + --disable-dram-formula \ + --sensor-reports-frequency 1000 + ``` ???+ info "Estimations' Storage" Your `Power Reports` will be stored on InfluxDB2. You can watch them in a grafana by using the [following tutorial](../grafana/grafana.md). -#### Running the Formula with Environment Variables +#### Running the Formula via Environment Variables Parameters are defined by using the prefixes `POWERAPI_`, `POWERAPI_INPUT_` and `POWERAPI_OUTPUT_` in the names of Environment Variables. The following conventions are used: @@ -302,11 +343,11 @@ where `PARAMETER_NAME` refers to names of parameters in upper case (e.g., `VERBO Below you find an example for running the Formula with Docker and Pip: -???+ example "Examples using docker" - +??? example "Examples using docker" + === "Docker with MongoDB/InfluxDB2" - ```sh hl_lines="9 10 11 12 13 14 15 16 17 18 19 20" + ```sh hl_lines="9-20" docker run -t \ --net=host \ -e POWERAPI_VERBOSE=true \ @@ -329,10 +370,10 @@ Below you find an example for running the Formula with Docker and Pip: -e POWERAPI_OUTPUT_PUSHER_POWER_TOKEN=mytoken \ powerapi/smartwatts-formula ``` - + === "Docker with CSV/InfluxDB2" - ```sh hl_lines="9 10 11 12 13 14 15 16 17 18 19" + ```sh hl_lines="9-19" docker run -t \ --net=host \ -e POWERAPI_VERBOSE=true \ @@ -354,13 +395,34 @@ Below you find an example for running the Formula with Docker and Pip: -e POWERAPI_OUTPUT_PUSHER_POWER_TOKEN=mytoken \ powerapi/smartwatts-formula ``` + + === "Docker with CSV/CSV" + + ```sh hl_lines="9-15" + docker run -t \ + --net=host \ + -e POWERAPI_VERBOSE=true \ + -e POWERAPI_STREAM=true \ + -e POWERAPI_CPU_BASE_FREQ=1900 \ + -e POWERAPI_CPU_ERROR_THRESHOLD=2.0 \ + -e POWERAPI_DISABLE_DRAM_FORMULA=true \ + -e POWERAPI_SENSOR_REPORTS_FREQUENCY=1000 \ + -e POWERAPI_INPUT_PULLER_MODEL=HWPC Report \ + -e POWERAPI_INPUT_PULLER_TYPE=csv \ + -e POWERAPI_INPUT_PULLER_DIRECTORY=hwpc_reports.d \ + -e POWERAPI_INPUT_PULLER_files="hwpc_report_1.json, hwpc_report_2.json" \ + -e POWERAPI_OUTPUT_PUSHER_POWER_MODEL=Power Report \ + -e POWERAPI_OUTPUT_PUSHER_POWER_TYPE=csv \ + -e POWERAPI_OUTPUT_PUSHER_POWER_DIRECTORY=power_reports.d \ + powerapi/smartwatts-formula + ``` -???+ example "Examples using docker" +??? example "Examples using pip" === "Pip with MongoDB/InfluxDB2" - ```sh hl_lines="7 8 9 10 11 12 13 14 15 16 17 18" + ```sh hl_lines="7-18" export POWERAPI_VERBOSE=true export POWERAPI_STREAM=false export POWERAPI_CPU_BASE_FREQ=1900 @@ -381,10 +443,10 @@ Below you find an example for running the Formula with Docker and Pip: export POWERAPI_OUTPUT_PUSHER_POWER_TOKEN=mytoken python -m smartwatts ``` - + === "Pip with CSV/InfluxDB2" - ```sh hl_lines="7 8 9 10 11 12 13 14 15 16 17" + ```sh hl_lines="7-17" export POWERAPI_VERBOSE=true export POWERAPI_STREAM=false export POWERAPI_CPU_BASE_FREQ=1900 @@ -404,15 +466,34 @@ Below you find an example for running the Formula with Docker and Pip: export POWERAPI_OUTPUT_PUSHER_POWER_TOKEN=mytoken python -m smartwatts ``` + + === "Pip with CSV/CSV" + + ```sh hl_lines="7-13" + export POWERAPI_VERBOSE=true + export POWERAPI_STREAM=false + export POWERAPI_CPU_BASE_FREQ=1900 + export POWERAPI_CPU_ERROR_THRESHOLD=2.0 + export POWERAPI_DISABLE_DRAM_FORMULA=true + export POWERAPI_SENSOR_REPORTS_FREQUENCY=1000 + export POWERAPI_INPUT_PULLER_MODEL=HWPC Report \ + export POWERAPI_INPUT_PULLER_TYPE=csv \ + export POWERAPI_INPUT_PULLER_DIRECTORY=hwpc_reports.d \ + export POWERAPI_INPUT_PULLER_files="hwpc_report_1.json, hwpc_report_2.json" \ + export POWERAPI_OUTPUT_PUSHER_POWER_MODEL=Power Report + export POWERAPI_OUTPUT_PUSHER_POWER_TYPE=csv + export POWERAPI_OUTPUT_PUSHER_POWER_DIRECTORY=power_reports.d + python -m smartwatts + ``` -#### Running the Formula with a Configuration File +#### Running the Formula via Configuration File Below you find example Configuration Files to use different input/output and how to use it with Docker or Pip: -???+ example "Examples configurations files" +??? example "Examples configurations files" === "Configuration file using MongoDB/InfluxDB2" - ```json hl_lines="5 6 7 8 9 10 11 14 15 16 17 18 19 20 21" title="config_file.json" + ```json hl_lines="5-21" title="config_file.json" { "verbose": true, "stream": true, @@ -441,9 +522,39 @@ Below you find example Configuration Files to use different input/output and how "sensor-reports-frequency": 1000 } ``` + + === "Configuration file using CSV/InfluxDB2" + ```json hl_lines="5-20" title="config_file.json" + { + "verbose": true, + "stream": true, + "input": { + "puller": { + "model": "HWPC Report", + "type": "csv", + "directory": "hwpc_reports.d", + "files": "hwpc_report_1.json, hwpc_report_2.json", + } + }, + "output": { + "pusher_power": { + "type": "influxdb2", + "uri": "127.0.0.1", + "port": 8086, + "db": "power_consumption", + "org": "org_test", + "token": "mytoken" + } + }, + "cpu-base-freq": 1900, + "cpu-error-threshold": 2.0, + "disable-dram-formula": true, + "sensor-reports-frequency": 1000 + } + ``` === "Configuration file using CSV/InfluxDB2" - ```json hl_lines="5 6 7 8 9 10 13 14 15 16 17 18 19 20" title="config_file.json" + ```json hl_lines="5-20" title="config_file.json" { "verbose": true, "stream": true, @@ -471,6 +582,32 @@ Below you find example Configuration Files to use different input/output and how "sensor-reports-frequency": 1000 } ``` + + === "Configuration file using CSV/CSV" + ```json hl_lines="5-16" title="config_file.json" + { + "verbose": true, + "stream": true, + "input": { + "puller": { + "model": "HWPC Report", + "type": "csv", + "directory": "hwpc_reports.d", + "files": "hwpc_report_1.json, hwpc_report_2.json", + } + }, + "output": { + "pusher_power": { + "type": "csv", + "directory": "power_reports.d", + } + }, + "cpu-base-freq": 1900, + "cpu-error-threshold": 2.0, + "disable-dram-formula": true, + "sensor-reports-frequency": 1000 + } + ``` Once you have your configuration file, run SmartWatts using one of the following command lines, depending on the installation you use: From 4c548b690566000570e7ad0e1687c557a4448cf4 Mon Sep 17 00:00:00 2001 From: Inkedstinct Date: Wed, 30 Oct 2024 11:10:48 +0100 Subject: [PATCH 050/108] docs(getting-started): Synchro archive with cloned --- docs/script/getting_started.tar.gz | Bin 4874 -> 4969 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/script/getting_started.tar.gz b/docs/script/getting_started.tar.gz index eaebd26d1574ffccbff3fa95378b9853f0340251..21705459278eab661c9a1b369bad1350a7460f2b 100644 GIT binary patch delta 4866 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 delta 4773 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 From 7f63b2eefb5fb55d102cf5dd7254b3129dd7916e Mon Sep 17 00:00:00 2001 From: chachignot Date: Wed, 30 Oct 2024 14:14:19 +0100 Subject: [PATCH 051/108] docs(getting-started): Merged pretty print with start.py --- .../getting_started/docker-compose.yaml | 10 +- docs/script/getting_started/pretty_print.py | 112 ------------------ .../getting_started/sensor/hwpc-mongodb.json | 10 +- docs/script/getting_started/start.py | 90 +++++++++++++- 4 files changed, 91 insertions(+), 131 deletions(-) delete mode 100644 docs/script/getting_started/pretty_print.py diff --git a/docs/script/getting_started/docker-compose.yaml b/docs/script/getting_started/docker-compose.yaml index bf08e15..9934d73 100644 --- a/docs/script/getting_started/docker-compose.yaml +++ b/docs/script/getting_started/docker-compose.yaml @@ -11,15 +11,6 @@ services: profiles: - mongodb - # CSV (Default is a dummy image, can be used to add treatments on CSV files) - csv: - container_name: csv - image: ${CSV_IMAGE} - volumes: - - ${PWD}/csv:/tmp/csv - profiles: - - csv - ############################################ # POWERAPI # @@ -51,6 +42,7 @@ services: # PowerAPI Formula formula: + user: $UID:$GUID container_name: formula image: ${FORMULA_IMAGE} command: diff --git a/docs/script/getting_started/pretty_print.py b/docs/script/getting_started/pretty_print.py deleted file mode 100644 index 768f88d..0000000 --- a/docs/script/getting_started/pretty_print.py +++ /dev/null @@ -1,112 +0,0 @@ -# Copyright (c) 2024, INRIA -# Copyright (c) 2024, University of Lille -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, this -# list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# * Neither the name of the copyright holder nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import csv -import os - - -def load_data(directory='./csv'): - """ - Load CSV files from the specified directory and return the data as a list of dictionaries. - """ - data = [] - for root, _, files in os.walk(directory): - for filename in files: - if filename.endswith('.csv'): - file_path = os.path.join(root, filename) - with open(file_path, mode='r', newline='', encoding='UTF-8') as f: - data.extend(csv.DictReader(f)) - return data - - -def calculate_statistics(data, scope): - """ - Calculate average, maximum, and minimum consumption for the given scope (cpu or dram). - """ - stats = {} - for row in data: - if row['scope'] == scope and row['target'] != 'rapl': - target = row['target'] - consumption = float(row['power']) - stats.setdefault(target, []).append(consumption) - - return { - target: { - 'avg': sum(consumptions) / len(consumptions), - 'max': max(consumptions), - 'min': min(consumptions) - } for target, consumptions in stats.items() - } - - -def print_statistics(stats, title): - """ - Print statistics (average, max, min) for the given data in a formatted table. - """ - if not stats: - return - - print(f"\n{title}\n") - print(f"{'Target':<20} {'Average consumption':<20} {'Maximum consumption':<20} {'Minimum consumption':<20}") - print("=" * 80) - - total = {'avg': 0, 'max': 0, 'min': 0} - for target, values in stats.items(): - if target != 'global': - print(f"{target:<20} {values['avg']:<20.2f} {values['max']:<20.2f} {values['min']:<20.2f}") - else: - total = values - - print("-" * 80) - print(f"{'Global':<20} {total['avg']:<20.2f} {total['max']:<20.2f} {total['min']:<20.2f}") - - -def start_pretty_print(): - """ - Pretty print the CPU and DRAM power consumption statistics from CSV files. - """ - print("The consumptions are given in Watt, note that the precision depends on the configuration file\n") - - data = load_data() - - # Calculate and print CPU statistics - cpu_stats = calculate_statistics(data, 'cpu') - print_statistics(cpu_stats, "CPU Consumption Statistics :") - - # Calculate and print DRAM statistics - dram_stats = calculate_statistics(data, 'dram') - print_statistics(dram_stats, "DRAM Consumption Statistics :") - - # Could add the GPU statistics here - - print("\nFor more precise evaluation, consult the PowerAPI documentation to adjust configurations.\n") - - -if __name__ == '__main__': - start_pretty_print() diff --git a/docs/script/getting_started/sensor/hwpc-mongodb.json b/docs/script/getting_started/sensor/hwpc-mongodb.json index 7e2a318..4ee5f0b 100644 --- a/docs/script/getting_started/sensor/hwpc-mongodb.json +++ b/docs/script/getting_started/sensor/hwpc-mongodb.json @@ -1,12 +1,13 @@ { "name": "sensor", "verbose": true, - "frequency": 500, + "frequency": 1000, + "cgroup_basepath": "/sys/fs/cgroup/", "output": { "type": "mongodb", - "uri": "mongodb://127.0.0.1", + "uri": "mongodb://mongodb:27017", "database": "db_sensor", - "collection": "report_0" + "collection": "prep" }, "system": { "rapl": { @@ -32,6 +33,5 @@ "INSTRUCTIONS_RETIRED" ] } - }, - "cgroup_basepath": "/sys/fs/cgroup/" + } } \ No newline at end of file diff --git a/docs/script/getting_started/start.py b/docs/script/getting_started/start.py index dd90c81..4d2caa5 100644 --- a/docs/script/getting_started/start.py +++ b/docs/script/getting_started/start.py @@ -52,7 +52,10 @@ def signal_handler(sig, frame): def docker_start(time): - os.system("docker-compose up -d") + id1 = os.popen("id -u").read() + id2 = os.popen("id -g").read() + print("UID=" + id1[:-1] + " GUID=" + id2[:-1] + " docker compose up -d") + os.system("UID=" + id1[:-1] + " GUID=" + id2[:-1] + " docker compose up -d") os.system("docker compose logs sensor -f &") os.system("docker compose logs formula -f &") os.system("sleep " + str(time)) @@ -62,7 +65,7 @@ def docker_start(time): def docker_stop(): os.system("set -ueo pipefail") os.system("set +x") - os.system("docker-compose down") + os.system("docker compose down") def load_data(): @@ -75,6 +78,82 @@ def load_data(): return data +def load_result(directory='./csv'): + """ + Load CSV files from the specified directory and return the data as a list of dictionaries. + """ + data = [] + for root, _, files in os.walk(directory): + for filename in files: + if filename.endswith('.csv'): + file_path = os.path.join(root, filename) + with open(file_path, mode='r', newline='', encoding='UTF-8') as f: + data.extend(csv.DictReader(f)) + return data + + +def calculate_statistics(data, scope): + """ + Calculate average, maximum, and minimum consumption for the given scope (cpu or dram). + """ + stats = {} + for row in data: + if row['scope'] == scope and row['target'] != 'rapl': + target = row['target'] + consumption = float(row['power']) + stats.setdefault(target, []).append(consumption) + + return { + target: { + 'avg': sum(consumptions) / len(consumptions), + 'max': max(consumptions), + 'min': min(consumptions) + } for target, consumptions in stats.items() + } + + +def print_statistics(stats, title): + """ + Print statistics (average, max, min) for the given data in a formatted table. + """ + if not stats: + return + + print(f"\n{title}\n") + print(f"{'Target':<20} {'Average consumption':<20} {'Maximum consumption':<20} {'Minimum consumption':<20}") + print("=" * 80) + + total = {'avg': 0, 'max': 0, 'min': 0} + for target, values in stats.items(): + if target != 'global': + print(f"{target:<20} {values['avg']:<20.2f} {values['max']:<20.2f} {values['min']:<20.2f}") + else: + total = values + + print("-" * 80) + print(f"{'Global':<20} {total['avg']:<20.2f} {total['max']:<20.2f} {total['min']:<20.2f}") + + +def start_pretty_print(): + """ + Pretty print the CPU and DRAM power consumption statistics from CSV files. + """ + print("The consumptions are given in Watt, note that the precision depends on the configuration file\n") + + data = load_result() + + # Calculate and print CPU statistics + cpu_stats = calculate_statistics(data, 'cpu') + print_statistics(cpu_stats, "CPU Consumption Statistics :") + + # Calculate and print DRAM statistics + dram_stats = calculate_statistics(data, 'dram') + print_statistics(dram_stats, "DRAM Consumption Statistics :") + + # Could add the GPU statistics here + + print("\nFor more precise evaluation, consult the PowerAPI documentation to adjust configurations.\n") + def find_cpu(data): """ Find the cpu in the list of compatible cpu @@ -211,7 +290,8 @@ def start_demo(): print("\nStarting the demo...") print("-" * 80) - print("The demo will run for " + val + "\n") + print("The demo will run for " + str(val) + " seconds\n") + print("If you wish to stop it, Ctrl-C will do so and stop the docker compose stack\n") docker_start(val) @@ -233,9 +313,9 @@ def start_demo(): else: print("\nThe demo has ended, " "you can see the result under the /csv directory" - " or use 'python3 pretty_print.py' " - "to get a quick summary of the result in the terminal\n") + " here is a quick summary\n") + start_pretty_print() if __name__ == '__main__': start_demo() From 872d82fd77c0d5b57c71408ae1b349c9f313dcdd Mon Sep 17 00:00:00 2001 From: Kellian Leveque Date: Wed, 30 Oct 2024 15:44:22 +0100 Subject: [PATCH 052/108] docs(getting-started): Improve clarity and formatting --- docs/getting_started.md | 64 ++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/docs/getting_started.md b/docs/getting_started.md index 8563179..7b1119f 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -2,20 +2,20 @@ In this tutorial, we will guide you through the first steps to get started with PowerAPI. The objective is to get a quick view of the capabilities of PowerAPI, by monitoring a process and getting a quick glimpse at the energy consumption. -A few things are required before we start : +A few things are required before we start: -- A compatible processor, you can see the compatible CPU architecture [here](./reference/sensors/hwpc-sensor.md#) and you can look on the following pages to find your CPU architecture : +- A compatible processor (you can see the compatible CPU architecture [here](./reference/sensors/hwpc-sensor.md#)), and you can look on the following pages to find your CPU architecture: * For [Intel Processor](https://en.wikipedia.org/wiki/List_of_Intel_processors) * For [Intel Xeon Processor](https://en.wikipedia.org/wiki/List_of_Intel_Xeon_processors) * For [AMD Processor](https://en.wikipedia.org/wiki/Table_of_AMD_processors) - A python installation ready - Docker & Docker-Compose ready (refer to [this official documentation](https://docs.docker.com/engine/install/) and the [post-install steps](https://docs.docker.com/engine/install/linux-postinstall/) if needed !) - Root access -- Optionnal : [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) to proceed by clonning the repository +- Optional : [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) to proceed by clonning the repository ## Which components to get a complete stack -If you wish to get started as soon as possible, the following archive will allow you to deploy the following elements : +If you wish to get started as soon as possible, the archive below will allow you to deploy the following elements: 1. A MongoDB instance to store the [Sensor](./reference/sensors/hwpc-sensor.md) Reports @@ -32,50 +32,50 @@ quick glimpse ## Preparation +You can either download the archive by cloning the repository or using wget. -1. Clone the repository: -```sh -git clone https://github.com/powerapi-ng/powerapi-ng.github.io.git -cd powerapi-ng.github.io/docs/script/getting-started -``` - -2. Download the archive: -``` -wget -c https://raw.githubusercontent.com/powerapi-ng/powerapi-ng.github.io/refs/heads/master/docs/script/getting_started.tar.gz -O - | tar -xz -cd getting_started -``` +=== "git" + ```sh + git clone https://github.com/powerapi-ng/powerapi-ng.github.io.git + cd powerapi-ng.github.io/docs/script/getting-started + ``` -From this archive, you will have all the necessary files to get started, let us break down each element. +=== "wget" + ``` + wget -c https://raw.githubusercontent.com/powerapi-ng/powerapi-ng.github.io/refs/heads/master/docs/script/getting_started.tar.gz -O - | tar -xz + cd getting_started + ``` +At this stage, you will have all the essential files to begin. Let's go through each element in detail. ### Archive content ```sh -|getting_started/ -|--csv/ -|--fomula/ -|----smartwatts-mongodb-csv.json -|--sensor/ -|----hwpc-mongodb.json -|--start.sh -|--start.py -|--stop.sh -|--pretty_print.py -|--docker-compose.yaml -|--.env +getting_started/ + |--csv/ + |--fomula/ + |----smartwatts-mongodb-csv.json + |--sensor/ + |----hwpc-mongodb.json + |--start.sh + |--start.py + |--stop.sh + |--pretty_print.py + |--docker-compose.yaml + |--.env ``` #### HWPC-Sensor and SmartWatts Configuration -As described in the [HWPC-Sensor Documentation](./reference/sensors/hwpc-sensor.md#global-parameters) and in the [SmartWatts Documentation](./reference/formulas/smartwatts.md#global-parameters) +As described in the [HWPC-Sensor Documentation](./reference/sensors/hwpc-sensor.md#global-parameters) and in the [SmartWatts Documentation](./reference/formulas/smartwatts.md#global-parameters), several parameters can be set, both globally and for specific Groups monitored for the sensor or the formula. -The provided docker-compose.yaml file use configuration files and the **.env** to set those parameters. -You can find example of both those configuration files in the archive under the **formula** and **sensor** directories. +The provided docker-compose.yaml file uses configuration files and the **.env** to set those parameters. +You can find examples of both those configuration files in the archive under the **formula** and **sensor** directories. ## Turn the key -Once all set, you shall be able to initiate the stack with : +Once all set, you shall be able to initiate the stack with: ```sh python3 start.py From c4b7ed869291c057da6caf73682aa5a327a5dcf3 Mon Sep 17 00:00:00 2001 From: Kellian Leveque Date: Wed, 30 Oct 2024 16:17:44 +0100 Subject: [PATCH 053/108] docs(hwpc-sensor): Typo --- docs/reference/sensors/hwpc-sensor.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/sensors/hwpc-sensor.md b/docs/reference/sensors/hwpc-sensor.md index 544b388..2e26d03 100644 --- a/docs/reference/sensors/hwpc-sensor.md +++ b/docs/reference/sensors/hwpc-sensor.md @@ -9,7 +9,7 @@ The figure below depicts how Sensor works in general : ![HWPC Sensor Overview](../../assets/images/reference/sensors/PowerAPI_HWPCSensorOverview.drawio.svg){ width="1000px"} HWPC Sensor uses the RAPL (Running Average Power Limit) technology to monitor CPU -power consumption. Have in mind that this sensor is mainly developped for "server" architectures. +power consumption. Keep in mind that this sensor is mainly developed for "server" architectures. The following table gives a glimpse of RAPL support regarding most common architectures: From a70ce290932efafc46b0394ebae89e799633cf4b Mon Sep 17 00:00:00 2001 From: Kellian Leveque Date: Wed, 30 Oct 2024 16:18:08 +0100 Subject: [PATCH 054/108] docs(smartwatts): Typo --- docs/reference/formulas/smartwatts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/formulas/smartwatts.md b/docs/reference/formulas/smartwatts.md index 3b363b8..24cd0e4 100644 --- a/docs/reference/formulas/smartwatts.md +++ b/docs/reference/formulas/smartwatts.md @@ -5,7 +5,7 @@ SmartWatts needs to receive several metrics available in Sensor's Reports, [HWPC - The Running Average Power Limit (`RAPL`) - `msr` events (`TSC`, `APERF`, `MPERF`) -- `core` events which depend on the Processor Architucture +- `core` events which depend on the Processor Architecture These raw metrics are then used as inputs for a power model that estimates the power consumption of each process. These estimations are recorded in [Power Reports](../reports/reports.md#power-reports) From b91e44d40e4a5f4f7bfdd5eae15e4c75e0d6d398 Mon Sep 17 00:00:00 2001 From: Kellian Leveque Date: Wed, 30 Oct 2024 16:18:36 +0100 Subject: [PATCH 055/108] docs(getting-started): Typo --- docs/getting_started.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/getting_started.md b/docs/getting_started.md index 7b1119f..5eb168f 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -11,7 +11,7 @@ A few things are required before we start: - A python installation ready - Docker & Docker-Compose ready (refer to [this official documentation](https://docs.docker.com/engine/install/) and the [post-install steps](https://docs.docker.com/engine/install/linux-postinstall/) if needed !) - Root access -- Optional : [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) to proceed by clonning the repository +- Optional : [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) to proceed by cloning the repository ## Which components to get a complete stack @@ -52,7 +52,7 @@ At this stage, you will have all the essential files to begin. Let's go through ```sh getting_started/ |--csv/ - |--fomula/ + |--formula/ |----smartwatts-mongodb-csv.json |--sensor/ |----hwpc-mongodb.json From 203af14867763a199fc1ec914cdffd5edd584696 Mon Sep 17 00:00:00 2001 From: chachignot Date: Tue, 5 Nov 2024 11:44:14 +0100 Subject: [PATCH 056/108] docs(getting-started): Fix option for UID/GUID --- docs/script/getting_started/.env | 22 ------------------ .../getting_started/docker-compose.yaml | 2 +- docs/script/getting_started/env_template | 23 +++++++++++++++++++ docs/script/getting_started/start.py | 16 ++++++++++--- 4 files changed, 37 insertions(+), 26 deletions(-) create mode 100644 docs/script/getting_started/env_template diff --git a/docs/script/getting_started/.env b/docs/script/getting_started/.env index 34ede63..e69de29 100644 --- a/docs/script/getting_started/.env +++ b/docs/script/getting_started/.env @@ -1,22 +0,0 @@ -# Sensor image -SENSOR_IMAGE=powerapi/hwpc-sensor:${HWPC_SENSOR_VERSION:-latest} - -# Formula image -FORMULA_IMAGE=powerapi/smartwatts-formula:${SMARTWATTS_VERSION:-latest} - -# Source selection -# Available options: mongodb, socket -POWERAPI_SOURCE=mongodb - -# Destination selection -# Available options: influxdb2, prometheus, mongodb, csv -POWERAPI_DESTINATION=csv - -# Third party images -MONGO_IMAGE=mongo:latest -MONGOEXPRESS_IMAGE=mongo-express:latest -INFLUXDB_IMAGE=influxdb:latest -CSV_IMAGE=busybox:stable-glibc - -# Docker compose profiles -COMPOSE_PROFILES=${POWERAPI_SOURCE},${POWERAPI_DESTINATION} diff --git a/docs/script/getting_started/docker-compose.yaml b/docs/script/getting_started/docker-compose.yaml index 9934d73..68b1b67 100644 --- a/docs/script/getting_started/docker-compose.yaml +++ b/docs/script/getting_started/docker-compose.yaml @@ -42,7 +42,7 @@ services: # PowerAPI Formula formula: - user: $UID:$GUID + user: ${UID}:${GUID} container_name: formula image: ${FORMULA_IMAGE} command: diff --git a/docs/script/getting_started/env_template b/docs/script/getting_started/env_template new file mode 100644 index 0000000..216686f --- /dev/null +++ b/docs/script/getting_started/env_template @@ -0,0 +1,23 @@ +# Sensor image +SENSOR_IMAGE=powerapi/hwpc-sensor:${HWPC_SENSOR_VERSION:-latest} + +# Formula image +FORMULA_IMAGE=powerapi/smartwatts-formula:${SMARTWATTS_VERSION:-latest} + +# Source selection +# Available options: mongodb, socket +POWERAPI_SOURCE=mongodb + +# Destination selection +# Available options: influxdb2, prometheus, mongodb, csv +POWERAPI_DESTINATION=csv + +# Third party images +MONGO_IMAGE=mongo:latest +MONGOEXPRESS_IMAGE=mongo-express:latest +INFLUXDB_IMAGE=influxdb:latest +CSV_IMAGE=busybox:stable-glibc + +# Docker compose profiles +COMPOSE_PROFILES=${POWERAPI_SOURCE},${POWERAPI_DESTINATION} + diff --git a/docs/script/getting_started/start.py b/docs/script/getting_started/start.py index 4d2caa5..a1ce881 100644 --- a/docs/script/getting_started/start.py +++ b/docs/script/getting_started/start.py @@ -36,6 +36,7 @@ import subprocess import json + # List of available processor architectures Template: "n - Arch name" # If an arch is added, the case statement in the # start_demo function should be updated accordingly with the proper core events @@ -54,8 +55,14 @@ def signal_handler(sig, frame): def docker_start(time): id1 = os.popen("id -u").read() id2 = os.popen("id -g").read() - print("UID=" + id1[:-1] + " GUID=" + id2[:-1] + " docker compose up -d") - os.system("UID=" + id1[:-1] + " GUID=" + id2[:-1] + " docker compose up -d") + with open('env_template', 'r') as firstfile, open('.env', 'a') as secondfile: + for line in firstfile: + secondfile.write(line) + secondfile.write("UID=" + id1) + secondfile.write("GUID=" + id2) + + print("docker compose up -d") + os.system("docker compose up -d") os.system("docker compose logs sensor -f &") os.system("docker compose logs formula -f &") os.system("sleep " + str(time)) @@ -66,6 +73,7 @@ def docker_stop(): os.system("set -ueo pipefail") os.system("set +x") os.system("docker compose down") + open('.env', 'w').close() def load_data(): @@ -154,6 +162,7 @@ def start_pretty_print(): print("\nFor more precise evaluation, consult the PowerAPI documentation to adjust configurations.\n") + def find_cpu(data): """ Find the cpu in the list of compatible cpu @@ -312,10 +321,11 @@ def start_demo(): "processor architecture\n") else: print("\nThe demo has ended, " - "you can see the result under the /csv directory" + "you can see the result under the /csv directory, but" " here is a quick summary\n") start_pretty_print() + if __name__ == '__main__': start_demo() From e7dffa074f5a56746c0f2e7f89b918413409ac78 Mon Sep 17 00:00:00 2001 From: chachignot Date: Tue, 5 Nov 2024 14:02:55 +0100 Subject: [PATCH 057/108] docs(getting-started): Clean up + Archive update --- docs/script/getting_started.tar.gz | Bin 4969 -> 18111 bytes docs/script/getting_started/start.py | 29 +++++++++++++++------------ 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/docs/script/getting_started.tar.gz b/docs/script/getting_started.tar.gz index 21705459278eab661c9a1b369bad1350a7460f2b..dbcad45ed39a66fbd8f0f353f5fddab98f408c08 100644 GIT binary patch literal 18111 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 Tf1uL|(E5e-?bfzqJGT7;d>v-i literal 4969 zcmV-v6PD~BiwFP!000001MNI(bK5wQ`K(`orE@EJN0KGkj^}i%t&%0$W{oUqMJkzG zCdHyf$l`<|xqR4hrTpKo8vvh@{K$B`mph_Tu>>?4{X(MwG;~eF&~mQzAT<1tng?(G zgwld?a(w)zb9D6X-C+m5w>#~3ZB{SJoA%L3yLEVSbaK*q(`p|bc8=bV<3EF#Vu_G* zLf$xrO|NX%*##GPmT%a>YmoGs<+`B%MR4=tHW2GS)^*`;R{x#jW3Z3*;qlRH>)&_X z@FBRz+JDee)c?};*O6_!s0}=S{vV&5w9EPb8fBOMgEf4=HNr4xtX=2YHLn`r8k)Zc zyCt-Oa6V_T0*Ff;Y>8q>;(qlaC|J%j&zuj(?{{J<~9#PqhCf3@xrWI8l ztCN|oT=vi9o`(+1@T`N?t+#0Kncsc;4Sl&T0N=|qRhf*t4ciE55Z+fRd*qD!4T14& zGP|6Q`bCtP_(J#@z^q>OXWECprm0&It8U~kC<&SR6}$UV`H2#0{#at!SF7*@+!+R<0@s#7PP@2+XMqES#!2Lo@M zjx>~2O&Rw!V4#N$fKppozDYb_`i=`Gs9a9Q=My0phNsJU^Y&8FMcq(9G665B9`d+kLMZw*b`?U+Ej^6#+wyqY8f0U3<7Vg@E#UB}4S~gHN zm|RXLs;p0ElQU%`tG&113R1kUXR0#nmd^hND@Fg${x*ZvOOEl_{tv?ag8zR9|4RS= z8fEV<2UnJJ0P*6gQkg5mp7ge6nWPczOXNKB{d%8B#LWAP$RiE2QYqBh?sY);VnG^9 z^4IME!s!c$z^2qA?M|ywX@Uv59#~-MNnWEA=RYhkVR)x|zU72X@9ss$csTxR!?)u3 z&&d(EgmV7BLfIn&*Sqtr>s3fV$@ffeH3H04=!C z>XS9Zl)eQMAeA>$6YH8`A!ck$E!Gd)@ni!j!p|w;c=!MSj++88HCQ2+DA3Qbx_$t| z-;pbdmIIu)ggPeFQZy2b#@&zwuOK5ZKR^lwYLX>1q*4joC2Tjq282{>{F zJcQb@Hd9XL+5{@3z6w1hoF&lIAAckwxF?etQ7)$=1<(QhS%0i4vWnAhFq#jQ@p+v9 z3NoH(WTadw8h~k&It~lCHan8Z8M%~agA4fDKUGGG_K}Tvrf6dv4Yp#WPo}WPQUCbU`Dg&YY(-ER$qXDl& zWhM_aWGekM00{z(qdHNi@<724@~<*5)SrE<3#_XA&p7}=BhuE#jf_fakBA1eGnmih zOQaj5q0UcXYog6HnVe51LlijfBjwq9Wgx5nMMe{qNo}smbr?hIv%%T6M}ubgd^%SZ zCOl=V$+Ovfio2YB;Qs>%2ayf>(0Rz@JQ*{FK~j_1M?{OVU}~t74;M1j&rq;TVto{Z z3X&UW88E;HV}dBP45wr)pO2Jtc|4G@b%J<4D5|^VB6W8cO`2as-eBcBO>vGT8)WQ(sI)n6n86=CZ*5M!xV)fS(xP$rATw zT5Pv&_!bSCxnUS=k9_*fzJVVlcz0JPdR+_*9?uP$w}$;CC0URf_dr1Awg8rON}I7* z%LJkcz9>MaQmZyIJZB4n>Yf4KvWElXr{?dj<q>yv4EQNE4VwehL<`+5ao;-;5ci`eNKfC zJwggKFARGT;mHDqiXkY$S_Cz0uaf|rIxXmW5Q7r~q6Om`*2=Z<)mlgEI@9gia_|d! z`5;=eQx+z8RQ5IaZ---rxWkLk&EtvmeqPT=M*t%IcAru7EouzON?8YGLH(yHLspsyO$wH>Ff8n2yjvk%2Q}X3UYHo$bY#tofkKdFg?7=trU*+? zSW?T{Sskg{FBlZ_3qXLuVk?6MkL*A!Z&qL~5L0n7Q#0VQ=Xzjt)|S$5&NoKd{cn!6 zpKJM6)wq##|K4fclW)~NCoU_Dr0tT=i|uWj(}cI>29$adgj_$jgq?(LXxKP0geA1< zv1ZvProC1=X<|j)70|vj$P={%>f+i0aomF0E?Up-p=%p< zi*1||W}AcQoH?T5tba+Ev&i|9jD@rHGFd+JMk~10Ry04~FyiIb4{H4YHeW~Mrs-Y@ zXkAzs?n2y*ZU;heM+EB5(z=d(emagrGAqxnu<6P#>i4p4-!TQ3$YII`0|ck8$L{-q zg{2B`)r{)05DAJpk#K}TMrdj(w663ZZA|I~;&7>X5_Jf;i@X#G(8or30)cL1o5V2r zX6-yDHUgU|nzQWRoHLlbYuAsJND0LWAU4U&3~a7sb`#y7D)~(wZlnzJcidv;lpQp= zrYkVtbsZxUUB^YUs_Sdm59_*^^V^KQJhc14O7Z-UpVc?$KeU*Yob=HFKZ}y&15IkLg`K6#r{z@`;foj^m_dVDz@y>I23ZLV|9=>+C z4j#)tc(d9iZ@*>kR(YV~(D2svza$Y|gTzIH4^lUBwT%K zLmsme3%8?Yv|fuFUGTYvlRc$kp4_KKh%T2ctjX$)s|IYnwsVBL>>M}AO)lr<#7H9FQkUY+jZ#1l=OW z1P3OBb@ANSOrlzT7TqNWpu)w*elJ`5aQEOY*zBEFKHT}n@DFV3N~{S7i5j+`d>B-O zmO2O_E4YG`o-hJL=>H0xro--4# zB1sO-?zJQa*|MVKl5)5~Q0K<-T?bu!EX7M%A56w)%DK)u4NrAEah7NKwQ3vOd^Spe ziV3YdXHpg^%gXGM)CzxY{oL;2f6@yrgU`~$7A+4agJ0y?HZ4C3b0y13k%w-%4S8zW z$p7IB*^7?x9q&Kh9UhnOKVGBk&_4@g#GL=WM<1E;SpM&EG5@2}Znrz-{_i!4c>fVU zKFB?g$h->4yZ|Vs{KroVu6;N1bbNS#_f+he0tA`|%iw@F9Z0*LQat<+ORBOI`E%Ipr#I_`et<>20Gi;aLF3- zY^0RrB$xzLc_A)bpFWR9WPC9CMb|E7a(}4L#~1yPCJ(za`AnZ~BXU;<@qKHb(P*Gw zDyk~0+gg>es?FvDc9fydWKEgL!$+i0x&L?1=jWE~`M)oe_$SIE`G1GU?c)0Xu2ts$ zy-wLH^Z&~HzcT-?%>OI%|H}Noo%w%h&Y$>%N67I<>l)V13d>FiqANU_rU7^|kuhl4 zb^@8S#x?Aa5{MiLpJgNu?*t&sOv7KStPsNX2o@Hi(Y1%y@en6Fq(S=7$Xo}`E(H)c z44&Q1nBb)9-08rkfZe52^l-xCK?i<=ZPK+%mXWjI2^TAN3y;pmgAD~Z5@P}3E9TbO zcd5?<`aG7Z;p4SC#<75J6^32_JCyi5s_FXI2R>a=ANEUB?5%_BbIOAXx@(=$+#{c) z7VxAvA+?DL?)Q$I`j&||O9OmzNJhpNT8vbscG~63oedCRY~b;}&^rc?eV0p0w|DNK z3@?Y|zrpo&Hd+ret%uTQk>kSjt*?MSoBB2QS_fZ?`HS65dP4Q18{tJwNL1w<=8ouA zCwOekR-pW&AW+5Pi%6zGo2g03L!d%-4XUe{Rik1HUyD$gS9kGqP8djNYl}6pu55L< zg(ybon`fl*=S!P8Fu74hB8CjY7i(CwzJiYijI^+r?J#?}gTP`333Za)QAnOm(Nyy( zI)WH0aH?TuhU7Y89v3$P&AJ8>P}ywRcPN-*m;B~P8~dvAz9Bd*_k% z!_D^n_;Q8_TWIn>HSvwxNxfiSyw?p&?-F8PtXznA;JX&z>fNqDHKIPv3=93ce3}@? z!hXP_w!gI{tih*zleJ{6eY&NCM zlw@%HP3&HK3ZZ{Q4A~imzGP+G#81ERMmKM6fCCIpm>KK2N9KGwXFPNq>Ta9eq7%+> zf@er(oCg;V`H&Au^R%nG&QG6^qAjxO5W0z}b+x77CDmLj5B;f{oVQdz;}@UeU(PLN zIu}k>l0%XQk5e|GcATN?K0kTn?1Z12P&;L;{X&dA<+$j%M@28lX6KKx`Ef9kRUM*U zT^Z|0c99}Mgr22r7tDMzRUe}E@P8Gx1!A-QVUU^QB5KQIRj%mbOOguC&!P!C!HAC| zSPudH;Od_Z1^H6MvGp>cZPSmO+9wI4O$k=_#&Usw{zJq6iwEMLH%}e2C8S@&9xig2 zS>&O&(fQeVT#1~q&)M{5j=UL!CIIip4kTC076D$-eAE z8-nMh&QvDlRhidML7Pu=wAI@ucw6RX&1mh3Y~E!ZmIo8)=^f=49~N82fh2~P>B5-F zx@K`F3nM}Q%-ReOEaIz$2Pd#`=-`WlD5OE*t$+lZ7Kjg_Yp}wT4UBn-1*_yB^zFug zqcox3Wwy$i#c|C>_?)hOX#~xG%^|J_u_otepp;fTN6uk$0bTeE32sClI{%TUc-6%E zfl{LN1Bxu0-o0>5SB*)1xAZA>kC~K_V~QQrLC(rH`eS^Er^i+AE?l|JAu0S^L3MLTm{a&- z#UTHRED&7~t=D+GDvl!s0m8&XJp6;r$8+-E`F8Y&O*q-S|6zOX7ail#{Ew63@4p|t=CtOVT*7;#gzAR-aOIgZNma>$kEM+N6 nS;|tDvXrGPWhqNp%2JlHl%*_XDN9-YPnQ1yR@rYR0C)fZiz>c$ diff --git a/docs/script/getting_started/start.py b/docs/script/getting_started/start.py index a1ce881..e6e54ae 100644 --- a/docs/script/getting_started/start.py +++ b/docs/script/getting_started/start.py @@ -31,8 +31,6 @@ import csv import os import sys -import signal -from subprocess import call import subprocess import json @@ -47,21 +45,18 @@ ["Zen 3", "Zen 4"]] -def signal_handler(sig, frame): - print('You sent SIGINT signal, stoping docker compose stack') - call("./stop.sh") - - def docker_start(time): + """ + Start the docker compose stack and the logs + :param time: The duration of the demo + """ id1 = os.popen("id -u").read() id2 = os.popen("id -g").read() - with open('env_template', 'r') as firstfile, open('.env', 'a') as secondfile: + with open('env_template', 'r', encoding='UTF-8') as firstfile, open('.env', 'a', encoding='UTF-8') as secondfile: for line in firstfile: secondfile.write(line) secondfile.write("UID=" + id1) secondfile.write("GUID=" + id2) - - print("docker compose up -d") os.system("docker compose up -d") os.system("docker compose logs sensor -f &") os.system("docker compose logs formula -f &") @@ -70,10 +65,13 @@ def docker_start(time): def docker_stop(): + """ + Stop the docker compose stack and clean the environment + """ os.system("set -ueo pipefail") os.system("set +x") os.system("docker compose down") - open('.env', 'w').close() + open('.env', 'w', encoding='UTF-8').close() def load_data(): @@ -170,9 +168,9 @@ def find_cpu(data): option = [] line = "cat /proc/cpuinfo | grep 'model name'" result = subprocess.check_output(line, shell=True, text=True).split("\n") - print("The CPU found is" + result[0].split(":")[1] ) + print("The CPU found is" + result[0].split(":")[1]) parse = parse_processor_name(result[0]) - for row in data : + for row in data: if parse[0] in row["Name"] and row["Manufacturer"] == parse[1]: option.append(row) @@ -193,6 +191,11 @@ def find_cpu(data): def parse_processor_name(name): + """ + Parse the processor name to extract the id and the brand + :param name: Name of the processor, extracted from /proc/cpuinfo + :return: id adn brand of the processor + """ if "Intel" in name: brand = "Intel" elif "AMD" in name: From 8b82ae3e92a382c0da9baf0da459b77eb89b4e02 Mon Sep 17 00:00:00 2001 From: daniel Date: Wed, 8 Jan 2025 15:29:21 +0100 Subject: [PATCH 058/108] fix: Correct issue related to csv directory permission by deleting/creating it --- docs/script/getting_started/start.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/script/getting_started/start.py b/docs/script/getting_started/start.py index e6e54ae..531d91e 100644 --- a/docs/script/getting_started/start.py +++ b/docs/script/getting_started/start.py @@ -33,6 +33,7 @@ import sys import subprocess import json +import shutil # List of available processor architectures Template: "n - Arch name" @@ -50,13 +51,16 @@ def docker_start(time): Start the docker compose stack and the logs :param time: The duration of the demo """ - id1 = os.popen("id -u").read() - id2 = os.popen("id -g").read() + csv_directory_path = "csv" with open('env_template', 'r', encoding='UTF-8') as firstfile, open('.env', 'a', encoding='UTF-8') as secondfile: for line in firstfile: secondfile.write(line) - secondfile.write("UID=" + id1) - secondfile.write("GUID=" + id2) + + if os.path.exists(csv_directory_path): + shutil.rmtree(csv_directory_path) + + os.makedirs(csv_directory_path, exist_ok=True) + os.system("docker compose up -d") os.system("docker compose logs sensor -f &") os.system("docker compose logs formula -f &") From 061b87b2ef9362c17fae69ffe2a4d65677050fbb Mon Sep 17 00:00:00 2001 From: daniel Date: Wed, 8 Jan 2025 15:41:04 +0100 Subject: [PATCH 059/108] fix: Remove user from formula --- docs/script/getting_started/docker-compose.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/script/getting_started/docker-compose.yaml b/docs/script/getting_started/docker-compose.yaml index 68b1b67..fcb7e60 100644 --- a/docs/script/getting_started/docker-compose.yaml +++ b/docs/script/getting_started/docker-compose.yaml @@ -42,7 +42,6 @@ services: # PowerAPI Formula formula: - user: ${UID}:${GUID} container_name: formula image: ${FORMULA_IMAGE} command: From 2133fef6d16becf0b89f7ae610825911f500a09e Mon Sep 17 00:00:00 2001 From: daniel Date: Thu, 9 Jan 2025 09:23:33 +0100 Subject: [PATCH 060/108] refactor: Update some variables and functions names --- docs/script/getting_started/start.py | 134 ++++++++++++++------------- 1 file changed, 71 insertions(+), 63 deletions(-) diff --git a/docs/script/getting_started/start.py b/docs/script/getting_started/start.py index 531d91e..0bb7c3a 100644 --- a/docs/script/getting_started/start.py +++ b/docs/script/getting_started/start.py @@ -40,21 +40,24 @@ # If an arch is added, the case statement in the # start_demo function should be updated accordingly with the proper core events # https://powerapi.org/reference/sensors/hwpc-sensor/ -arch_tab = [["Sandy bridge", "Ivy bridge", "Haswell", "Broadwell", "Comet lake"], - ["Skylake", "Cascade lake", "Kaby Lake R", "Kaby Lake", "Coffee Lake", "Amber Lake", "Rocket lake", "Whiskey lake"], - ["Zen", "Zen+", "Zen 2"], - ["Zen 3", "Zen 4"]] +architectures_table = [["Sandy bridge", "Ivy bridge", "Haswell", "Broadwell", "Comet lake"], + ["Skylake", "Cascade lake", "Kaby Lake R", "Kaby Lake", "Coffee Lake", "Amber Lake", "Rocket lake", "Whiskey lake"], + ["Zen", "Zen+", "Zen 2"], + ["Zen 3", "Zen 4"]] +csv_directory_path = "csv" -def docker_start(time): + +def start_docker_compose(time): """ Start the docker compose stack and the logs :param time: The duration of the demo """ - csv_directory_path = "csv" - with open('env_template', 'r', encoding='UTF-8') as firstfile, open('.env', 'a', encoding='UTF-8') as secondfile: - for line in firstfile: - secondfile.write(line) + + with open('env_template', 'r', encoding='UTF-8') as env_template_file, open('.env', 'a', encoding='UTF-8') \ + as env_file: + for current_file_line in env_template_file: + env_file.write(current_file_line) if os.path.exists(csv_directory_path): shutil.rmtree(csv_directory_path) @@ -65,10 +68,10 @@ def docker_start(time): os.system("docker compose logs sensor -f &") os.system("docker compose logs formula -f &") os.system("sleep " + str(time)) - docker_stop() + stop_docker_compose() -def docker_stop(): +def stop_docker_compose(): """ Stop the docker compose stack and clean the environment """ @@ -78,39 +81,42 @@ def docker_stop(): open('.env', 'w', encoding='UTF-8').close() -def load_data(): +def load_cpus_information(): """ - Load CSV files from the specified directory and return the data as a list of dictionaries. + Load CPUs information from a CSV file and return the data as a list of dictionaries. """ - data = [] - with open("./cpu.csv", mode='r', newline='', encoding='UTF-8') as f: - data.extend(csv.DictReader(f)) - return data + cpus_information = [] + with open("./cpu.csv", mode='r', newline='', encoding='UTF-8') as cpu_csv_file: + cpus_information.extend(csv.DictReader(cpu_csv_file)) + return cpus_information -def load_result(directory='./csv'): +def load_csv_files_from_directory(directory=csv_directory_path): """ Load CSV files from the specified directory and return the data as a list of dictionaries. + :param directory: The directory that contains the csv files """ data = [] - for root, _, files in os.walk(directory): - for filename in files: - if filename.endswith('.csv'): - file_path = os.path.join(root, filename) - with open(file_path, mode='r', newline='', encoding='UTF-8') as f: - data.extend(csv.DictReader(f)) + for root_directory, _, files in os.walk(directory): + for current_file_name in files: + if current_file_name.endswith('.csv'): + current_file_path = os.path.join(root_directory, current_file_name) + with open(current_file_path, mode='r', newline='', encoding='UTF-8') as current_file: + data.extend(csv.DictReader(current_file)) return data -def calculate_statistics(data, scope): +def compute_statistics(data, scope): """ - Calculate average, maximum, and minimum consumption for the given scope (cpu or dram). + Compute average, maximum, and minimum consumption for the given scope (CPU or DRAM) by using a given data. + :param data: Data to compute statistics + :param scope: CPU or DRAM """ stats = {} - for row in data: - if row['scope'] == scope and row['target'] != 'rapl': - target = row['target'] - consumption = float(row['power']) + for current_row in data: + if current_row['scope'] == scope and current_row['target'] != 'rapl': + target = current_row['target'] + consumption = float(current_row['power']) stats.setdefault(target, []).append(consumption) return { @@ -125,6 +131,8 @@ def calculate_statistics(data, scope): def print_statistics(stats, title): """ Print statistics (average, max, min) for the given data in a formatted table. + :param stats: Statistics to be printed + :param title: Title used for the provided statistics """ if not stats: return @@ -150,14 +158,14 @@ def start_pretty_print(): """ print("The consumptions are given in Watt, note that the precision depends on the configuration file\n") - data = load_result() + data = load_csv_files_from_directory() # Calculate and print CPU statistics - cpu_stats = calculate_statistics(data, 'cpu') + cpu_stats = compute_statistics(data, 'cpu') print_statistics(cpu_stats, "CPU Consumption Statistics :") # Calculate and print DRAM statistics - dram_stats = calculate_statistics(data, 'dram') + dram_stats = compute_statistics(data, 'dram') print_statistics(dram_stats, "DRAM Consumption Statistics :") # Could add the GPU statistics here @@ -220,37 +228,37 @@ def start_demo(): """ print("PowerAPI demo") print("=" * 80) - cpu = find_cpu(load_data()) + cpu = find_cpu(load_cpus_information()) print("\nSetting up configuration files...") print("-" * 80) # Update core events in the sensor configuration # file based on the selected processor architecture - with open('sensor/hwpc-mongodb.json', encoding='UTF-8') as f: - data = json.load(f) + with open('sensor/hwpc-mongodb.json', encoding='UTF-8') as hwpc_sensor_configuration_file: + sensor_configuration = json.load(hwpc_sensor_configuration_file) - if cpu["Family"] in arch_tab[0]: - data['container']['core']['events'] = [ + if cpu["Family"] in architectures_table[0]: + sensor_configuration['container']['core']['events'] = [ "CPU_CLK_UNHALTED:REF_P", "CPU_CLK_UNHALTED:THREAD_P", "LLC_MISSES", "INSTRUCTIONS_RETIRED" ] - elif cpu["Family"] in arch_tab[1]: - data['container']['core']['events'] = [ + elif cpu["Family"] in architectures_table[1]: + sensor_configuration['container']['core']['events'] = [ "CPU_CLK_THREAD_UNHALTED:REF_P", "CPU_CLK_THREAD_UNHALTED:THREAD_P", "LLC_MISSES", "INSTRUCTIONS_RETIRED" ] - elif cpu["Family"] in arch_tab[2]: - data['container']['core']['events'] = [ + elif cpu["Family"] in architectures_table[2]: + sensor_configuration['container']['core']['events'] = [ "CYCLES_NOT_IN_HALT", "RETIRED_INSTRUCTIONS", "RETIRED_UOPS" ] - elif cpu["Family"] in arch_tab[3]: - data['container']['core']['events'] = [ + elif cpu["Family"] in architectures_table[3]: + sensor_configuration['container']['core']['events'] = [ "CYCLES_NOT_IN_HALT", "RETIRED_INSTRUCTIONS", "RETIRED_OPS" @@ -263,18 +271,18 @@ def start_demo(): cgroup = subprocess.run(["stat", "-fc", "%T", "/sys/fs/cgroup/"], text=True, capture_output=True, check=True) if cgroup.stdout == "cgroup2fs\n": - data["cgroup_basepath"] = "/sys/fs/cgroup/" + sensor_configuration["cgroup_basepath"] = "/sys/fs/cgroup/" else: - data["cgroup_basepath"] = "/sys/fs/cgroup/perf_event" + sensor_configuration["cgroup_basepath"] = "/sys/fs/cgroup/perf_event" - print("Cgroup version updated") + print("cgroup version updated") - with open('sensor/hwpc-mongodb.json', 'w', encoding='UTF-8') as f: - json.dump(data, f, indent=4) + with open('sensor/hwpc-mongodb.json', 'w', encoding='UTF-8') as hwpc_sensor_configuration_file: + json.dump(sensor_configuration, hwpc_sensor_configuration_file, indent=4) # Update parameters in the formula configuration - with open('formula/smartwatts-mongodb-csv.json', encoding='UTF-8') as f: - formula_config = json.load(f) + with open('formula/smartwatts-mongodb-csv.json', encoding='UTF-8') as smartwatts_configuration_file: + formula_config = json.load(smartwatts_configuration_file) if cpu["Base frequency"] != '': formula_config["cpu-base-freq"] = int(float(cpu["Base frequency"])*1000) @@ -284,21 +292,21 @@ def start_demo(): formula_config["cpu-tdp"] = int(cpu["TDP"][:-1]) print("TDP updated\n") - with open('formula/smartwatts-mongodb-csv.json', 'w', encoding='UTF-8') as f: - json.dump(formula_config, f, indent=4) + with open('formula/smartwatts-mongodb-csv.json', 'w', encoding='UTF-8') as smartwatts_configuration_file: + json.dump(formula_config, smartwatts_configuration_file, indent=4) print("Please enter the number of second you want the demo to run for (minimum 30) or exit to quit:") - waiting = True - while waiting: + waiting_for_execution_time = True + while waiting_for_execution_time: try: - val = input() - val = int(val) - if val < 30: + execution_time = input() + execution_time = int(execution_time) + if execution_time < 30: print("Invalid input, please enter a valid number or exit to quit") else: - waiting = False + waiting_for_execution_time = False except ValueError: - if val == "exit": + if execution_time == "exit": print("Exiting...") sys.exit() else: @@ -306,15 +314,15 @@ def start_demo(): print("\nStarting the demo...") print("-" * 80) - print("The demo will run for " + str(val) + " seconds\n") + print("The demo will run for " + str(execution_time) + " seconds\n") print("If you wish to stop it, Ctrl-C will do so and stop the docker compose stack\n") - docker_start(val) + start_docker_compose(execution_time) verification = 0 # Get all the csv power report in the csv directory - for root, _, files in os.walk('./csv'): + for root, _, files in os.walk(csv_directory_path): for filename in files: if filename.endswith('.csv'): verification += 1 From 6863ff8254a40a5f8a5195ed98f034d9fa8abeff Mon Sep 17 00:00:00 2001 From: daniel Date: Thu, 9 Jan 2025 09:36:24 +0100 Subject: [PATCH 061/108] style: Add white line to pretty print --- docs/script/getting_started/start.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/script/getting_started/start.py b/docs/script/getting_started/start.py index 0bb7c3a..0949c33 100644 --- a/docs/script/getting_started/start.py +++ b/docs/script/getting_started/start.py @@ -327,7 +327,7 @@ def start_demo(): if filename.endswith('.csv'): verification += 1 file_path = os.path.join(root, filename) - print("The power report is available at: " + file_path) + print("\nThe power report is available at: " + file_path) if verification == 0: print("\nNo power report available, " From ca3f3b5381555fe3755c8c794db015b891769925 Mon Sep 17 00:00:00 2001 From: daniel Date: Thu, 9 Jan 2025 09:56:28 +0100 Subject: [PATCH 062/108] refactor: Force usage of bash shell for system commands --- docs/script/getting_started/start.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/script/getting_started/start.py b/docs/script/getting_started/start.py index 0949c33..c99fae8 100644 --- a/docs/script/getting_started/start.py +++ b/docs/script/getting_started/start.py @@ -64,10 +64,10 @@ def start_docker_compose(time): os.makedirs(csv_directory_path, exist_ok=True) - os.system("docker compose up -d") - os.system("docker compose logs sensor -f &") - os.system("docker compose logs formula -f &") - os.system("sleep " + str(time)) + os.system("bash -c docker compose up -d") + os.system("bash -c docker compose logs sensor -f &") + os.system("bash -c docker compose logs formula -f &") + os.system("bash -c sleep " + str(time)) stop_docker_compose() @@ -75,9 +75,9 @@ def stop_docker_compose(): """ Stop the docker compose stack and clean the environment """ - os.system("set -ueo pipefail") - os.system("set +x") - os.system("docker compose down") + os.system("bash -c set -ueo pipefail") + os.system("bash -c set +x") + os.system("bash -c docker compose down") open('.env', 'w', encoding='UTF-8').close() From a68b80802a4f1839060a62f5178ea3865ee67c7d Mon Sep 17 00:00:00 2001 From: daniel Date: Thu, 9 Jan 2025 10:00:41 +0100 Subject: [PATCH 063/108] refactor: Force usage of bash shell for system commands --- docs/script/getting_started/start.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/script/getting_started/start.py b/docs/script/getting_started/start.py index c99fae8..f40a9e6 100644 --- a/docs/script/getting_started/start.py +++ b/docs/script/getting_started/start.py @@ -64,10 +64,10 @@ def start_docker_compose(time): os.makedirs(csv_directory_path, exist_ok=True) - os.system("bash -c docker compose up -d") - os.system("bash -c docker compose logs sensor -f &") - os.system("bash -c docker compose logs formula -f &") - os.system("bash -c sleep " + str(time)) + os.system("bash -c 'docker compose up -d'") + os.system("bash -c 'docker compose logs sensor -f &'") + os.system("bash -c 'docker compose logs formula -f &'") + os.system("bash -c 'sleep " + str(time)+"'") stop_docker_compose() @@ -75,9 +75,9 @@ def stop_docker_compose(): """ Stop the docker compose stack and clean the environment """ - os.system("bash -c set -ueo pipefail") - os.system("bash -c set +x") - os.system("bash -c docker compose down") + os.system("bash -c 'set -ueo pipefail'") + os.system("bash -c 'set +x'") + os.system("bash -c 'docker compose down'") open('.env', 'w', encoding='UTF-8').close() From d1cf296382dbbc0f289b8de98a3d5deea53e1a22 Mon Sep 17 00:00:00 2001 From: daniel Date: Thu, 9 Jan 2025 10:26:11 +0100 Subject: [PATCH 064/108] refactor: Define global variable for minimum execution time --- docs/script/getting_started/start.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/docs/script/getting_started/start.py b/docs/script/getting_started/start.py index f40a9e6..3e3f438 100644 --- a/docs/script/getting_started/start.py +++ b/docs/script/getting_started/start.py @@ -35,18 +35,22 @@ import json import shutil - # List of available processor architectures Template: "n - Arch name" # If an arch is added, the case statement in the # start_demo function should be updated accordingly with the proper core events # https://powerapi.org/reference/sensors/hwpc-sensor/ architectures_table = [["Sandy bridge", "Ivy bridge", "Haswell", "Broadwell", "Comet lake"], - ["Skylake", "Cascade lake", "Kaby Lake R", "Kaby Lake", "Coffee Lake", "Amber Lake", "Rocket lake", "Whiskey lake"], + ["Skylake", "Cascade lake", "Kaby Lake R", "Kaby Lake", "Coffee Lake", "Amber Lake", + "Rocket lake", "Whiskey lake"], ["Zen", "Zen+", "Zen 2"], ["Zen 3", "Zen 4"]] +# The csv directory path csv_directory_path = "csv" +# The default execution time for the demo +minimum_execution_time = 30 + def start_docker_compose(time): """ @@ -67,7 +71,7 @@ def start_docker_compose(time): os.system("bash -c 'docker compose up -d'") os.system("bash -c 'docker compose logs sensor -f &'") os.system("bash -c 'docker compose logs formula -f &'") - os.system("bash -c 'sleep " + str(time)+"'") + os.system("bash -c 'sleep " + str(time) + "'") stop_docker_compose() @@ -285,7 +289,7 @@ def start_demo(): formula_config = json.load(smartwatts_configuration_file) if cpu["Base frequency"] != '': - formula_config["cpu-base-freq"] = int(float(cpu["Base frequency"])*1000) + formula_config["cpu-base-freq"] = int(float(cpu["Base frequency"]) * 1000) print("Base frequency updated") if cpu["TDP"] != '': @@ -295,13 +299,14 @@ def start_demo(): with open('formula/smartwatts-mongodb-csv.json', 'w', encoding='UTF-8') as smartwatts_configuration_file: json.dump(formula_config, smartwatts_configuration_file, indent=4) - print("Please enter the number of second you want the demo to run for (minimum 30) or exit to quit:") + print(f'Please enter the number of second you want the demo to run for (minimum {minimum_execution_time}) or exit ' + f'to quit:') waiting_for_execution_time = True while waiting_for_execution_time: try: execution_time = input() execution_time = int(execution_time) - if execution_time < 30: + if execution_time < minimum_execution_time: print("Invalid input, please enter a valid number or exit to quit") else: waiting_for_execution_time = False @@ -314,7 +319,7 @@ def start_demo(): print("\nStarting the demo...") print("-" * 80) - print("The demo will run for " + str(execution_time) + " seconds\n") + print(f'The demo will run for {execution_time} seconds\n') print("If you wish to stop it, Ctrl-C will do so and stop the docker compose stack\n") start_docker_compose(execution_time) @@ -327,7 +332,7 @@ def start_demo(): if filename.endswith('.csv'): verification += 1 file_path = os.path.join(root, filename) - print("\nThe power report is available at: " + file_path) + print(f'\nThe power report is available at: {file_path}') if verification == 0: print("\nNo power report available, " From dde7768c62bc16cab34b55cc054d5fbd2be36762 Mon Sep 17 00:00:00 2001 From: daniel Date: Thu, 9 Jan 2025 11:05:25 +0100 Subject: [PATCH 065/108] fix: Change umask value for current user --- docs/script/getting_started/start.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/script/getting_started/start.py b/docs/script/getting_started/start.py index 3e3f438..99673a3 100644 --- a/docs/script/getting_started/start.py +++ b/docs/script/getting_started/start.py @@ -66,7 +66,9 @@ def start_docker_compose(time): if os.path.exists(csv_directory_path): shutil.rmtree(csv_directory_path) + previous_umask = os.umask(0) os.makedirs(csv_directory_path, exist_ok=True) + os.umask(previous_umask) os.system("bash -c 'docker compose up -d'") os.system("bash -c 'docker compose logs sensor -f &'") From 9075a30ef19a449047ca34d0b72bdcdfc3520300 Mon Sep 17 00:00:00 2001 From: daniel Date: Thu, 9 Jan 2025 11:43:10 +0100 Subject: [PATCH 066/108] fix: Change umask value for current user --- docs/script/getting_started/start.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/script/getting_started/start.py b/docs/script/getting_started/start.py index 99673a3..f591024 100644 --- a/docs/script/getting_started/start.py +++ b/docs/script/getting_started/start.py @@ -66,9 +66,12 @@ def start_docker_compose(time): if os.path.exists(csv_directory_path): shutil.rmtree(csv_directory_path) + previous_umask = os.umask(0) - os.makedirs(csv_directory_path, exist_ok=True) - os.umask(previous_umask) + try: + os.makedirs(csv_directory_path, exist_ok=True) + finally: + os.umask(previous_umask) os.system("bash -c 'docker compose up -d'") os.system("bash -c 'docker compose logs sensor -f &'") From 1f2de031c73273370497be4874dc3896724c39d0 Mon Sep 17 00:00:00 2001 From: chachignot Date: Thu, 9 Jan 2025 15:25:14 +0100 Subject: [PATCH 067/108] docs(getting-started): Add exception gestion for CPU choice --- docs/script/getting_started/start.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/script/getting_started/start.py b/docs/script/getting_started/start.py index f591024..10c2d2d 100644 --- a/docs/script/getting_started/start.py +++ b/docs/script/getting_started/start.py @@ -207,6 +207,9 @@ def find_cpu(data): print(str(i) + " - " + option[i]["Name"]) choice = int(input()) print("You have selected : " + option[choice]["Name"]) + if choice < 0 or choice >= len(option): + print("Invalid choice, exiting...") + sys.exit() cpu = option[choice] return cpu From eba10eddee924db495785db106b8b80792e30514 Mon Sep 17 00:00:00 2001 From: KellianL <125127820+KellianL@users.noreply.github.com> Date: Thu, 30 Jan 2025 16:58:47 +0100 Subject: [PATCH 068/108] docs(smartwatts): Fix HWPC Report -> HWPCReport, same for Power report --- docs/reference/formulas/smartwatts.md | 52 +++++++++++++-------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/docs/reference/formulas/smartwatts.md b/docs/reference/formulas/smartwatts.md index 24cd0e4..cb68494 100644 --- a/docs/reference/formulas/smartwatts.md +++ b/docs/reference/formulas/smartwatts.md @@ -249,8 +249,8 @@ In order to run the Formula, you can execute one of the following command lines, docker run -t \ --net=host \ powerapi/smartwatts-formula --verbose \ - --input mongodb --model HWPC Report --uri mongodb://127.0.0.1 --db test --collection prep \ - --output influxdb2 --model Power Report --uri 127.0.0.1 --port 8086 --db power_consumption --org org_test --token mytoken \ + --input mongodb --model HWPCReport --uri mongodb://127.0.0.1 --db test --collection prep \ + --output influxdb2 --model PowerReport --uri 127.0.0.1 --port 8086 --db power_consumption --org org_test --token mytoken \ --cpu-base-freq 1900 \ --cpu-error-threshold 2.0 \ --disable-dram-formula \ @@ -263,8 +263,8 @@ In order to run the Formula, you can execute one of the following command lines, docker run -t \ --net=host \ powerapi/smartwatts-formula --verbose \ - --input csv --model HWPC Report --directory hwcp\_reports.d --files "hwpc\_report\_1, hwpc\_report\_2" \ - --output influxdb2 --model Power Report --uri 127.0.0.1 --port 8086 --db power_consumption --org org_test --token mytoken \ + --input csv --model HWPCReport --directory hwcp\_reports.d --files "hwpc\_report\_1, hwpc\_report\_2" \ + --output influxdb2 --model PowerReport --uri 127.0.0.1 --port 8086 --db power_consumption --org org_test --token mytoken \ --cpu-base-freq 1900 \ --cpu-error-threshold 2.0 \ --disable-dram-formula \ @@ -277,7 +277,7 @@ In order to run the Formula, you can execute one of the following command lines, docker run -t \ --net=host \ powerapi/smartwatts-formula --verbose \ - --input csv --model HWPC Report --directory hwcp\_reports.d --files "hwpc\_report\_1, hwpc\_report\_2" \ + --input csv --model HWPCReport --directory hwcp\_reports.d --files "hwpc\_report\_1, hwpc\_report\_2" \ --output csv --directory power_reports.d \ --cpu-base-freq 1900 \ --cpu-error-threshold 2.0 \ @@ -292,8 +292,8 @@ In order to run the Formula, you can execute one of the following command lines, ```sh hl_lines="3 4" python -m smartwatts \ --verbose \ - --input mongodb --model HWPC Report --uri mongodb://127.0.0.1 --db test --collection prep \ - --output influxdb2 --model Power Report --uri 127.0.0.1 --port 8086 --db power_consumption --org org_test --token mytoken\ + --input mongodb --model HWPCReport --uri mongodb://127.0.0.1 --db test --collection prep \ + --output influxdb2 --model PowerReport --uri 127.0.0.1 --port 8086 --db power_consumption --org org_test --token mytoken\ --cpu-base-freq 1900 \ --cpu-error-threshold 2.0 \ --disable-dram-formula \ @@ -305,8 +305,8 @@ In order to run the Formula, you can execute one of the following command lines, ```sh hl_lines="3 4" python -m smartwatts \ --verbose \ - --input csv --model HWPC Report --directory hwcp\_reports.d --name puller\_csv --files "hwpc\_report\_1.json, hwpc\_report\_2.json" \ - --output influxdb2 --model Power Report --uri 127.0.0.1 --port 8086 --db power_consumption --org org_test --token mytoken\ + --input csv --model HWPCReport --directory hwcp\_reports.d --name puller\_csv --files "hwpc\_report\_1.json, hwpc\_report\_2.json" \ + --output influxdb2 --model PowerReport --uri 127.0.0.1 --port 8086 --db power_consumption --org org_test --token mytoken\ --cpu-base-freq 1900 \ --cpu-error-threshold 2.0 \ --disable-dram-formula \ @@ -318,7 +318,7 @@ In order to run the Formula, you can execute one of the following command lines, ```sh hl_lines="3 4" python -m smartwatts \ --verbose \ - --input csv --model HWPC Report --directory hwcp\_reports.d --name puller\_csv --files "hwpc\_report\_1.json, hwpc\_report\_2.json" \ + --input csv --model HWPCReport --directory hwcp\_reports.d --name puller\_csv --files "hwpc\_report\_1.json, hwpc\_report\_2.json" \ --output csv --directory power_reports.d\ --cpu-base-freq 1900 \ --cpu-error-threshold 2.0 \ @@ -356,12 +356,12 @@ Below you find an example for running the Formula with Docker and Pip: -e POWERAPI_CPU_ERROR_THRESHOLD=2.0 \ -e POWERAPI_DISABLE_DRAM_FORMULA=true \ -e POWERAPI_SENSOR_REPORTS_FREQUENCY=1000 \ - -e POWERAPI_INPUT_PULLER_MODEL=HWPC Report \ + -e POWERAPI_INPUT_PULLER_MODEL=HWPCReport \ -e POWERAPI_INPUT_PULLER_TYPE=mongodb \ -e POWERAPI_INPUT_PULLER_URI=mongodb://127.0.0.1 \ -e POWERAPI_INPUT_PULLER_DB=test \ -e POWERAPI_INPUT_PULLER_COLLECTION=prep \ - -e POWERAPI_OUTPUT_PUSHER_POWER_MODEL=Power Report \ + -e POWERAPI_OUTPUT_PUSHER_POWER_MODEL=PowerReport \ -e POWERAPI_OUTPUT_PUSHER_POWER_TYPE=influxdb2 \ -e POWERAPI_OUTPUT_PUSHER_POWER_URI=127.0.0.1 \ -e POWERAPI_OUTPUT_PUSHER_POWER_PORT=8086 \ @@ -382,11 +382,11 @@ Below you find an example for running the Formula with Docker and Pip: -e POWERAPI_CPU_ERROR_THRESHOLD=2.0 \ -e POWERAPI_DISABLE_DRAM_FORMULA=true \ -e POWERAPI_SENSOR_REPORTS_FREQUENCY=1000 \ - -e POWERAPI_INPUT_PULLER_MODEL=HWPC Report \ + -e POWERAPI_INPUT_PULLER_MODEL=HWPCReport \ -e POWERAPI_INPUT_PULLER_TYPE=csv \ -e POWERAPI_INPUT_PULLER_DIRECTORY=hwpc_reports.d \ -e POWERAPI_INPUT_PULLER_files="hwpc_report_1.json, hwpc_report_2.json" \ - -e POWERAPI_OUTPUT_PUSHER_POWER_MODEL=Power Report \ + -e POWERAPI_OUTPUT_PUSHER_POWER_MODEL=PowerReport \ -e POWERAPI_OUTPUT_PUSHER_POWER_TYPE=influxdb2 \ -e POWERAPI_OUTPUT_PUSHER_POWER_URI=127.0.0.1 \ -e POWERAPI_OUTPUT_PUSHER_POWER_PORT=8086 \ @@ -407,11 +407,11 @@ Below you find an example for running the Formula with Docker and Pip: -e POWERAPI_CPU_ERROR_THRESHOLD=2.0 \ -e POWERAPI_DISABLE_DRAM_FORMULA=true \ -e POWERAPI_SENSOR_REPORTS_FREQUENCY=1000 \ - -e POWERAPI_INPUT_PULLER_MODEL=HWPC Report \ + -e POWERAPI_INPUT_PULLER_MODEL=HWPCReport \ -e POWERAPI_INPUT_PULLER_TYPE=csv \ -e POWERAPI_INPUT_PULLER_DIRECTORY=hwpc_reports.d \ -e POWERAPI_INPUT_PULLER_files="hwpc_report_1.json, hwpc_report_2.json" \ - -e POWERAPI_OUTPUT_PUSHER_POWER_MODEL=Power Report \ + -e POWERAPI_OUTPUT_PUSHER_POWER_MODEL=PowerReport \ -e POWERAPI_OUTPUT_PUSHER_POWER_TYPE=csv \ -e POWERAPI_OUTPUT_PUSHER_POWER_DIRECTORY=power_reports.d \ powerapi/smartwatts-formula @@ -429,12 +429,12 @@ Below you find an example for running the Formula with Docker and Pip: export POWERAPI_CPU_ERROR_THRESHOLD=2.0 export POWERAPI_DISABLE_DRAM_FORMULA=true export POWERAPI_SENSOR_REPORTS_FREQUENCY=1000 - export POWERAPI_INPUT_PULLER_MODEL=HWPC Report + export POWERAPI_INPUT_PULLER_MODEL=HWPCReport export POWERAPI_INPUT_PULLER_TYPE=mongodb export POWERAPI_INPUT_PULLER_URI=mongodb://127.0.0.1 export POWERAPI_INPUT_PULLER_DB=test export POWERAPI_INPUT_PULLER_COLLECTION=prep - export POWERAPI_OUTPUT_PUSHER_POWER_MODEL=Power Report + export POWERAPI_OUTPUT_PUSHER_POWER_MODEL=PowerReport export POWERAPI_OUTPUT_PUSHER_POWER_TYPE=influxdb2 export POWERAPI_OUTPUT_PUSHER_POWER_URI=127.0.0.1 export POWERAPI_OUTPUT_PUSHER_POWER_PORT=8086 @@ -453,11 +453,11 @@ Below you find an example for running the Formula with Docker and Pip: export POWERAPI_CPU_ERROR_THRESHOLD=2.0 export POWERAPI_DISABLE_DRAM_FORMULA=true export POWERAPI_SENSOR_REPORTS_FREQUENCY=1000 - export POWERAPI_INPUT_PULLER_MODEL=HWPC Report \ + export POWERAPI_INPUT_PULLER_MODEL=HWPCReport \ export POWERAPI_INPUT_PULLER_TYPE=csv \ export POWERAPI_INPUT_PULLER_DIRECTORY=hwpc_reports.d \ export POWERAPI_INPUT_PULLER_files="hwpc_report_1.json, hwpc_report_2.json" \ - export POWERAPI_OUTPUT_PUSHER_POWER_MODEL=Power Report + export POWERAPI_OUTPUT_PUSHER_POWER_MODEL=PowerReport export POWERAPI_OUTPUT_PUSHER_POWER_TYPE=influxdb2 export POWERAPI_OUTPUT_PUSHER_POWER_URI=127.0.0.1 export POWERAPI_OUTPUT_PUSHER_POWER_PORT=8086 @@ -476,11 +476,11 @@ Below you find an example for running the Formula with Docker and Pip: export POWERAPI_CPU_ERROR_THRESHOLD=2.0 export POWERAPI_DISABLE_DRAM_FORMULA=true export POWERAPI_SENSOR_REPORTS_FREQUENCY=1000 - export POWERAPI_INPUT_PULLER_MODEL=HWPC Report \ + export POWERAPI_INPUT_PULLER_MODEL=HWPCReport \ export POWERAPI_INPUT_PULLER_TYPE=csv \ export POWERAPI_INPUT_PULLER_DIRECTORY=hwpc_reports.d \ export POWERAPI_INPUT_PULLER_files="hwpc_report_1.json, hwpc_report_2.json" \ - export POWERAPI_OUTPUT_PUSHER_POWER_MODEL=Power Report + export POWERAPI_OUTPUT_PUSHER_POWER_MODEL=PowerReport export POWERAPI_OUTPUT_PUSHER_POWER_TYPE=csv export POWERAPI_OUTPUT_PUSHER_POWER_DIRECTORY=power_reports.d python -m smartwatts @@ -499,7 +499,7 @@ Below you find example Configuration Files to use different input/output and how "stream": true, "input": { "puller": { - "model": "HWPC Report", + "model": "HWPCReport", "type": "mongodb", "uri": "mongodb://127.0.0.1", "db": "test", @@ -530,7 +530,7 @@ Below you find example Configuration Files to use different input/output and how "stream": true, "input": { "puller": { - "model": "HWPC Report", + "model": "HWPCReport", "type": "csv", "directory": "hwpc_reports.d", "files": "hwpc_report_1.json, hwpc_report_2.json", @@ -560,7 +560,7 @@ Below you find example Configuration Files to use different input/output and how "stream": true, "input": { "puller": { - "model": "HWPC Report", + "model": "HWPCReport", "type": "csv", "directory": "hwpc_reports.d", "files": "hwpc_report_1.json, hwpc_report_2.json", @@ -590,7 +590,7 @@ Below you find example Configuration Files to use different input/output and how "stream": true, "input": { "puller": { - "model": "HWPC Report", + "model": "HWPCReport", "type": "csv", "directory": "hwpc_reports.d", "files": "hwpc_report_1.json, hwpc_report_2.json", From 4242ffcc54d5ecfbb6460c6b6c74fa7468780b32 Mon Sep 17 00:00:00 2001 From: KellianL <125127820+KellianL@users.noreply.github.com> Date: Wed, 12 Feb 2025 16:37:15 +0100 Subject: [PATCH 069/108] docs(smartwatts): Fix examples CSV/CSV --- docs/reference/formulas/smartwatts.md | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/docs/reference/formulas/smartwatts.md b/docs/reference/formulas/smartwatts.md index cb68494..17c8304 100644 --- a/docs/reference/formulas/smartwatts.md +++ b/docs/reference/formulas/smartwatts.md @@ -114,8 +114,7 @@ Table below depicts the different parameters for CSV type input: | Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | | ------------- | ----- | ------------- | ------------- | ----------| ------------------------------------ | - | `files` | string | `f` | "" | No | The list of input CSV files with the format "file1,file2,file3..." | - | `directory` | string | `d` | "." (Current directory) | No |The directory where output CSV files will be written | + | `files` | string | `f` | "" | No | The list of input CSV files with the format "file1,file2,file3..." | | `directory` | string | `d` | "." (Current directory) | No |The directory where output CSV files will be written | | `name` | string | `n` | puller_csv | No | The related puller name | | `model` | string | `m` | HWPC Report | No | The Report type stored by the database | @@ -262,8 +261,9 @@ In order to run the Formula, you can execute one of the following command lines, ```sh hl_lines="4 5" docker run -t \ --net=host \ + --volume /tmp/powerapi-sensor-reporting:/data \ powerapi/smartwatts-formula --verbose \ - --input csv --model HWPCReport --directory hwcp\_reports.d --files "hwpc\_report\_1, hwpc\_report\_2" \ + --input csv --model HWPCReport --files "/data/rapl.csv,/data/msr.csv,/data/core.csv" \ --output influxdb2 --model PowerReport --uri 127.0.0.1 --port 8086 --db power_consumption --org org_test --token mytoken \ --cpu-base-freq 1900 \ --cpu-error-threshold 2.0 \ @@ -275,9 +275,10 @@ In order to run the Formula, you can execute one of the following command lines, ```sh hl_lines="4 5" docker run -t \ - --net=host \ + --volume /tmp/powerapi-sensor-reporting:/data \ + --volume $(pwd)/power_reports.d:/home/powerapi/power_reports.d powerapi/smartwatts-formula --verbose \ - --input csv --model HWPCReport --directory hwcp\_reports.d --files "hwpc\_report\_1, hwpc\_report\_2" \ + --input csv --model HWPCReport --files "/data/rapl.csv,/data/msr.csv,/data/core.csv" \ --output csv --directory power_reports.d \ --cpu-base-freq 1900 \ --cpu-error-threshold 2.0 \ @@ -293,7 +294,7 @@ In order to run the Formula, you can execute one of the following command lines, python -m smartwatts \ --verbose \ --input mongodb --model HWPCReport --uri mongodb://127.0.0.1 --db test --collection prep \ - --output influxdb2 --model PowerReport --uri 127.0.0.1 --port 8086 --db power_consumption --org org_test --token mytoken\ + --output influxdb2 --model PowerReport --uri 127.0.0.1 --port 8086 --db power_consumption --org org_test --token mytoken \ --cpu-base-freq 1900 \ --cpu-error-threshold 2.0 \ --disable-dram-formula \ @@ -305,8 +306,8 @@ In order to run the Formula, you can execute one of the following command lines, ```sh hl_lines="3 4" python -m smartwatts \ --verbose \ - --input csv --model HWPCReport --directory hwcp\_reports.d --name puller\_csv --files "hwpc\_report\_1.json, hwpc\_report\_2.json" \ - --output influxdb2 --model PowerReport --uri 127.0.0.1 --port 8086 --db power_consumption --org org_test --token mytoken\ + --input csv --model HWPCReport --name puller_csv --files "rapl.csv,msr.csv,core.csv" \ + --output influxdb2 --model PowerReport --uri 127.0.0.1 --port 8086 --db power_consumption --org org_test --token mytoken \ --cpu-base-freq 1900 \ --cpu-error-threshold 2.0 \ --disable-dram-formula \ @@ -314,12 +315,13 @@ In order to run the Formula, you can execute one of the following command lines, ``` === "Pip with CSV/CSV" - + === `power_reports.d` should be accessible by the user defined in the dockerfile + === You can change permissions with `chmod` ```sh hl_lines="3 4" python -m smartwatts \ --verbose \ - --input csv --model HWPCReport --directory hwcp\_reports.d --name puller\_csv --files "hwpc\_report\_1.json, hwpc\_report\_2.json" \ - --output csv --directory power_reports.d\ + --input csv --model HWPCReport --name puller_csv --files "rapl.csv,msr.csv,core.csv" \ + --output csv --directory power_reports.d \ --cpu-base-freq 1900 \ --cpu-error-threshold 2.0 \ --disable-dram-formula \ From 4d8610ec48994274372a5f859e176d66dd115d41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?No=C3=A9=20Chachignot?= Date: Wed, 9 Apr 2025 13:46:16 +0200 Subject: [PATCH 070/108] Files for getting started update --- docs/getting_started.md | 86 +- docs/script/getting_started/curl_version/.env | 26 + .../curl_version/docker-compose-amd1.yaml | 135 ++ .../curl_version/docker-compose-amd2.yaml | 135 ++ .../curl_version/docker-compose-intel1.yaml | 139 ++ .../curl_version/docker-compose-intel2.yaml | 139 ++ .../getting_started/curl_version/start.sh | 89 + docs/script/getting_started/env_template | 3 +- docs/script/getting_started/test.md | 2041 +++++++++++++++++ 9 files changed, 2731 insertions(+), 62 deletions(-) create mode 100644 docs/script/getting_started/curl_version/.env create mode 100644 docs/script/getting_started/curl_version/docker-compose-amd1.yaml create mode 100644 docs/script/getting_started/curl_version/docker-compose-amd2.yaml create mode 100644 docs/script/getting_started/curl_version/docker-compose-intel1.yaml create mode 100644 docs/script/getting_started/curl_version/docker-compose-intel2.yaml create mode 100755 docs/script/getting_started/curl_version/start.sh create mode 100644 docs/script/getting_started/test.md diff --git a/docs/getting_started.md b/docs/getting_started.md index 5eb168f..2bd05bf 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -1,92 +1,58 @@ # Getting started In this tutorial, we will guide you through the first steps to get started with PowerAPI. -The objective is to get a quick view of the capabilities of PowerAPI, by monitoring a process and getting a quick glimpse at the energy consumption. +The objective is to get a quick view of the capabilities of PowerAPI. A few things are required before we start: - A compatible processor (you can see the compatible CPU architecture [here](./reference/sensors/hwpc-sensor.md#)), and you can look on the following pages to find your CPU architecture: * For [Intel Processor](https://en.wikipedia.org/wiki/List_of_Intel_processors) * For [Intel Xeon Processor](https://en.wikipedia.org/wiki/List_of_Intel_Xeon_processors) * For [AMD Processor](https://en.wikipedia.org/wiki/Table_of_AMD_processors) -- A python installation ready - Docker & Docker-Compose ready (refer to [this official documentation](https://docs.docker.com/engine/install/) and the [post-install steps](https://docs.docker.com/engine/install/linux-postinstall/) if needed !) - Root access -- Optional : [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) to proceed by cloning the repository ## Which components to get a complete stack -If you wish to get started as soon as possible, the archive below will allow you to deploy the following elements: -1. A MongoDB instance to store the [Sensor](./reference/sensors/hwpc-sensor.md) -Reports +The complete stack of PowerAPI is composed of : -3. An [HWPC-Sensor](./reference/sensors/hwpc-sensor.md) that outputs its -[HWPC Reports](./reference/reports/reports.md#hwpc-reports) in a MongoDB Database, -within the HWPC Report Collection - -4. A [SmartWatts](./reference/formulas/smartwatts.md) that streams the -[HWPC Reports](./reference/reports/reports.md#hwpc-reports) from the MongoDB -Database Collection, processes it and outputs its -[Power Reports](./reference/reports/reports.md#power-reports) as CSV files for a -quick glimpse +- The Sensor and the Formula, these are the two main parts of PowerAPI, the Sensor retrieve power consumption related metrics and the Formula compute an estimation of the power consumption. -## Preparation +- The Sensor and the Formula both need an *output*, the supported *output* are listed [here](./reference/database/sources_destinations.md). The forumla will also use the Sensor *output* as his *input* -You can either download the archive by cloning the repository or using wget. +- Finally, they will both need configuration files, as described in the [HWPC-Sensor Documentation](./reference/sensors/hwpc-sensor.md#global-parameters) and in the [SmartWatts Documentation](./reference/formulas/smartwatts.md#global-parameters), several parameters can be set, both globally and for specific Groups monitored. These parameters can also be set using CLI parameters. -=== "git" - ```sh - git clone https://github.com/powerapi-ng/powerapi-ng.github.io.git - cd powerapi-ng.github.io/docs/script/getting-started - ``` +To learn more see the [overview section](./reference/overview.md). -=== "wget" - ``` - wget -c https://raw.githubusercontent.com/powerapi-ng/powerapi-ng.github.io/refs/heads/master/docs/script/getting_started.tar.gz -O - | tar -xz - cd getting_started - ``` -At this stage, you will have all the essential files to begin. Let's go through each element in detail. -### Archive content +## Test powerAPI -```sh -getting_started/ - |--csv/ - |--formula/ - |----smartwatts-mongodb-csv.json - |--sensor/ - |----hwpc-mongodb.json - |--start.sh - |--start.py - |--stop.sh - |--pretty_print.py - |--docker-compose.yaml - |--.env -``` +If you wish to get started as soon as possible, using the following cURL link below will allow you to setup everything quickly using Docker. -#### HWPC-Sensor and SmartWatts Configuration +CURL_LINK_PLACEHOLDER -As described in the [HWPC-Sensor Documentation](./reference/sensors/hwpc-sensor.md#global-parameters) and in the [SmartWatts Documentation](./reference/formulas/smartwatts.md#global-parameters), -several parameters can be set, both globally and for specific Groups monitored for the sensor or the formula. +This curl link will run [this script](./script/getting_started/curl_version/start.sh). It will detect the CPU used, and download the appropriate Docker compose file and environnement file for your configuration. -The provided docker-compose.yaml file uses configuration files and the **.env** to set those parameters. -You can find examples of both those configuration files in the archive under the **formula** and **sensor** directories. +It will then execute the Docker compose file, deploying the following elements for 3 minutes: +1. A MongoDB instance to store the [Sensor](./reference/sensors/hwpc-sensor.md) and the [Formula](./reference/formulas/smartwatts.md) reports -## Turn the key +2. An [HWPC-Sensor](./reference/sensors/hwpc-sensor.md) that outputs its +[HWPC Reports](./reference/reports/reports.md#hwpc-reports) in a MongoDB Database, +within the HWPC Report Collection -Once all set, you shall be able to initiate the stack with: +3. A [SmartWatts](./reference/formulas/smartwatts.md) that streams the +[HWPC Reports](./reference/reports/reports.md#hwpc-reports) from the MongoDB +Database Collection, processes it and outputs its +[Power Reports](./reference/reports/reports.md#power-reports) in another MongoDB Database -```sh -python3 start.py -``` +In this specific case, we don't use configuration file for the Sensor or the Formula and directly use the CLI parameters in the Docker compose file. -After the 2 minutes of monitoring, you will be able to see the result inside the **csv** directory. -If you have trouble understanding the output, you can read the [Power Report documentation](./reference/reports/reports.md#power-reports). +After he result will be visible under the *power* collection within the *power_consumption* database. -!!! info "Quick results overview" - Only in the context of this testing archive, after the monitoring, you can use the following command to get a pretty print of the result directly inside the terminal. +TODO : +- How to watch the result +- Fix all Links +- Overview ? +- Output input - ```sh - python3 pretty_print.py - ``` diff --git a/docs/script/getting_started/curl_version/.env b/docs/script/getting_started/curl_version/.env new file mode 100644 index 0000000..12e57fb --- /dev/null +++ b/docs/script/getting_started/curl_version/.env @@ -0,0 +1,26 @@ +# Sensor image +SENSOR_IMAGE=powerapi/hwpc-sensor:${HWPC_SENSOR_VERSION:-latest} + +# Formula image +FORMULA_IMAGE=powerapi/smartwatts-formula:${SMARTWATTS_VERSION:-latest} + +# Source selection +# Available options: mongodb, socket +POWERAPI_SOURCE=mongodb + +# Destination selection +# Available options: influxdb2, prometheus, mongodb, csv +POWERAPI_DESTINATION=mongodb + +# Third party images +MONGO_IMAGE=mongo:latest +MONGOEXPRESS_IMAGE=mongo-express:latest +CSV_IMAGE=busybox:stable-glibc + +# Docker compose profiles +COMPOSE_PROFILES=${POWERAPI_SOURCE},${POWERAPI_DESTINATION} + +# Permission Value + + + diff --git a/docs/script/getting_started/curl_version/docker-compose-amd1.yaml b/docs/script/getting_started/curl_version/docker-compose-amd1.yaml new file mode 100644 index 0000000..a9f754f --- /dev/null +++ b/docs/script/getting_started/curl_version/docker-compose-amd1.yaml @@ -0,0 +1,135 @@ +services: + ############################################ + # SOURCES & DESTINATIONS # + ############################################ + # MongoDB + mongodb: + container_name: mongodb + image: ${MONGO_IMAGE} + networks: + - powerapi-network + profiles: + - mongodb + + + ############################################ + # POWERAPI # + ############################################ + # PowerAPI Sensor + sensor: + container_name: sensor + image: ${SENSOR_IMAGE} + privileged: true + command: + - "-n" + - "$(hostname -f)" + - "-r" + - "mongodb" + - "-U" + - "mongodb://127.0.0.1" + - "-D" + - "db_sensor" + - "-C" + - "report_0" + - "-s" + - "rapl" + - "-o" + - "-e" + - "RAPL_ENERGY_PKG" + - "-s" + - "msr" + - "-e" + - "TSC" + - "-e" + - "APERF" + - "-e" + - "MPERF" + - "-c" + - "core" + - "-e" + - "CYCLES_NOT_IN_HALT" + - "-e" + - "RETIRED_INSTRUCTIONS" + - "-e" + - "RETIRED_UOPS" + volumes: + - ${PWD}/sensor/hwpc-${POWERAPI_SOURCE}.json:/etc/sensor.json + - type: bind + source: /proc + target: /proc + - type: bind + source: /sys + target: /sys + - type: bind + source: /var/lib/docker/containers + target: /var/lib/docker/containers + depends_on: + - ${POWERAPI_SOURCE} + networks: + - powerapi-network + restart: unless-stopped + + # PowerAPI Formula + formula: + container_name: formula + image: ${FORMULA_IMAGE} + command: + - "--verbose" + - "--input" + - "mongodb" + - "--model" + - "HWPCReport" + - "--uri" + - "mongodb://127.0.0.1" + - "--db" + - "db_sensor" + - "--collection" + - "prep" + - "--output" + - "csv" + - "--model" + - "PowerReport" + - "--directory" + - "/tmp/csv" + - "--cpu-base-freq" + - "1900" + - "--cpu-error-threshold" + - "2.0" + - "--disable-dram-formula" + - "--sensor-reports-frequency" + - "1000" + volumes: + - ${PWD}/formula/smartwatts-${POWERAPI_SOURCE}-${POWERAPI_DESTINATION}.json:/etc/formula.json + - ${PWD}/csv:/tmp/csv + networks: + - powerapi-network + restart: unless-stopped + + + + ############################################ + # TOOLS # + ############################################ + + # Mongo Express + mongo-express: + container_name: mongo-express + image: ${MONGOEXPRESS_IMAGE} + environment: + ME_CONFIG_MONGODB_SERVER: mongodb + ME_CONFIG_MONGODB_URL: mongodb://mongodb:27017 + depends_on: + - mongodb + ports: + - "8081:8081" + networks: + - powerapi-network + profiles: + - mongodb + +############################################ +# DOCKER # +############################################ + +networks: + powerapi-network: \ No newline at end of file diff --git a/docs/script/getting_started/curl_version/docker-compose-amd2.yaml b/docs/script/getting_started/curl_version/docker-compose-amd2.yaml new file mode 100644 index 0000000..94ee2e5 --- /dev/null +++ b/docs/script/getting_started/curl_version/docker-compose-amd2.yaml @@ -0,0 +1,135 @@ +services: + ############################################ + # SOURCES & DESTINATIONS # + ############################################ + # MongoDB + mongodb: + container_name: mongodb + image: ${MONGO_IMAGE} + networks: + - powerapi-network + profiles: + - mongodb + + + ############################################ + # POWERAPI # + ############################################ + # PowerAPI Sensor + sensor: + container_name: sensor + image: ${SENSOR_IMAGE} + privileged: true + command: + - "-n" + - "$(hostname -f)" + - "-r" + - "mongodb" + - "-U" + - "mongodb://127.0.0.1" + - "-D" + - "db_sensor" + - "-C" + - "report_0" + - "-s" + - "rapl" + - "-o" + - "-e" + - "RAPL_ENERGY_PKG" + - "-s" + - "msr" + - "-e" + - "TSC" + - "-e" + - "APERF" + - "-e" + - "MPERF" + - "-c" + - "core" + - "-e" + - "CYCLES_NOT_IN_HALT" + - "-e" + - "RETIRED_INSTRUCTIONS" + - "-e" + - "RETIRED_OPS" + volumes: + - ${PWD}/sensor/hwpc-${POWERAPI_SOURCE}.json:/etc/sensor.json + - type: bind + source: /proc + target: /proc + - type: bind + source: /sys + target: /sys + - type: bind + source: /var/lib/docker/containers + target: /var/lib/docker/containers + depends_on: + - ${POWERAPI_SOURCE} + networks: + - powerapi-network + restart: unless-stopped + + # PowerAPI Formula + formula: + container_name: formula + image: ${FORMULA_IMAGE} + command: + - "--verbose" + - "--input" + - "mongodb" + - "--model" + - "HWPCReport" + - "--uri" + - "mongodb://127.0.0.1" + - "--db" + - "db_sensor" + - "--collection" + - "prep" + - "--output" + - "csv" + - "--model" + - "PowerReport" + - "--directory" + - "/tmp/csv" + - "--cpu-base-freq" + - "1900" + - "--cpu-error-threshold" + - "2.0" + - "--disable-dram-formula" + - "--sensor-reports-frequency" + - "1000" + volumes: + - ${PWD}/formula/smartwatts-${POWERAPI_SOURCE}-${POWERAPI_DESTINATION}.json:/etc/formula.json + - ${PWD}/csv:/tmp/csv + networks: + - powerapi-network + restart: unless-stopped + + + + ############################################ + # TOOLS # + ############################################ + + # Mongo Express + mongo-express: + container_name: mongo-express + image: ${MONGOEXPRESS_IMAGE} + environment: + ME_CONFIG_MONGODB_SERVER: mongodb + ME_CONFIG_MONGODB_URL: mongodb://mongodb:27017 + depends_on: + - mongodb + ports: + - "8081:8081" + networks: + - powerapi-network + profiles: + - mongodb + +############################################ +# DOCKER # +############################################ + +networks: + powerapi-network: diff --git a/docs/script/getting_started/curl_version/docker-compose-intel1.yaml b/docs/script/getting_started/curl_version/docker-compose-intel1.yaml new file mode 100644 index 0000000..0023f09 --- /dev/null +++ b/docs/script/getting_started/curl_version/docker-compose-intel1.yaml @@ -0,0 +1,139 @@ +services: + ############################################ + # SOURCES & DESTINATIONS # + ############################################ + # MongoDB + mongodb: + container_name: mongodb + image: ${MONGO_IMAGE} + networks: + - powerapi-network + profiles: + - mongodb + + + ############################################ + # POWERAPI # + ############################################ + # PowerAPI Sensor + sensor: + container_name: sensor + image: ${SENSOR_IMAGE} + privileged: true + command: + - "-n" + - "$(hostname -f)" + - "-r" + - "mongodb" + - "-U" + - "mongodb://127.0.0.1" + - "-D" + - "db_sensor" + - "-C" + - "report_0" + - "-s" + - "rapl" + - "-o" + - "-p" + - "/sys/fs/cgroup/" + - "-e" + - "RAPL_ENERGY_PKG" + - "-s" + - "msr" + - "-e" + - "TSC" + - "-e" + - "APERF" + - "-e" + - "MPERF" + - "-c" + - "core" + - "-e" + - "CPU_CLK_UNHALTED:REF_P" + - "-e" + - "CPU_CLK_UNHALTED:THREAD_P" + - "-e" + - "LLC_MISSES" + - "-e" + - "INSTRUCTIONS_RETIRED" + volumes: + - ${PWD}/sensor/hwpc-${POWERAPI_SOURCE}.json:/etc/sensor.json + - type: bind + source: /proc + target: /proc + - type: bind + source: /sys + target: /sys + - type: bind + source: /var/lib/docker/containers + target: /var/lib/docker/containers + depends_on: + - ${POWERAPI_SOURCE} + networks: + - powerapi-network + restart: unless-stopped + + # PowerAPI Formula + formula: + container_name: formula + image: ${FORMULA_IMAGE} + command: + - "--verbose" + - "--input" + - "mongodb" + - "--model" + - "HWPCReport" + - "--uri" + - "mongodb://127.0.0.1" + - "--db" + - "db_sensor" + - "--collection" + - "prep" + - "--output" + - "csv" + - "--model" + - "PowerReport" + - "--directory" + - "/tmp/csv" + - "--cpu-base-freq" + - "1900" + - "--cpu-error-threshold" + - "2.0" + - "--disable-dram-formula" + - "--sensor-reports-frequency" + - "1000" + volumes: + - ${PWD}/formula/smartwatts-${POWERAPI_SOURCE}-${POWERAPI_DESTINATION}.json:/etc/formula.json + - ${PWD}/csv:/tmp/csv + networks: + - powerapi-network + restart: unless-stopped + + + + ############################################ + # TOOLS # + ############################################ + + # Mongo Express + mongo-express: + container_name: mongo-express + image: ${MONGOEXPRESS_IMAGE} + environment: + ME_CONFIG_MONGODB_SERVER: mongodb + ME_CONFIG_MONGODB_URL: mongodb://mongodb:27017 + depends_on: + - mongodb + ports: + - "8081:8081" + networks: + - powerapi-network + profiles: + - mongodb + +############################################ +# DOCKER # +############################################ + +networks: + powerapi-network: \ No newline at end of file diff --git a/docs/script/getting_started/curl_version/docker-compose-intel2.yaml b/docs/script/getting_started/curl_version/docker-compose-intel2.yaml new file mode 100644 index 0000000..3f81552 --- /dev/null +++ b/docs/script/getting_started/curl_version/docker-compose-intel2.yaml @@ -0,0 +1,139 @@ +services: + ############################################ + # SOURCES & DESTINATIONS # + ############################################ + # MongoDB + mongodb: + container_name: mongodb + image: ${MONGO_IMAGE} + networks: + - powerapi-network + profiles: + - mongodb + + + ############################################ + # POWERAPI # + ############################################ + # PowerAPI Sensor + sensor: + container_name: sensor + image: ${SENSOR_IMAGE} + privileged: true + command: + - "-n" + - "$(hostname -f)" + - "-r" + - "mongodb" + - "-U" + - "mongodb://127.0.0.1" + - "-D" + - "db_sensor" + - "-C" + - "report_0" + - "-s" + - "rapl" + - "-o" + - "-p" + - "/sys/fs/cgroup/" + - "-e" + - "RAPL_ENERGY_PKG" + - "-s" + - "msr" + - "-e" + - "TSC" + - "-e" + - "APERF" + - "-e" + - "MPERF" + - "-c" + - "core" + - "-e" + - "CPU_CLK_THREAD_UNHALTED:REF_P" + - "-e" + - "CPU_CLK_THREAD_UNHALTED:THREAD_P" + - "-e" + - "LLC_MISSES" + - "-e" + - "INSTRUCTIONS_RETIRED" + volumes: + - ${PWD}/sensor/hwpc-${POWERAPI_SOURCE}.json:/etc/sensor.json + - type: bind + source: /proc + target: /proc + - type: bind + source: /sys + target: /sys + - type: bind + source: /var/lib/docker/containers + target: /var/lib/docker/containers + depends_on: + - ${POWERAPI_SOURCE} + networks: + - powerapi-network + restart: unless-stopped + + # PowerAPI Formula + formula: + container_name: formula + image: ${FORMULA_IMAGE} + command: + - "--verbose" + - "--input" + - "mongodb" + - "--model" + - "HWPCReport" + - "--uri" + - "mongodb://127.0.0.1" + - "--db" + - "db_sensor" + - "--collection" + - "prep" + - "--output" + - "csv" + - "--model" + - "PowerReport" + - "--directory" + - "/tmp/csv" + - "--cpu-base-freq" + - "1900" + - "--cpu-error-threshold" + - "2.0" + - "--disable-dram-formula" + - "--sensor-reports-frequency" + - "1000" + volumes: + - ${PWD}/formula/smartwatts-${POWERAPI_SOURCE}-${POWERAPI_DESTINATION}.json:/etc/formula.json + - ${PWD}/csv:/tmp/csv + networks: + - powerapi-network + restart: unless-stopped + + + + ############################################ + # TOOLS # + ############################################ + + # Mongo Express + mongo-express: + container_name: mongo-express + image: ${MONGOEXPRESS_IMAGE} + environment: + ME_CONFIG_MONGODB_SERVER: mongodb + ME_CONFIG_MONGODB_URL: mongodb://mongodb:27017 + depends_on: + - mongodb + ports: + - "8081:8081" + networks: + - powerapi-network + profiles: + - mongodb + +############################################ +# DOCKER # +############################################ + +networks: + powerapi-network: \ No newline at end of file diff --git a/docs/script/getting_started/curl_version/start.sh b/docs/script/getting_started/curl_version/start.sh new file mode 100755 index 0000000..b7a7718 --- /dev/null +++ b/docs/script/getting_started/curl_version/start.sh @@ -0,0 +1,89 @@ +#!/bin/bash + +Intel1=("Sandy bridge" "Ivy bridge" "Haswell" "Broadwell" "Comet lake") +Intel2=("Skylake" "Cascade lake" "Kaby Lake R" "Kaby Lake" "Coffee Lake" "Amber Lake" "Rocket lake" "Whiskey lake") +AMD1=("Zen" "Zen+" "Zen 2") +AMD2=("Zen 3" "Zen 4") + +Intel1Event=("CPU_CLK_UNHALTED:REF_P" "CPU_CLK_UNHALTED:THREAD_P" "LLC_MISSES" "INSTRUCTIONS_RETIRED") +Intel2Event=("CPU_CLK_THREAD_UNHALTED:REF_P" "CPU_CLK_THREAD_UNHALTED:THREAD_P" "LLC_MISSES" "INSTRUCTIONS_RETIRED") +AMD1Event=("CYCLES_NOT_IN_HALT" "RETIRED_INSTRUCTIONS" "RETIRED_UOPS") +AMD2Event=("CYCLES_NOT_IN_HALT" "RETIRED_INSTRUCTIONS" "RETIRED_OPS") + + +echo "Detecting cgroup..." + +cgroup=$(stat -fc %T /sys/fs/cgroup/) + +if [ "$cgroup" = "cgroup2fs" ] +then + echo "Cgroup v2 detected" + cgroup_path="/sys/fs/cgroup/" +else + echo "Cgroup v1 detected" + cgroup_path="/sys/fs/cgroup/perf_event" +fi + +echo "Detecting CPU..." + +CPU=$(cat /proc/cpuinfo | grep 'model name' | uniq) +CPU=${CPU:13:3} + +echo "$CPU" + +if [ "$CPU" = "Int" ] +then + echo "Intel Processor Detected" + CPUF=$(cat /sys/devices/cpu/caps/pmu_name) + echo "$CPUF" + if [ "$CPUF" = "sandybridge" ] || [ "$CPUF" = "ivybridge" ] || [ "$CPUF" = "haswell" ] || [ "$CPUF" = "broadwell" ] || [ "$CPUF" = "cometlake" ] + then + echo "Intel1" + curl -sSL http://localhost:8000/docker-compose-intel1.yaml -o docker-compose-intel1.yaml + sed -i "/sensor:/,/^[^ ]/s/^\(\s*- \"-o\"\)/\1\n - \"-p\"\n - \"${cgroup_path}\"/" docker-compose-intel1.yaml + docker compose -f docker-compose-intel1.yaml up + fi + if [ "$CPUF" = "skylake" ] || [ "$CPUF" = "cascadelake" ] || [ "$CPUF" = "kabylaker" ] || [ "$CPUF" = "kabylake" ] || [ "$CPUF" = "coffeelake" ] || [ "$CPUF" = "amberlake" ] || [ "$CPUF" = "rocketlake" ] || [ "$CPUF" = "whiskeylake" ] + then + echo "Intel2" + curl -sSL http://localhost:8000/docker-compose-intel2.yaml -o docker-compose-intel2.yaml + sed -i "/- \"-o\"/a\ - \"-p\"\n - \"${cgroup_path}\"" docker-compose-intel2.yaml + docker compose -f docker-compose-intel2.yaml up + fi +fi + +if [ "$CPU" = "AMD" ] +then + echo "AMD" + CPUF=$(cat /proc/cpuinfo | grep 'cpu family' | uniq) + CPUF=${CPUF:13:2} + CPUF=$((CPUF - 23)) + + if [ "$CPUF" = "0" ] + then + echo "AMD1" + echo "$CPUF" + curl -sSL http://localhost:8000/docker-compose-amd1.yaml -o docker-compose-amd1.yaml + sed -i "/sensor:/,/^[^ ]/s/^\(\s*- \"-o\"\)/\1\n - \"-p\"\n - \"${cgroup_path}\"/" docker-compose-amd1.yaml + docker compose -f docker-compose-amd1.yaml up + fi + + if [ "$CPUF" = "2" ] + then + echo "AMD2" + echo "$CPUF" + curl -sSL http://localhost:8000/docker-compose-amd2.yaml -o docker-compose-amd2.yaml + sed -i "/sensor:/,/^[^ ]/s/^\(\s*- \"-o\"\)/\1\n - \"-p\"\n - \"${cgroup_path}\"/" docker-compose-amd2.yaml + docker compose -f docker-compose-amd2.yaml up + fi + + echo "$CPUF" +fi + +docker compose logs sensor -f & +docker compose logs formula -f & +sleep 180 + +set -ueo pipefail +set +x +docker compose down \ No newline at end of file diff --git a/docs/script/getting_started/env_template b/docs/script/getting_started/env_template index 216686f..7084ad2 100644 --- a/docs/script/getting_started/env_template +++ b/docs/script/getting_started/env_template @@ -19,5 +19,4 @@ INFLUXDB_IMAGE=influxdb:latest CSV_IMAGE=busybox:stable-glibc # Docker compose profiles -COMPOSE_PROFILES=${POWERAPI_SOURCE},${POWERAPI_DESTINATION} - +COMPOSE_PROFILES=${POWERAPI_SOURCE},${POWERAPI_DESTINATION} \ No newline at end of file diff --git a/docs/script/getting_started/test.md b/docs/script/getting_started/test.md new file mode 100644 index 0000000..a5d925f --- /dev/null +++ b/docs/script/getting_started/test.md @@ -0,0 +1,2041 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + powerapi/README.md at master · powerapi-ng/powerapi · GitHub + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ + + +
+ Skip to content + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + +
+ + + + + + + + + +
+
+
+ + + + + + + + + + + + +
+ +
+ +
+ +
+ + + + / + + powerapi + + + Public +
+ + +
+ +
+ + +
+
+ +
+
+ + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + +

Latest commit

 

History

History
65 lines (50 loc) · 8.03 KB

README.md

File metadata and controls

65 lines (50 loc) · 8.03 KB

Powerapi

+

License: BSD 3 +GitHub Workflow Status +PyPI +Codecov +Zenodo +JOSS paper

+

PowerAPI is a middleware toolkit for building software-defined power meters. +Software-defined power meters are configurable software libraries that can estimate the power consumption of software in real-time. +PowerAPI supports the acquisition of raw metrics from a wide diversity of sensors (eg., physical meters, processor interfaces, hardware counters, OS counters) and the delivery of power consumptions via different channels (including file system, network, web, graphical). +As a middleware toolkit, PowerAPI offers the capability of assembling power meters «à la carte» to accommodate user requirements.

+

About

+

PowerAPI is an open-source project developed by the Spirals project-team, a joint research group between the University of Lille and Inria.

+

The documentation of the project is available here.

+

Mailing list

+

You can follow the latest news and asks questions by subscribing to our mailing list.

+

Contributing

+

If you would like to contribute code you can do so through GitHub by forking the repository and sending a pull request.

+

When submitting code, please make every effort to follow existing conventions and style in order to keep the code as readable as possible.

+

Publications

+ +

Use Cases

+

PowerAPI is used in a variety of projects to address key challenges of GreenIT:

+
    +
  • SmartWatts is a self-adaptive power meter that can estimate the energy consumption of software containers in real-time.
  • +
  • GenPack provides a container scheduling strategy to minimize the energy footprint of cloud infrastructures.
  • +
  • VirtualWatts provides process-level power estimation of applications running in virtual machines.
  • +
  • Web Energy Archive ranks popular websites based on the energy footpring they imposes to browsers.
  • +
  • Greenspector optimises the power consumption of software by identifying potential energy leaks in the source code.
  • +
+

Research Projects

+

Currently, PowerAPI is used in two research projects:

+ +

License

+

PowerAPI is licensed under the BSD-3-Clause License. See the LICENSE file for details.

+

FOSSA Status

+
+
+ + + + +
+ +
+ +
+
+ +
+ +
+

Footer

+ + + + +
+
+ + + + + © 2025 GitHub, Inc. + +
+ + +
+
+ + + + + + + + + + + + + + + + + + + +
+ +
+
+ + + From 781489a1d59d01de30035b707d3837844a27650c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?No=C3=A9=20Chachignot?= Date: Wed, 9 Apr 2025 13:48:37 +0200 Subject: [PATCH 071/108] docs(getting-started): Update script --- docs/script/getting_started/curl_version/start.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/script/getting_started/curl_version/start.sh b/docs/script/getting_started/curl_version/start.sh index b7a7718..ab76535 100755 --- a/docs/script/getting_started/curl_version/start.sh +++ b/docs/script/getting_started/curl_version/start.sh @@ -80,8 +80,6 @@ then echo "$CPUF" fi -docker compose logs sensor -f & -docker compose logs formula -f & sleep 180 set -ueo pipefail From 2876137876029b47d453377447e7b4c872501f75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?No=C3=A9=20Chachignot?= Date: Wed, 9 Apr 2025 14:11:44 +0200 Subject: [PATCH 072/108] docs(getting-started): Fix sed --- .../getting_started/curl_version/start.sh | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/docs/script/getting_started/curl_version/start.sh b/docs/script/getting_started/curl_version/start.sh index ab76535..73c0992 100755 --- a/docs/script/getting_started/curl_version/start.sh +++ b/docs/script/getting_started/curl_version/start.sh @@ -39,16 +39,18 @@ then if [ "$CPUF" = "sandybridge" ] || [ "$CPUF" = "ivybridge" ] || [ "$CPUF" = "haswell" ] || [ "$CPUF" = "broadwell" ] || [ "$CPUF" = "cometlake" ] then echo "Intel1" - curl -sSL http://localhost:8000/docker-compose-intel1.yaml -o docker-compose-intel1.yaml - sed -i "/sensor:/,/^[^ ]/s/^\(\s*- \"-o\"\)/\1\n - \"-p\"\n - \"${cgroup_path}\"/" docker-compose-intel1.yaml + curl -sSL https://raw.githubusercontent.com/Inkedstinct/powerapi-ng.github.io/refs/heads/7_doc/nld_proofread/docs/script/getting_started/curl_version/docker-compose-intel1.yaml -o docker-compose-intel1.yaml + sed -i "/- \"-o\"/a\ - \"-p\"\n - \"${cgroup_path}\"" docker-compose-intel1.yaml docker compose -f docker-compose-intel1.yaml up + sed -i '/- "-p"/,+1d' docker-compose-intel1.yaml fi if [ "$CPUF" = "skylake" ] || [ "$CPUF" = "cascadelake" ] || [ "$CPUF" = "kabylaker" ] || [ "$CPUF" = "kabylake" ] || [ "$CPUF" = "coffeelake" ] || [ "$CPUF" = "amberlake" ] || [ "$CPUF" = "rocketlake" ] || [ "$CPUF" = "whiskeylake" ] then echo "Intel2" - curl -sSL http://localhost:8000/docker-compose-intel2.yaml -o docker-compose-intel2.yaml + curl -sSL https://raw.githubusercontent.com/Inkedstinct/powerapi-ng.github.io/refs/heads/7_doc/nld_proofread/docs/script/getting_started/curl_version/docker-compose-intel2.yaml -o docker-compose-intel2.yaml sed -i "/- \"-o\"/a\ - \"-p\"\n - \"${cgroup_path}\"" docker-compose-intel2.yaml docker compose -f docker-compose-intel2.yaml up + sed -i '/- "-p"/,+1d' docker-compose-intel2.yaml fi fi @@ -63,18 +65,20 @@ then then echo "AMD1" echo "$CPUF" - curl -sSL http://localhost:8000/docker-compose-amd1.yaml -o docker-compose-amd1.yaml - sed -i "/sensor:/,/^[^ ]/s/^\(\s*- \"-o\"\)/\1\n - \"-p\"\n - \"${cgroup_path}\"/" docker-compose-amd1.yaml + curl -sSL https://raw.githubusercontent.com/Inkedstinct/powerapi-ng.github.io/refs/heads/7_doc/nld_proofread/docs/script/getting_started/curl_version/docker-compose-amd1.yaml -o docker-compose-amd1.yaml + sed -i "/- \"-o\"/a\ - \"-p\"\n - \"${cgroup_path}\"" docker-compose-amd1.yaml docker compose -f docker-compose-amd1.yaml up + sed -i '/- "-p"/,+1d' docker-compose-amd1.yaml fi if [ "$CPUF" = "2" ] then echo "AMD2" echo "$CPUF" - curl -sSL http://localhost:8000/docker-compose-amd2.yaml -o docker-compose-amd2.yaml - sed -i "/sensor:/,/^[^ ]/s/^\(\s*- \"-o\"\)/\1\n - \"-p\"\n - \"${cgroup_path}\"/" docker-compose-amd2.yaml + curl -sSL https://raw.githubusercontent.com/Inkedstinct/powerapi-ng.github.io/refs/heads/7_doc/nld_proofread/docs/script/getting_started/curl_version/docker-compose-amd2.yaml -o docker-compose-amd2.yaml + sed -i "/- \"-o\"/a\ - \"-p\"\n - \"${cgroup_path}\"" docker-compose-amd2.yaml docker compose -f docker-compose-amd2.yaml up + sed -i '/- "-p"/,+1d' docker-compose-amd2.yaml fi echo "$CPUF" From 18307eccaa1c05821cedecf1c3074a02666b2761 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?No=C3=A9=20Chachignot?= Date: Wed, 9 Apr 2025 14:58:36 +0200 Subject: [PATCH 073/108] docs(getting-started): Refactor logs --- .../curl_version/docker-compose-intel2.yaml | 2 - .../getting_started/curl_version/start.sh | 78 ++++++++++--------- 2 files changed, 43 insertions(+), 37 deletions(-) diff --git a/docs/script/getting_started/curl_version/docker-compose-intel2.yaml b/docs/script/getting_started/curl_version/docker-compose-intel2.yaml index 3f81552..4e837c8 100644 --- a/docs/script/getting_started/curl_version/docker-compose-intel2.yaml +++ b/docs/script/getting_started/curl_version/docker-compose-intel2.yaml @@ -34,8 +34,6 @@ services: - "-s" - "rapl" - "-o" - - "-p" - - "/sys/fs/cgroup/" - "-e" - "RAPL_ENERGY_PKG" - "-s" diff --git a/docs/script/getting_started/curl_version/start.sh b/docs/script/getting_started/curl_version/start.sh index 73c0992..82fa259 100755 --- a/docs/script/getting_started/curl_version/start.sh +++ b/docs/script/getting_started/curl_version/start.sh @@ -1,5 +1,15 @@ #!/bin/bash +log_info() { + echo -e "\033[1;32m[INFO]\033[0m $1" +} +log_warn() { + echo -e "\033[1;33m[WARN]\033[0m $1" +} +log_error() { + echo -e "\033[1;31m[ERROR]\033[0m $1" +} + Intel1=("Sandy bridge" "Ivy bridge" "Haswell" "Broadwell" "Comet lake") Intel2=("Skylake" "Cascade lake" "Kaby Lake R" "Kaby Lake" "Coffee Lake" "Amber Lake" "Rocket lake" "Whiskey lake") AMD1=("Zen" "Zen+" "Zen 2") @@ -10,82 +20,80 @@ Intel2Event=("CPU_CLK_THREAD_UNHALTED:REF_P" "CPU_CLK_THREAD_UNHALTED:THREAD_P" AMD1Event=("CYCLES_NOT_IN_HALT" "RETIRED_INSTRUCTIONS" "RETIRED_UOPS") AMD2Event=("CYCLES_NOT_IN_HALT" "RETIRED_INSTRUCTIONS" "RETIRED_OPS") - -echo "Detecting cgroup..." +log_info "Detecting cgroup..." cgroup=$(stat -fc %T /sys/fs/cgroup/) -if [ "$cgroup" = "cgroup2fs" ] -then - echo "Cgroup v2 detected" +if [ "$cgroup" = "cgroup2fs" ]; then + log_info " Cgroup v2 detected" cgroup_path="/sys/fs/cgroup/" else - echo "Cgroup v1 detected" + log_info " Cgroup v1 detected" cgroup_path="/sys/fs/cgroup/perf_event" fi -echo "Detecting CPU..." +log_info "Detecting CPU..." CPU=$(cat /proc/cpuinfo | grep 'model name' | uniq) CPU=${CPU:13:3} -echo "$CPU" +log_info "CPU string detected: $CPU" -if [ "$CPU" = "Int" ] -then - echo "Intel Processor Detected" +if [ "$CPU" = "Int" ]; then + log_info "Intel CPU Detected" CPUF=$(cat /sys/devices/cpu/caps/pmu_name) - echo "$CPUF" - if [ "$CPUF" = "sandybridge" ] || [ "$CPUF" = "ivybridge" ] || [ "$CPUF" = "haswell" ] || [ "$CPUF" = "broadwell" ] || [ "$CPUF" = "cometlake" ] - then - echo "Intel1" + log_info "PMU name: $CPUF" + + if [ "$CPUF" = "sandybridge" ] || [ "$CPUF" = "ivybridge" ] || [ "$CPUF" = "haswell" ] || [ "$CPUF" = "broadwell" ] || [ "$CPUF" = "cometlake" ]; then + log_info "Intel CPU compatible" curl -sSL https://raw.githubusercontent.com/Inkedstinct/powerapi-ng.github.io/refs/heads/7_doc/nld_proofread/docs/script/getting_started/curl_version/docker-compose-intel1.yaml -o docker-compose-intel1.yaml sed -i "/- \"-o\"/a\ - \"-p\"\n - \"${cgroup_path}\"" docker-compose-intel1.yaml docker compose -f docker-compose-intel1.yaml up sed -i '/- "-p"/,+1d' docker-compose-intel1.yaml - fi - if [ "$CPUF" = "skylake" ] || [ "$CPUF" = "cascadelake" ] || [ "$CPUF" = "kabylaker" ] || [ "$CPUF" = "kabylake" ] || [ "$CPUF" = "coffeelake" ] || [ "$CPUF" = "amberlake" ] || [ "$CPUF" = "rocketlake" ] || [ "$CPUF" = "whiskeylake" ] - then - echo "Intel2" + elif [ "$CPUF" = "skylake" ] || [ "$CPUF" = "cascadelake" ] || [ "$CPUF" = "kabylaker" ] || [ "$CPUF" = "kabylake" ] || [ "$CPUF" = "coffeelake" ] || [ "$CPUF" = "amberlake" ] || [ "$CPUF" = "rocketlake" ] || [ "$CPUF" = "whiskeylake" ]; then + log_info "Intel CPU compatible" curl -sSL https://raw.githubusercontent.com/Inkedstinct/powerapi-ng.github.io/refs/heads/7_doc/nld_proofread/docs/script/getting_started/curl_version/docker-compose-intel2.yaml -o docker-compose-intel2.yaml sed -i "/- \"-o\"/a\ - \"-p\"\n - \"${cgroup_path}\"" docker-compose-intel2.yaml docker compose -f docker-compose-intel2.yaml up sed -i '/- "-p"/,+1d' docker-compose-intel2.yaml + else + log_error "CPU not supported" + exit 1 fi -fi -if [ "$CPU" = "AMD" ] -then - echo "AMD" +elif [ "$CPU" = "AMD" ]; then + log_info "AMD CPU Detected" CPUF=$(cat /proc/cpuinfo | grep 'cpu family' | uniq) CPUF=${CPUF:13:2} CPUF=$((CPUF - 23)) + log_info "Normalized AMD family: $CPUF" - if [ "$CPUF" = "0" ] - then - echo "AMD1" - echo "$CPUF" + if [ "$CPUF" = "0" ]; then + log_info "AMD CPU Compatible" curl -sSL https://raw.githubusercontent.com/Inkedstinct/powerapi-ng.github.io/refs/heads/7_doc/nld_proofread/docs/script/getting_started/curl_version/docker-compose-amd1.yaml -o docker-compose-amd1.yaml sed -i "/- \"-o\"/a\ - \"-p\"\n - \"${cgroup_path}\"" docker-compose-amd1.yaml docker compose -f docker-compose-amd1.yaml up sed -i '/- "-p"/,+1d' docker-compose-amd1.yaml - fi - - if [ "$CPUF" = "2" ] - then - echo "AMD2" - echo "$CPUF" + elif [ "$CPUF" = "2" ]; then + log_info "AMD CPU Compatible" curl -sSL https://raw.githubusercontent.com/Inkedstinct/powerapi-ng.github.io/refs/heads/7_doc/nld_proofread/docs/script/getting_started/curl_version/docker-compose-amd2.yaml -o docker-compose-amd2.yaml sed -i "/- \"-o\"/a\ - \"-p\"\n - \"${cgroup_path}\"" docker-compose-amd2.yaml docker compose -f docker-compose-amd2.yaml up sed -i '/- "-p"/,+1d' docker-compose-amd2.yaml + else + log_error "CPU not supported" + exit 1 fi - echo "$CPUF" +else + log_error "Unrecognized CPU architecture: $CPU" + exit 1 fi +log_info "Waiting 180s before cleanup..." sleep 180 set -ueo pipefail set +x -docker compose down \ No newline at end of file +log_info "Shutting down containers..." +docker compose down From f1293b3d9dbcbd4edfa17e6a7f6b08675b666c3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?No=C3=A9=20Chachignot?= Date: Wed, 9 Apr 2025 15:11:07 +0200 Subject: [PATCH 074/108] docs(getting-started): Fix mongodb URI --- .../getting_started/curl_version/docker-compose-amd1.yaml | 4 ++-- .../getting_started/curl_version/docker-compose-amd2.yaml | 4 ++-- .../getting_started/curl_version/docker-compose-intel1.yaml | 4 ++-- .../getting_started/curl_version/docker-compose-intel2.yaml | 4 ++-- docs/script/getting_started/curl_version/start.sh | 1 - 5 files changed, 8 insertions(+), 9 deletions(-) diff --git a/docs/script/getting_started/curl_version/docker-compose-amd1.yaml b/docs/script/getting_started/curl_version/docker-compose-amd1.yaml index a9f754f..30640b0 100644 --- a/docs/script/getting_started/curl_version/docker-compose-amd1.yaml +++ b/docs/script/getting_started/curl_version/docker-compose-amd1.yaml @@ -26,7 +26,7 @@ services: - "-r" - "mongodb" - "-U" - - "mongodb://127.0.0.1" + - "mongodb://mongodb:27017" - "-D" - "db_sensor" - "-C" @@ -80,7 +80,7 @@ services: - "--model" - "HWPCReport" - "--uri" - - "mongodb://127.0.0.1" + - "mongodb://mongodb:27017" - "--db" - "db_sensor" - "--collection" diff --git a/docs/script/getting_started/curl_version/docker-compose-amd2.yaml b/docs/script/getting_started/curl_version/docker-compose-amd2.yaml index 94ee2e5..bddc12a 100644 --- a/docs/script/getting_started/curl_version/docker-compose-amd2.yaml +++ b/docs/script/getting_started/curl_version/docker-compose-amd2.yaml @@ -26,7 +26,7 @@ services: - "-r" - "mongodb" - "-U" - - "mongodb://127.0.0.1" + - "mongodb://mongodb:27017" - "-D" - "db_sensor" - "-C" @@ -80,7 +80,7 @@ services: - "--model" - "HWPCReport" - "--uri" - - "mongodb://127.0.0.1" + - "mongodb://mongodb:27017" - "--db" - "db_sensor" - "--collection" diff --git a/docs/script/getting_started/curl_version/docker-compose-intel1.yaml b/docs/script/getting_started/curl_version/docker-compose-intel1.yaml index 0023f09..1cb8aab 100644 --- a/docs/script/getting_started/curl_version/docker-compose-intel1.yaml +++ b/docs/script/getting_started/curl_version/docker-compose-intel1.yaml @@ -26,7 +26,7 @@ services: - "-r" - "mongodb" - "-U" - - "mongodb://127.0.0.1" + - "mongodb://mongodb:27017" - "-D" - "db_sensor" - "-C" @@ -84,7 +84,7 @@ services: - "--model" - "HWPCReport" - "--uri" - - "mongodb://127.0.0.1" + - "mongodb://mongodb:27017" - "--db" - "db_sensor" - "--collection" diff --git a/docs/script/getting_started/curl_version/docker-compose-intel2.yaml b/docs/script/getting_started/curl_version/docker-compose-intel2.yaml index 4e837c8..4347261 100644 --- a/docs/script/getting_started/curl_version/docker-compose-intel2.yaml +++ b/docs/script/getting_started/curl_version/docker-compose-intel2.yaml @@ -26,7 +26,7 @@ services: - "-r" - "mongodb" - "-U" - - "mongodb://127.0.0.1" + - "mongodb://mongodb:27017" - "-D" - "db_sensor" - "-C" @@ -82,7 +82,7 @@ services: - "--model" - "HWPCReport" - "--uri" - - "mongodb://127.0.0.1" + - "mongodb://mongodb:27017" - "--db" - "db_sensor" - "--collection" diff --git a/docs/script/getting_started/curl_version/start.sh b/docs/script/getting_started/curl_version/start.sh index 82fa259..7c47fb8 100755 --- a/docs/script/getting_started/curl_version/start.sh +++ b/docs/script/getting_started/curl_version/start.sh @@ -55,7 +55,6 @@ if [ "$CPU" = "Int" ]; then curl -sSL https://raw.githubusercontent.com/Inkedstinct/powerapi-ng.github.io/refs/heads/7_doc/nld_proofread/docs/script/getting_started/curl_version/docker-compose-intel2.yaml -o docker-compose-intel2.yaml sed -i "/- \"-o\"/a\ - \"-p\"\n - \"${cgroup_path}\"" docker-compose-intel2.yaml docker compose -f docker-compose-intel2.yaml up - sed -i '/- "-p"/,+1d' docker-compose-intel2.yaml else log_error "CPU not supported" exit 1 From 8c703285ca8ba4c81802433f82f6a713cab507a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?No=C3=A9=20Chachignot?= Date: Wed, 9 Apr 2025 15:26:06 +0200 Subject: [PATCH 075/108] Add .env to script --- docs/getting_started.md | 2 +- docs/script/getting_started/curl_version/start.sh | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/docs/getting_started.md b/docs/getting_started.md index 2bd05bf..2d07189 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -29,7 +29,7 @@ To learn more see the [overview section](./reference/overview.md). If you wish to get started as soon as possible, using the following cURL link below will allow you to setup everything quickly using Docker. -CURL_LINK_PLACEHOLDER +curl -sSL https://raw.githubusercontent.com/Inkedstinct/powerapi-ng.github.io/refs/heads/7_doc/nld_proofread/docs/script/getting_started/curl_version/start.sh | bash This curl link will run [this script](./script/getting_started/curl_version/start.sh). It will detect the CPU used, and download the appropriate Docker compose file and environnement file for your configuration. diff --git a/docs/script/getting_started/curl_version/start.sh b/docs/script/getting_started/curl_version/start.sh index 7c47fb8..52f65eb 100755 --- a/docs/script/getting_started/curl_version/start.sh +++ b/docs/script/getting_started/curl_version/start.sh @@ -20,6 +20,18 @@ Intel2Event=("CPU_CLK_THREAD_UNHALTED:REF_P" "CPU_CLK_THREAD_UNHALTED:THREAD_P" AMD1Event=("CYCLES_NOT_IN_HALT" "RETIRED_INSTRUCTIONS" "RETIRED_UOPS") AMD2Event=("CYCLES_NOT_IN_HALT" "RETIRED_INSTRUCTIONS" "RETIRED_OPS") +log_info "Starting" + +log_info "Downloading .env" +curl -sSL https://raw.githubusercontent.com/Inkedstinct/powerapi-ng.github.io/refs/heads/7_doc/nld_proofread/docs/script/getting_started/curl_version/.env -o .env + +if [[ -s .env ]]; then + log_info ".env file downloaded successfully" +else + log_error "Failed to download .env or file is empty" + exit 1 +fi + log_info "Detecting cgroup..." cgroup=$(stat -fc %T /sys/fs/cgroup/) From e5ea5bf9c925bde737a322d4546c8ac412bae413 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?No=C3=A9=20Chachignot?= Date: Wed, 9 Apr 2025 15:43:37 +0200 Subject: [PATCH 076/108] Add cleanup at the end, Change the way docker compose is stared --- .../getting_started/curl_version/start.sh | 32 ++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/docs/script/getting_started/curl_version/start.sh b/docs/script/getting_started/curl_version/start.sh index 52f65eb..d1c03ab 100755 --- a/docs/script/getting_started/curl_version/start.sh +++ b/docs/script/getting_started/curl_version/start.sh @@ -60,13 +60,13 @@ if [ "$CPU" = "Int" ]; then log_info "Intel CPU compatible" curl -sSL https://raw.githubusercontent.com/Inkedstinct/powerapi-ng.github.io/refs/heads/7_doc/nld_proofread/docs/script/getting_started/curl_version/docker-compose-intel1.yaml -o docker-compose-intel1.yaml sed -i "/- \"-o\"/a\ - \"-p\"\n - \"${cgroup_path}\"" docker-compose-intel1.yaml - docker compose -f docker-compose-intel1.yaml up + docker compose -f docker-compose-intel1.yaml up -d sed -i '/- "-p"/,+1d' docker-compose-intel1.yaml elif [ "$CPUF" = "skylake" ] || [ "$CPUF" = "cascadelake" ] || [ "$CPUF" = "kabylaker" ] || [ "$CPUF" = "kabylake" ] || [ "$CPUF" = "coffeelake" ] || [ "$CPUF" = "amberlake" ] || [ "$CPUF" = "rocketlake" ] || [ "$CPUF" = "whiskeylake" ]; then log_info "Intel CPU compatible" curl -sSL https://raw.githubusercontent.com/Inkedstinct/powerapi-ng.github.io/refs/heads/7_doc/nld_proofread/docs/script/getting_started/curl_version/docker-compose-intel2.yaml -o docker-compose-intel2.yaml sed -i "/- \"-o\"/a\ - \"-p\"\n - \"${cgroup_path}\"" docker-compose-intel2.yaml - docker compose -f docker-compose-intel2.yaml up + docker compose -f docker-compose-intel2.yaml up -d else log_error "CPU not supported" exit 1 @@ -83,13 +83,13 @@ elif [ "$CPU" = "AMD" ]; then log_info "AMD CPU Compatible" curl -sSL https://raw.githubusercontent.com/Inkedstinct/powerapi-ng.github.io/refs/heads/7_doc/nld_proofread/docs/script/getting_started/curl_version/docker-compose-amd1.yaml -o docker-compose-amd1.yaml sed -i "/- \"-o\"/a\ - \"-p\"\n - \"${cgroup_path}\"" docker-compose-amd1.yaml - docker compose -f docker-compose-amd1.yaml up + docker compose -f docker-compose-amd1.yaml up -d sed -i '/- "-p"/,+1d' docker-compose-amd1.yaml elif [ "$CPUF" = "2" ]; then log_info "AMD CPU Compatible" curl -sSL https://raw.githubusercontent.com/Inkedstinct/powerapi-ng.github.io/refs/heads/7_doc/nld_proofread/docs/script/getting_started/curl_version/docker-compose-amd2.yaml -o docker-compose-amd2.yaml sed -i "/- \"-o\"/a\ - \"-p\"\n - \"${cgroup_path}\"" docker-compose-amd2.yaml - docker compose -f docker-compose-amd2.yaml up + docker compose -f docker-compose-amd2.yaml up -d sed -i '/- "-p"/,+1d' docker-compose-amd2.yaml else log_error "CPU not supported" @@ -101,6 +101,9 @@ else exit 1 fi +bash -c 'docker compose logs sensor -f &' +bash -c 'docker compose logs formula -f &' + log_info "Waiting 180s before cleanup..." sleep 180 @@ -108,3 +111,24 @@ set -ueo pipefail set +x log_info "Shutting down containers..." docker compose down + +log_info "Removing .env and docker-compose files..." + +if [[ -f .env ]]; then + rm .env + log_info "Removed .env" +else + log_warn ".env not found for cleanup" +fi + +for file in docker-compose-*.yaml; do + if [[ -f "$file" ]]; then + rm "$file" + log_info "Removed $file" + else + log_warn "No docker-compose yaml files found for cleanup" + fi +done + +log_info "Cleanup complete" +log_info "Script completed successfully, result can be found under the csv directory" From 300f2e434086f86a132162bf84d0d983caebb181 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?No=C3=A9=20Chachignot?= Date: Wed, 9 Apr 2025 16:23:08 +0200 Subject: [PATCH 077/108] docs(getting-started): Try fix for permission --- .../getting_started/curl_version/start.sh | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/docs/script/getting_started/curl_version/start.sh b/docs/script/getting_started/curl_version/start.sh index d1c03ab..38f9630 100755 --- a/docs/script/getting_started/curl_version/start.sh +++ b/docs/script/getting_started/curl_version/start.sh @@ -32,6 +32,27 @@ else exit 1 fi +if [ ! -d "csv" ]; then + mkdir -p csv + log_info "'csv' directory created." +else + log_warn "'csv' directory already existing." +fi + +if [ ! -d "formula" ]; then + mkdir -p formula + log_info "'formula' directory created." +else + log_warn "'formula' directory already existing." +fi + +if [ ! -f "formula/smartwatts-mongodb-mongodb.json" ]; then + touch formula/smartwatts-mongodb-mongodb.json + log_info "'formula/smartwatts-mongodb-mongodb.json' file created." +else + log_warn "'formula/smartwatts-mongodb-mongodb.json' file already existing." +fi + log_info "Detecting cgroup..." cgroup=$(stat -fc %T /sys/fs/cgroup/) From aefcf4cb02d1541028f2eae90176d5745f2d1e0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?No=C3=A9=20Chachignot?= Date: Wed, 9 Apr 2025 16:31:20 +0200 Subject: [PATCH 078/108] docs(getting-started): Try fix for permission for sensor --- docs/script/getting_started/curl_version/start.sh | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/script/getting_started/curl_version/start.sh b/docs/script/getting_started/curl_version/start.sh index 38f9630..d6ec36f 100755 --- a/docs/script/getting_started/curl_version/start.sh +++ b/docs/script/getting_started/curl_version/start.sh @@ -53,6 +53,20 @@ else log_warn "'formula/smartwatts-mongodb-mongodb.json' file already existing." fi +if [ ! -d "sensor" ]; then + mkdir -p sensor + log_info "'sensor' directory created." +else + log_warn "'sensor' directory already existing." +fi + +if [ ! -f "sensor/hwpc-mongodb.json" ]; then + touch sensor/hwpc-mongodb.json + log_info "'sensor/hwpc-mongodb.json' file created." +else + log_warn "'sensor/hwpc-mongodb.json' file already existing." +fi + log_info "Detecting cgroup..." cgroup=$(stat -fc %T /sys/fs/cgroup/) From 269093d0e87e54d73a2f8c79af1af816c9106a03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?No=C3=A9=20Chachignot?= Date: Wed, 9 Apr 2025 16:46:42 +0200 Subject: [PATCH 079/108] docs(getting-started): try to fix logs --- docs/script/getting_started/curl_version/start.sh | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/script/getting_started/curl_version/start.sh b/docs/script/getting_started/curl_version/start.sh index d6ec36f..74d3340 100755 --- a/docs/script/getting_started/curl_version/start.sh +++ b/docs/script/getting_started/curl_version/start.sh @@ -136,10 +136,9 @@ else exit 1 fi -bash -c 'docker compose logs sensor -f &' -bash -c 'docker compose logs formula -f &' - -log_info "Waiting 180s before cleanup..." +timeout 180 docker compose logs sensor -f & +timeout 180 docker compose logs formula -f & +log_info "Running 180s before cleanup..." sleep 180 set -ueo pipefail From 2937d71c5254e16972351742e11d24f576330104 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?No=C3=A9=20Chachignot?= Date: Tue, 15 Apr 2025 11:52:06 +0200 Subject: [PATCH 080/108] docs(getting-started): try to fix logs + set smartwatts version to 2.3.1 --- docs/script/getting_started/curl_version/.env | 2 +- docs/script/getting_started/curl_version/start.sh | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/script/getting_started/curl_version/.env b/docs/script/getting_started/curl_version/.env index 12e57fb..e1974a1 100644 --- a/docs/script/getting_started/curl_version/.env +++ b/docs/script/getting_started/curl_version/.env @@ -2,7 +2,7 @@ SENSOR_IMAGE=powerapi/hwpc-sensor:${HWPC_SENSOR_VERSION:-latest} # Formula image -FORMULA_IMAGE=powerapi/smartwatts-formula:${SMARTWATTS_VERSION:-latest} +FORMULA_IMAGE=powerapi/smartwatts-formula:${SMARTWATTS_VERSION:-2.3.1} # Source selection # Available options: mongodb, socket diff --git a/docs/script/getting_started/curl_version/start.sh b/docs/script/getting_started/curl_version/start.sh index 74d3340..e130a56 100755 --- a/docs/script/getting_started/curl_version/start.sh +++ b/docs/script/getting_started/curl_version/start.sh @@ -138,7 +138,8 @@ fi timeout 180 docker compose logs sensor -f & timeout 180 docker compose logs formula -f & -log_info "Running 180s before cleanup..." +log_info "Running + 180s before cleanup..." sleep 180 set -ueo pipefail From 21439e93c967b26db82fa356d4a87aed5af5632e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?No=C3=A9=20Chachignot?= Date: Tue, 15 Apr 2025 13:55:56 +0200 Subject: [PATCH 081/108] docs(getting-started): Clean up + logs --- .../getting_started/curl_version/start.sh | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/docs/script/getting_started/curl_version/start.sh b/docs/script/getting_started/curl_version/start.sh index e130a56..caf33c0 100755 --- a/docs/script/getting_started/curl_version/start.sh +++ b/docs/script/getting_started/curl_version/start.sh @@ -72,10 +72,10 @@ log_info "Detecting cgroup..." cgroup=$(stat -fc %T /sys/fs/cgroup/) if [ "$cgroup" = "cgroup2fs" ]; then - log_info " Cgroup v2 detected" + log_info "Cgroup v2 detected" cgroup_path="/sys/fs/cgroup/" else - log_info " Cgroup v1 detected" + log_info "Cgroup v1 detected" cgroup_path="/sys/fs/cgroup/perf_event" fi @@ -95,13 +95,12 @@ if [ "$CPU" = "Int" ]; then log_info "Intel CPU compatible" curl -sSL https://raw.githubusercontent.com/Inkedstinct/powerapi-ng.github.io/refs/heads/7_doc/nld_proofread/docs/script/getting_started/curl_version/docker-compose-intel1.yaml -o docker-compose-intel1.yaml sed -i "/- \"-o\"/a\ - \"-p\"\n - \"${cgroup_path}\"" docker-compose-intel1.yaml - docker compose -f docker-compose-intel1.yaml up -d - sed -i '/- "-p"/,+1d' docker-compose-intel1.yaml + docker compose -f docker-compose-intel1.yaml up elif [ "$CPUF" = "skylake" ] || [ "$CPUF" = "cascadelake" ] || [ "$CPUF" = "kabylaker" ] || [ "$CPUF" = "kabylake" ] || [ "$CPUF" = "coffeelake" ] || [ "$CPUF" = "amberlake" ] || [ "$CPUF" = "rocketlake" ] || [ "$CPUF" = "whiskeylake" ]; then log_info "Intel CPU compatible" curl -sSL https://raw.githubusercontent.com/Inkedstinct/powerapi-ng.github.io/refs/heads/7_doc/nld_proofread/docs/script/getting_started/curl_version/docker-compose-intel2.yaml -o docker-compose-intel2.yaml sed -i "/- \"-o\"/a\ - \"-p\"\n - \"${cgroup_path}\"" docker-compose-intel2.yaml - docker compose -f docker-compose-intel2.yaml up -d + docker compose -f docker-compose-intel2.yaml up else log_error "CPU not supported" exit 1 @@ -118,14 +117,12 @@ elif [ "$CPU" = "AMD" ]; then log_info "AMD CPU Compatible" curl -sSL https://raw.githubusercontent.com/Inkedstinct/powerapi-ng.github.io/refs/heads/7_doc/nld_proofread/docs/script/getting_started/curl_version/docker-compose-amd1.yaml -o docker-compose-amd1.yaml sed -i "/- \"-o\"/a\ - \"-p\"\n - \"${cgroup_path}\"" docker-compose-amd1.yaml - docker compose -f docker-compose-amd1.yaml up -d - sed -i '/- "-p"/,+1d' docker-compose-amd1.yaml + docker compose -f docker-compose-amd1.yaml up elif [ "$CPUF" = "2" ]; then log_info "AMD CPU Compatible" curl -sSL https://raw.githubusercontent.com/Inkedstinct/powerapi-ng.github.io/refs/heads/7_doc/nld_proofread/docs/script/getting_started/curl_version/docker-compose-amd2.yaml -o docker-compose-amd2.yaml sed -i "/- \"-o\"/a\ - \"-p\"\n - \"${cgroup_path}\"" docker-compose-amd2.yaml - docker compose -f docker-compose-amd2.yaml up -d - sed -i '/- "-p"/,+1d' docker-compose-amd2.yaml + docker compose -f docker-compose-amd2.yaml up else log_error "CPU not supported" exit 1 @@ -136,10 +133,6 @@ else exit 1 fi -timeout 180 docker compose logs sensor -f & -timeout 180 docker compose logs formula -f & -log_info "Running - 180s before cleanup..." sleep 180 set -ueo pipefail From 707f560ac30a993f4b147ed4e4c8b963d2ae7be7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?No=C3=A9=20Chachignot?= Date: Tue, 15 Apr 2025 15:11:44 +0200 Subject: [PATCH 082/108] docs(getting-started): update compose --- .../getting_started/curl_version/docker-compose-intel2.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/script/getting_started/curl_version/docker-compose-intel2.yaml b/docs/script/getting_started/curl_version/docker-compose-intel2.yaml index 4347261..1117de8 100644 --- a/docs/script/getting_started/curl_version/docker-compose-intel2.yaml +++ b/docs/script/getting_started/curl_version/docker-compose-intel2.yaml @@ -30,7 +30,7 @@ services: - "-D" - "db_sensor" - "-C" - - "report_0" + - "prep" - "-s" - "rapl" - "-o" From b6afc712687730b82e4caa5c3c57d9e48558ff6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?No=C3=A9=20Chachignot?= Date: Tue, 15 Apr 2025 15:21:41 +0200 Subject: [PATCH 083/108] docs(getting-started): fix all compose --- .../getting_started/curl_version/docker-compose-amd1.yaml | 4 ++-- .../getting_started/curl_version/docker-compose-amd2.yaml | 4 ++-- .../getting_started/curl_version/docker-compose-intel1.yaml | 4 ++-- .../getting_started/curl_version/docker-compose-intel2.yaml | 2 +- docs/script/getting_started/curl_version/start.sh | 1 - 5 files changed, 7 insertions(+), 8 deletions(-) diff --git a/docs/script/getting_started/curl_version/docker-compose-amd1.yaml b/docs/script/getting_started/curl_version/docker-compose-amd1.yaml index 30640b0..878e6c7 100644 --- a/docs/script/getting_started/curl_version/docker-compose-amd1.yaml +++ b/docs/script/getting_started/curl_version/docker-compose-amd1.yaml @@ -22,7 +22,7 @@ services: privileged: true command: - "-n" - - "$(hostname -f)" + - "sensor" - "-r" - "mongodb" - "-U" @@ -30,7 +30,7 @@ services: - "-D" - "db_sensor" - "-C" - - "report_0" + - "prep" - "-s" - "rapl" - "-o" diff --git a/docs/script/getting_started/curl_version/docker-compose-amd2.yaml b/docs/script/getting_started/curl_version/docker-compose-amd2.yaml index bddc12a..0747114 100644 --- a/docs/script/getting_started/curl_version/docker-compose-amd2.yaml +++ b/docs/script/getting_started/curl_version/docker-compose-amd2.yaml @@ -22,7 +22,7 @@ services: privileged: true command: - "-n" - - "$(hostname -f)" + - "sensor" - "-r" - "mongodb" - "-U" @@ -30,7 +30,7 @@ services: - "-D" - "db_sensor" - "-C" - - "report_0" + - "prep" - "-s" - "rapl" - "-o" diff --git a/docs/script/getting_started/curl_version/docker-compose-intel1.yaml b/docs/script/getting_started/curl_version/docker-compose-intel1.yaml index 1cb8aab..ab08025 100644 --- a/docs/script/getting_started/curl_version/docker-compose-intel1.yaml +++ b/docs/script/getting_started/curl_version/docker-compose-intel1.yaml @@ -22,7 +22,7 @@ services: privileged: true command: - "-n" - - "$(hostname -f)" + - "sensor" - "-r" - "mongodb" - "-U" @@ -30,7 +30,7 @@ services: - "-D" - "db_sensor" - "-C" - - "report_0" + - "prep" - "-s" - "rapl" - "-o" diff --git a/docs/script/getting_started/curl_version/docker-compose-intel2.yaml b/docs/script/getting_started/curl_version/docker-compose-intel2.yaml index 1117de8..c7f0bea 100644 --- a/docs/script/getting_started/curl_version/docker-compose-intel2.yaml +++ b/docs/script/getting_started/curl_version/docker-compose-intel2.yaml @@ -22,7 +22,7 @@ services: privileged: true command: - "-n" - - "$(hostname -f)" + - "sensor" - "-r" - "mongodb" - "-U" diff --git a/docs/script/getting_started/curl_version/start.sh b/docs/script/getting_started/curl_version/start.sh index caf33c0..7500889 100755 --- a/docs/script/getting_started/curl_version/start.sh +++ b/docs/script/getting_started/curl_version/start.sh @@ -127,7 +127,6 @@ elif [ "$CPU" = "AMD" ]; then log_error "CPU not supported" exit 1 fi - else log_error "Unrecognized CPU architecture: $CPU" exit 1 From 29c1bd0a08eee204ba25d8241305bbb6097ad53e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?No=C3=A9=20Chachignot?= Date: Tue, 15 Apr 2025 15:29:10 +0200 Subject: [PATCH 084/108] docs(getting-started): remove logs --- docs/script/getting_started/curl_version/start.sh | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/script/getting_started/curl_version/start.sh b/docs/script/getting_started/curl_version/start.sh index 7500889..f70998b 100755 --- a/docs/script/getting_started/curl_version/start.sh +++ b/docs/script/getting_started/curl_version/start.sh @@ -95,12 +95,12 @@ if [ "$CPU" = "Int" ]; then log_info "Intel CPU compatible" curl -sSL https://raw.githubusercontent.com/Inkedstinct/powerapi-ng.github.io/refs/heads/7_doc/nld_proofread/docs/script/getting_started/curl_version/docker-compose-intel1.yaml -o docker-compose-intel1.yaml sed -i "/- \"-o\"/a\ - \"-p\"\n - \"${cgroup_path}\"" docker-compose-intel1.yaml - docker compose -f docker-compose-intel1.yaml up + docker compose -f docker-compose-intel1.yaml up -d elif [ "$CPUF" = "skylake" ] || [ "$CPUF" = "cascadelake" ] || [ "$CPUF" = "kabylaker" ] || [ "$CPUF" = "kabylake" ] || [ "$CPUF" = "coffeelake" ] || [ "$CPUF" = "amberlake" ] || [ "$CPUF" = "rocketlake" ] || [ "$CPUF" = "whiskeylake" ]; then log_info "Intel CPU compatible" curl -sSL https://raw.githubusercontent.com/Inkedstinct/powerapi-ng.github.io/refs/heads/7_doc/nld_proofread/docs/script/getting_started/curl_version/docker-compose-intel2.yaml -o docker-compose-intel2.yaml sed -i "/- \"-o\"/a\ - \"-p\"\n - \"${cgroup_path}\"" docker-compose-intel2.yaml - docker compose -f docker-compose-intel2.yaml up + docker compose -f docker-compose-intel2.yaml up -d else log_error "CPU not supported" exit 1 @@ -117,12 +117,12 @@ elif [ "$CPU" = "AMD" ]; then log_info "AMD CPU Compatible" curl -sSL https://raw.githubusercontent.com/Inkedstinct/powerapi-ng.github.io/refs/heads/7_doc/nld_proofread/docs/script/getting_started/curl_version/docker-compose-amd1.yaml -o docker-compose-amd1.yaml sed -i "/- \"-o\"/a\ - \"-p\"\n - \"${cgroup_path}\"" docker-compose-amd1.yaml - docker compose -f docker-compose-amd1.yaml up + docker compose -f docker-compose-amd1.yaml up -d elif [ "$CPUF" = "2" ]; then log_info "AMD CPU Compatible" curl -sSL https://raw.githubusercontent.com/Inkedstinct/powerapi-ng.github.io/refs/heads/7_doc/nld_proofread/docs/script/getting_started/curl_version/docker-compose-amd2.yaml -o docker-compose-amd2.yaml sed -i "/- \"-o\"/a\ - \"-p\"\n - \"${cgroup_path}\"" docker-compose-amd2.yaml - docker compose -f docker-compose-amd2.yaml up + docker compose -f docker-compose-amd2.yaml up -d else log_error "CPU not supported" exit 1 @@ -132,6 +132,8 @@ else exit 1 fi +log_info "Containers started successfully" +log_info "Waiting for 3 minutes..." sleep 180 set -ueo pipefail From 649f5e6a91395a7f9510cda64dcfb572ea4b548a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?No=C3=A9=20Chachignot?= Date: Tue, 15 Apr 2025 15:38:00 +0200 Subject: [PATCH 085/108] docs(getting-started): Fix cleanup --- docs/script/getting_started/curl_version/start.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/script/getting_started/curl_version/start.sh b/docs/script/getting_started/curl_version/start.sh index f70998b..0f14228 100755 --- a/docs/script/getting_started/curl_version/start.sh +++ b/docs/script/getting_started/curl_version/start.sh @@ -86,6 +86,8 @@ CPU=${CPU:13:3} log_info "CPU string detected: $CPU" +dockerfile="" + if [ "$CPU" = "Int" ]; then log_info "Intel CPU Detected" CPUF=$(cat /sys/devices/cpu/caps/pmu_name) @@ -96,11 +98,13 @@ if [ "$CPU" = "Int" ]; then curl -sSL https://raw.githubusercontent.com/Inkedstinct/powerapi-ng.github.io/refs/heads/7_doc/nld_proofread/docs/script/getting_started/curl_version/docker-compose-intel1.yaml -o docker-compose-intel1.yaml sed -i "/- \"-o\"/a\ - \"-p\"\n - \"${cgroup_path}\"" docker-compose-intel1.yaml docker compose -f docker-compose-intel1.yaml up -d + dockerfile="docker-compose-intel1.yaml" elif [ "$CPUF" = "skylake" ] || [ "$CPUF" = "cascadelake" ] || [ "$CPUF" = "kabylaker" ] || [ "$CPUF" = "kabylake" ] || [ "$CPUF" = "coffeelake" ] || [ "$CPUF" = "amberlake" ] || [ "$CPUF" = "rocketlake" ] || [ "$CPUF" = "whiskeylake" ]; then log_info "Intel CPU compatible" curl -sSL https://raw.githubusercontent.com/Inkedstinct/powerapi-ng.github.io/refs/heads/7_doc/nld_proofread/docs/script/getting_started/curl_version/docker-compose-intel2.yaml -o docker-compose-intel2.yaml sed -i "/- \"-o\"/a\ - \"-p\"\n - \"${cgroup_path}\"" docker-compose-intel2.yaml docker compose -f docker-compose-intel2.yaml up -d + dockerfile="docker-compose-intel2.yaml" else log_error "CPU not supported" exit 1 @@ -118,11 +122,13 @@ elif [ "$CPU" = "AMD" ]; then curl -sSL https://raw.githubusercontent.com/Inkedstinct/powerapi-ng.github.io/refs/heads/7_doc/nld_proofread/docs/script/getting_started/curl_version/docker-compose-amd1.yaml -o docker-compose-amd1.yaml sed -i "/- \"-o\"/a\ - \"-p\"\n - \"${cgroup_path}\"" docker-compose-amd1.yaml docker compose -f docker-compose-amd1.yaml up -d + dockerfile="docker-compose-amd1.yaml" elif [ "$CPUF" = "2" ]; then log_info "AMD CPU Compatible" curl -sSL https://raw.githubusercontent.com/Inkedstinct/powerapi-ng.github.io/refs/heads/7_doc/nld_proofread/docs/script/getting_started/curl_version/docker-compose-amd2.yaml -o docker-compose-amd2.yaml sed -i "/- \"-o\"/a\ - \"-p\"\n - \"${cgroup_path}\"" docker-compose-amd2.yaml docker compose -f docker-compose-amd2.yaml up -d + dockerfile="docker-compose-amd2.yaml" else log_error "CPU not supported" exit 1 @@ -139,7 +145,7 @@ sleep 180 set -ueo pipefail set +x log_info "Shutting down containers..." -docker compose down +docker compose -f $dockerfile down log_info "Removing .env and docker-compose files..." From aff4d277af0498956c1cf0ae2ee30185aaa1aea4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?No=C3=A9=20Chachignot?= Date: Tue, 15 Apr 2025 15:47:30 +0200 Subject: [PATCH 086/108] docs(getting-started): Fix cleanup --- docs/script/getting_started/curl_version/start.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/script/getting_started/curl_version/start.sh b/docs/script/getting_started/curl_version/start.sh index 0f14228..e840718 100755 --- a/docs/script/getting_started/curl_version/start.sh +++ b/docs/script/getting_started/curl_version/start.sh @@ -165,5 +165,11 @@ for file in docker-compose-*.yaml; do fi done +rm -rf formula +log_info "Removed formula directory" + +rm -rf sensor +log_info "Removed sensor directory" + log_info "Cleanup complete" log_info "Script completed successfully, result can be found under the csv directory" From a14feff389362ca36459bf4228fdb1f3aee7120e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?No=C3=A9=20Chachignot?= Date: Tue, 15 Apr 2025 17:09:15 +0200 Subject: [PATCH 087/108] docs(hwpc-sensor): Update Zen version compatibility --- docs/reference/sensors/hwpc-sensor.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/sensors/hwpc-sensor.md b/docs/reference/sensors/hwpc-sensor.md index 2e26d03..116ca85 100644 --- a/docs/reference/sensors/hwpc-sensor.md +++ b/docs/reference/sensors/hwpc-sensor.md @@ -18,7 +18,7 @@ most common architectures: | Architecture | RAPL Supported | |--------------|----------------| -| AMD Zen (1, 2, 3, 4) | :material-check: Supported | +| AMD Zen (2, 3) | :material-check: Supported | | Intel Sandy Bridge and [newer](https://en.wikipedia.org/wiki/List_of_Intel_Core_processors#Core_i_(2nd_gen)) (except for below mentions) | :material-check: Supported | | Intel Tiger Lake (11th Gen) | :material-close: Not Supported | | Intel Alder Lake (12th Gen) | :material-close: Not Supported | From 25c806cbe68e240a6a0831cfa4df3b507b5b5d98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?No=C3=A9=20Chachignot?= Date: Wed, 23 Apr 2025 15:21:41 +0200 Subject: [PATCH 088/108] docs(getting-started): Fix for Zen 1 no compatibility --- docs/script/getting_started/curl_version/start.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/script/getting_started/curl_version/start.sh b/docs/script/getting_started/curl_version/start.sh index e840718..7e4f7fd 100755 --- a/docs/script/getting_started/curl_version/start.sh +++ b/docs/script/getting_started/curl_version/start.sh @@ -22,6 +22,13 @@ AMD2Event=("CYCLES_NOT_IN_HALT" "RETIRED_INSTRUCTIONS" "RETIRED_OPS") log_info "Starting" +log_info "Checking for RAPL availability..." +if [ ! -d "/sys/class/powercap/intel-rapl" ]; then + log_error "RAPL not available, please check that you CPU supports RAPL" + exit 1 +fi +log_info "RAPL available under /sys/class/powercap/intel-rapl" + log_info "Downloading .env" curl -sSL https://raw.githubusercontent.com/Inkedstinct/powerapi-ng.github.io/refs/heads/7_doc/nld_proofread/docs/script/getting_started/curl_version/.env -o .env From 05872a90cbe61cc6877c15270b841383f1b01e68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?No=C3=A9=20Chachignot?= Date: Tue, 6 May 2025 15:24:31 +0200 Subject: [PATCH 089/108] docs(getting-started): Correction to .md, .env and clean up script, still problem with amd --- docs/getting_started.md | 13 ++++--------- docs/script/getting_started/curl_version/.env | 2 -- docs/script/getting_started/curl_version/start.sh | 10 ---------- 3 files changed, 4 insertions(+), 21 deletions(-) diff --git a/docs/getting_started.md b/docs/getting_started.md index 2d07189..ee63fd1 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -18,7 +18,7 @@ The complete stack of PowerAPI is composed of : - The Sensor and the Formula, these are the two main parts of PowerAPI, the Sensor retrieve power consumption related metrics and the Formula compute an estimation of the power consumption. -- The Sensor and the Formula both need an *output*, the supported *output* are listed [here](./reference/database/sources_destinations.md). The forumla will also use the Sensor *output* as his *input* +- The Sensor and the Formula both need an *output*, the supported *output* are listed [here](./reference/database/sources_destinations.md). The formula will also use the Sensor *output* as his *input* - Finally, they will both need configuration files, as described in the [HWPC-Sensor Documentation](./reference/sensors/hwpc-sensor.md#global-parameters) and in the [SmartWatts Documentation](./reference/formulas/smartwatts.md#global-parameters), several parameters can be set, both globally and for specific Groups monitored. These parameters can also be set using CLI parameters. @@ -35,7 +35,7 @@ This curl link will run [this script](./script/getting_started/curl_version/star It will then execute the Docker compose file, deploying the following elements for 3 minutes: -1. A MongoDB instance to store the [Sensor](./reference/sensors/hwpc-sensor.md) and the [Formula](./reference/formulas/smartwatts.md) reports +1. A MongoDB instance to store the [Sensor](./reference/sensors/hwpc-sensor.md) reports. 2. An [HWPC-Sensor](./reference/sensors/hwpc-sensor.md) that outputs its [HWPC Reports](./reference/reports/reports.md#hwpc-reports) in a MongoDB Database, @@ -44,15 +44,10 @@ within the HWPC Report Collection 3. A [SmartWatts](./reference/formulas/smartwatts.md) that streams the [HWPC Reports](./reference/reports/reports.md#hwpc-reports) from the MongoDB Database Collection, processes it and outputs its -[Power Reports](./reference/reports/reports.md#power-reports) in another MongoDB Database +[Power Reports](./reference/reports/reports.md#power-reports) into CSV. In this specific case, we don't use configuration file for the Sensor or the Formula and directly use the CLI parameters in the Docker compose file. -After he result will be visible under the *power* collection within the *power_consumption* database. +After he result will be visible under the *csv* directory. -TODO : -- How to watch the result -- Fix all Links -- Overview ? -- Output input diff --git a/docs/script/getting_started/curl_version/.env b/docs/script/getting_started/curl_version/.env index e1974a1..aacd139 100644 --- a/docs/script/getting_started/curl_version/.env +++ b/docs/script/getting_started/curl_version/.env @@ -20,7 +20,5 @@ CSV_IMAGE=busybox:stable-glibc # Docker compose profiles COMPOSE_PROFILES=${POWERAPI_SOURCE},${POWERAPI_DESTINATION} -# Permission Value - diff --git a/docs/script/getting_started/curl_version/start.sh b/docs/script/getting_started/curl_version/start.sh index 7e4f7fd..6c1fce5 100755 --- a/docs/script/getting_started/curl_version/start.sh +++ b/docs/script/getting_started/curl_version/start.sh @@ -10,16 +10,6 @@ log_error() { echo -e "\033[1;31m[ERROR]\033[0m $1" } -Intel1=("Sandy bridge" "Ivy bridge" "Haswell" "Broadwell" "Comet lake") -Intel2=("Skylake" "Cascade lake" "Kaby Lake R" "Kaby Lake" "Coffee Lake" "Amber Lake" "Rocket lake" "Whiskey lake") -AMD1=("Zen" "Zen+" "Zen 2") -AMD2=("Zen 3" "Zen 4") - -Intel1Event=("CPU_CLK_UNHALTED:REF_P" "CPU_CLK_UNHALTED:THREAD_P" "LLC_MISSES" "INSTRUCTIONS_RETIRED") -Intel2Event=("CPU_CLK_THREAD_UNHALTED:REF_P" "CPU_CLK_THREAD_UNHALTED:THREAD_P" "LLC_MISSES" "INSTRUCTIONS_RETIRED") -AMD1Event=("CYCLES_NOT_IN_HALT" "RETIRED_INSTRUCTIONS" "RETIRED_UOPS") -AMD2Event=("CYCLES_NOT_IN_HALT" "RETIRED_INSTRUCTIONS" "RETIRED_OPS") - log_info "Starting" log_info "Checking for RAPL availability..." From d990be4321a60049c7eb245c226299986cdfa8f6 Mon Sep 17 00:00:00 2001 From: daniel Date: Mon, 12 May 2025 12:04:35 +0200 Subject: [PATCH 090/108] style(hwpc-sensor): Correct some sentences in HWPC Sensor Description --- docs/reference/sensors/hwpc-sensor.md | 80 +++++++++++++++++---------- 1 file changed, 51 insertions(+), 29 deletions(-) diff --git a/docs/reference/sensors/hwpc-sensor.md b/docs/reference/sensors/hwpc-sensor.md index 116ca85..5c99b84 100644 --- a/docs/reference/sensors/hwpc-sensor.md +++ b/docs/reference/sensors/hwpc-sensor.md @@ -1,9 +1,9 @@ # HWPC Sensor -HardWare Performance Counter (HWPC) Sensor is a tool that monitors the Intel CPU -performance counter and the power consumption of CPU. +HardWare Performance Counter (HWPC) Sensor is a tool that monitors the CPU +performance counters and its power consumption. -The figure below depicts how Sensor works in general : +The figure below depicts how Sensor works in general: ![HWPC Sensor Overview](../../assets/images/reference/sensors/PowerAPI_HWPCSensorOverview.drawio.svg){ width="1000px"} @@ -14,7 +14,7 @@ The following table gives a glimpse of RAPL support regarding most common architectures: !!! tip "CPU architecture" - `lscpu` will give you the necessary information about your CPU Architecture + `lscpu` will give you the necessary information about your CPU Architecture | Architecture | RAPL Supported | |--------------|----------------| @@ -28,21 +28,20 @@ most common architectures: !!! note "HWPC Sensor pre-requisites" In addition of a supported architecture, there is some pre-requisites: - - Using a Linux distribution exposing the [perf](https://perf.wiki.kernel.org/index.php/Main_Page) api - - Using Cgroup version 1 when using version 1.2 or older. See [this section](../cgroup/cgroup_v1_activation.md) about its configuration - - Deploying on a physical device as the HWPC Sensor must have access to the real CPU register + - Using a Linux distribution exposing the [`perf`](https://perf.wiki.kernel.org/index.php/Main_Page) api + - Using Cgroup version 1 when using version 1.2 or older. See [this section](../cgroup/cgroup_v1_activation.md) about its configuration + - Deploying on a physical server as the HWPC Sensor must have access to the real CPU register ## Sensor outputs -The sensor provides raw values of performance counters as well as `RAPL` raw values in microjoules. +The sensor provides raw values of performance counters and `RAPL` raw values in micro-joules. ## Installation -The default installation is done through a Docker container. -The different images can be found on the [Docker Hub](https://hub.docker.com/r/powerapi/hwpc-sensor/tags) +The default installation is done through a Docker container. The different images can be found on [Docker Hub](https://hub.docker.com/r/powerapi/hwpc-sensor/tags). -Here is a sample to deploy the latest image version available. +Here is the command to deploy the latest image version available. === "Docker" ```sh @@ -55,7 +54,7 @@ The following tabs gives a complete overview of available parameters, along with ??? info "Global Parameters" - The table below shows the different parameters related to the Sensor global configuration, nested objects (system, container, output) are described in dedicated sections below: + The table below shows the different parameters related to the Sensor global configuration: | Parameter | Type | CLI shortcut | Default Value | Description | | ------------- | ----- | :-------------: | :-------------: | ------------------------------------ | @@ -65,8 +64,9 @@ The following tabs gives a complete overview of available parameters, along with |`cgroup_basepath` | `string` | `p` | `/sys/fs/cgroup` (`cgroup` V2) | The base path for `cgroups`. To use `cgroup` V1 `/sys/fs/cgroup/perf_event` needs to be used as value | |`system` | `dict` | `s` | - | A system group with a monitoring type and a list of system events (cf. [`system` Group Parameters](hwpc-sensor.md#system-and-container-groups-parameters)) | |`container` | `dict` | `c` | - | A group with a monitoring type and a list of events (cf. [`system` Group Parameters](hwpc-sensor.md#system-and-container-groups-parameters)) | - |`output` | `dict`| `r` | { "type": "csv", "directory": "." } | The [output information](hwpc-sensor.md#output), the Sensor only supports [MongoDB](./hwpc-sensor.md#mongodb-output) (`mongodb`), [CSV](./hwpc-sensor.md#csv-output) (`csv`) and [socket](./hwpc-sensor.md#socket-output) as output. | + |`output` | `dict`| `r` | { "type": "csv", "directory": "." } | The [output information](hwpc-sensor.md#output), the Sensor only supports [MongoDB](hwpc-sensor.md#output "MongoDB Output") (`mongodb`), [CSV](hwpc-sensor.md#output) (`csv`) and [Socket](hwpc-sensor.md#output) (`socket`) as output. | + Nested parameters (system, container, output) are described in dedicated sections below. ??? info "Group Parameters (`system` and `container`)" The table below shows the different parameters related to the Sensor `system` and `container` configuration fields: @@ -91,7 +91,7 @@ The following tabs gives a complete overview of available parameters, along with ### Output -As precised, three kinds of outputs are supported: Socket, MongoDB and CSV files. +Three kinds of outputs are supported: Socket, MongoDB and CSV files. ??? info "MongoDB Output" @@ -122,12 +122,12 @@ As precised, three kinds of outputs are supported: Socket, MongoDB and CSV files ### Running the Sensor with a Configuration File -The following snippets describe the configuration file of an HWPC Sensor instance, two examples are provided for possible outputs: +The following snippets describe the configuration file of an HWPC Sensor instance. One example is provided for each possible output: !!! example "Examples for an Intel Processor, using a Configuration File" - + === "MongoDB Output" - + ```json hl_lines="5-10" title="config_file.json" { "name": "sensor", @@ -160,9 +160,9 @@ The following snippets describe the configuration file of an HWPC Sensor instanc } } ``` - + === "CSV Output" - + ```json hl_lines="5-8" title="config_file.json" { "name": "sensor", @@ -193,9 +193,8 @@ The following snippets describe the configuration file of an HWPC Sensor instanc } } ``` - === "Socket Output" - + ```json hl_lines="5-9" title="config_file.json" { "name": "sensor", @@ -228,11 +227,13 @@ The following snippets describe the configuration file of an HWPC Sensor instanc } ``` -The following CLI command shows how to use this configuration file in the deployment of an HWPC Sensor instance as a Docker container : + Please notice that you may need to adapt `core` values according to your processor architecture. + +The following CLI command shows how to use this configuration file (named `config_file.json`) in the deployment of an HWPC Sensor instance as a Docker container : === "Docker" - ```sh + ```sh docker run --rm \ --net=host \ --privileged \ @@ -247,12 +248,12 @@ The following CLI command shows how to use this configuration file in the deploy ### Running the Sensor via CLI parameters -The following CLI command shows how to launch an instance of HWPC Sensor with the same configuration as [above](hwpc-sensor.md#running-the-sensor-with-a-configuration-file), again two example are provided for both possible output: +The following CLI command shows how to launch an instance of HWPC Sensor with the same configuration as [above](hwpc-sensor.md#running-the-sensor-with-a-configuration-file). One example is provided for each possible output: !!! example "Examples using a CLI Parameters" - + === "CLI with MongoDB Output" - + ```sh docker run --rm \ --net=host \ @@ -269,9 +270,9 @@ The following CLI command shows how to launch an instance of HWPC Sensor with th -s "msr" -e "TSC" -e "APERF" -e "MPERF" \ -c "core" -e "CPU_CLK_THREAD_UNHALTED:REF_P" -e "CPU_CLK_THREAD_UNHALTED:THREAD_P" -e "LLC_MISSES" -e "INSTRUCTIONS_RETIRED" ``` - - === "Docker with CSV output" - + + === "CLI with CSV output" + ```sh docker run --rm \ --net=host \ @@ -289,5 +290,26 @@ The following CLI command shows how to launch an instance of HWPC Sensor with th -c "core" -e "CPU_CLK_THREAD_UNHALTED:REF_P" -e "CPU_CLK_THREAD_UNHALTED:THREAD_P" -e "LLC_MISSES" -e "INSTRUCTIONS_RETIRED" ``` + === "CLI with Socket output" + + ```sh + docker run --rm \ + --net=host \ + --privileged \ + --pid=host \ + -v /sys:/sys \ + -v /var/lib/docker/containers:/var/lib/docker/containers:ro \ + -v /tmp/powerapi-sensor-reporting:/reporting \ + -v $(pwd):/srv \ + powerapi/hwpc-sensor \ + -n "$(hostname -f)" \ + -r "socket "-U "127.0.0.1" -P 9876 \ + -s "rapl" -o -e "RAPL_ENERGY_PKG" \ + -s "msr" -e "TSC" -e "APERF" -e "MPERF" \ + -c "core" -e "CPU_CLK_THREAD_UNHALTED:REF_P" -e "CPU_CLK_THREAD_UNHALTED:THREAD_P" -e "LLC_MISSES" -e "INSTRUCTIONS_RETIRED" + ``` + + Please notice that you may need to adapt core values according to your processor architecture. + !!! tip "CLI parameters' names" You can only use shortcuts. From aed859212048cf799e7826b1a2090a17298e1028 Mon Sep 17 00:00:00 2001 From: daniel Date: Mon, 12 May 2025 14:24:23 +0200 Subject: [PATCH 091/108] docs(hwpc-sensor): Update support information for different ZEN architectures --- docs/reference/sensors/hwpc-sensor.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/reference/sensors/hwpc-sensor.md b/docs/reference/sensors/hwpc-sensor.md index 5c99b84..73892d3 100644 --- a/docs/reference/sensors/hwpc-sensor.md +++ b/docs/reference/sensors/hwpc-sensor.md @@ -29,7 +29,8 @@ most common architectures: In addition of a supported architecture, there is some pre-requisites: - Using a Linux distribution exposing the [`perf`](https://perf.wiki.kernel.org/index.php/Main_Page) api - - Using Cgroup version 1 when using version 1.2 or older. See [this section](../cgroup/cgroup_v1_activation.md) about its configuration + - Using cgroup version 1 when using version `1.2` or older. See [this section](../cgroup/cgroup_v1_activation.md) about its configuration + - Using an AMD Zen 1 processor requires version `1.4` or older. - Deploying on a physical server as the HWPC Sensor must have access to the real CPU register @@ -82,12 +83,12 @@ The following tabs gives a complete overview of available parameters, along with | Architectures | Group | Events | | ------------- | ----- | ------------- | - |Intel Sandy Bridge and newer, AMD Zen 2 | `rapl` | `RAPL_ENERGY_PKG`, `RAPL_ENERGY_DRAM`| - |Intel Sandy Bridge and newer, AMD Zen 2 | `msr` | `TSC`, `APERF`, `MPERF`| + |Intel Sandy Bridge and newer, AMD Zen 2, 3, 4, 5 | `rapl` | `RAPL_ENERGY_PKG`, `RAPL_ENERGY_DRAM`| + |Intel Sandy Bridge and newer, AMD Zen 2, 3, 4, 5 | `msr` | `TSC`, `APERF`, `MPERF`| |Intel Skylake, Whiskey Lake, Coffee Lake| `core` | `CPU_CLK_THREAD_UNHALTED:REF_P`, `CPU_CLK_THREAD_UNHALTED:THREAD_P`, `LLC_MISSES`,`INSTRUCTIONS_RETIRED`| |Intel Sandy Bridge, Comet Lake | `core` | `CPU_CLK_UNHALTED:REF_P`, `CPU_CLK_UNHALTED:THREAD_P`, `LLC_MISSES`,`INSTRUCTIONS_RETIRED`| |AMD Zen 2 | `core`| `CYCLES_NOT_IN_HALT`, `RETIRED_INSTRUCTIONS` , `RETIRED_UOPS`| - |AMD Zen 3 | `core`| `CYCLES_NOT_IN_HALT`, `RETIRED_INSTRUCTIONS` , `RETIRED_OPS`| + |AMD Zen 3, 4, 5 | `core`| `CYCLES_NOT_IN_HALT`, `RETIRED_INSTRUCTIONS` , `RETIRED_OPS`| ### Output From a38e6b0d89552bd07e08e3cafd343da4f3d7111f Mon Sep 17 00:00:00 2001 From: daniel Date: Mon, 12 May 2025 14:55:39 +0200 Subject: [PATCH 092/108] style(getting-started): Change some sentences, Format commands --- docs/getting_started.md | 49 +++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/docs/getting_started.md b/docs/getting_started.md index ee63fd1..0c9815f 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -2,52 +2,53 @@ In this tutorial, we will guide you through the first steps to get started with PowerAPI. The objective is to get a quick view of the capabilities of PowerAPI. -A few things are required before we start: +A few things are required before we start: -- A compatible processor (you can see the compatible CPU architecture [here](./reference/sensors/hwpc-sensor.md#)), and you can look on the following pages to find your CPU architecture: - * For [Intel Processor](https://en.wikipedia.org/wiki/List_of_Intel_processors) - * For [Intel Xeon Processor](https://en.wikipedia.org/wiki/List_of_Intel_Xeon_processors) - * For [AMD Processor](https://en.wikipedia.org/wiki/Table_of_AMD_processors) -- Docker & Docker-Compose ready (refer to [this official documentation](https://docs.docker.com/engine/install/) and the [post-install steps](https://docs.docker.com/engine/install/linux-postinstall/) if needed !) -- Root access +- A compatible processor (you can see the compatible CPU architecture [here](./reference/sensors/hwpc-sensor.md#)). You can take a look on the following links to find your CPU architecture: + * For [Intel Processor](https://en.wikipedia.org/wiki/List_of_Intel_processors). + * For [Intel Xeon Processor](https://en.wikipedia.org/wiki/List_of_Intel_Xeon_processors). + * For [AMD Processor](https://en.wikipedia.org/wiki/Table_of_AMD_processors). +- You can also use the `lscpu` command to get information about your processor. +- Docker & Docker-Compose ready (refer to [this official documentation](https://docs.docker.com/engine/install/) and the [post-install steps](https://docs.docker.com/engine/install/linux-postinstall/) if needed!). +- Root access. ## Which components to get a complete stack -The complete stack of PowerAPI is composed of : +The complete stack of PowerAPI is composed of: -- The Sensor and the Formula, these are the two main parts of PowerAPI, the Sensor retrieve power consumption related metrics and the Formula compute an estimation of the power consumption. +- A Sensor and a Formula that enable us to define a Software Power Meter. The Sensor retrieves power consumption related metrics and the Formula compute an estimation of the power consumption. Currently, PowerAPI provides [HWPC-Sensor](./reference/sensors/hwpc-sensor.md) and [SmartWatts Formula](./reference/formulas/smartwatts.md) -- The Sensor and the Formula both need an *output*, the supported *output* are listed [here](./reference/database/sources_destinations.md). The formula will also use the Sensor *output* as his *input* +- The Sensor and the Formula need an *output*. The supported *output* are listed [here](./reference/database/inputs_outputs.md). The Formula will also use the Sensor *output* as its *input*. -- Finally, they will both need configuration files, as described in the [HWPC-Sensor Documentation](./reference/sensors/hwpc-sensor.md#global-parameters) and in the [SmartWatts Documentation](./reference/formulas/smartwatts.md#global-parameters), several parameters can be set, both globally and for specific Groups monitored. These parameters can also be set using CLI parameters. +- Finally, they need a configuration as described in the [HWPC-Sensor](./reference/sensors/hwpc-sensor.md#global-parameters) and [SmartWatts](./reference/formulas/smartwatts.md#global-parameters) documentation. The configuration parameters can be defined via a configuration file or via the CLI. To learn more see the [overview section](./reference/overview.md). -## Test powerAPI +## Executing a Software Power Meter -If you wish to get started as soon as possible, using the following cURL link below will allow you to setup everything quickly using Docker. +The following command allow you to setup and run a software power meter by using HWPC-Sensor and SmartWatts Formula via Docker. +```sh curl -sSL https://raw.githubusercontent.com/Inkedstinct/powerapi-ng.github.io/refs/heads/7_doc/nld_proofread/docs/script/getting_started/curl_version/start.sh | bash +``` -This curl link will run [this script](./script/getting_started/curl_version/start.sh). It will detect the CPU used, and download the appropriate Docker compose file and environnement file for your configuration. +This command will run [this script](./script/getting_started/curl_version/start.sh). It will detect the CPU used, and download the appropriate Docker compose file and environment file for your configuration. -It will then execute the Docker compose file, deploying the following elements for 3 minutes: +It will then execute the Docker compose file, deploying the following elements for 3 minutes: 1. A MongoDB instance to store the [Sensor](./reference/sensors/hwpc-sensor.md) reports. -2. An [HWPC-Sensor](./reference/sensors/hwpc-sensor.md) that outputs its -[HWPC Reports](./reference/reports/reports.md#hwpc-reports) in a MongoDB Database, +2. An [HWPC-Sensor](./reference/sensors/hwpc-sensor.md) that outputs its +[HWPC Reports](./reference/reports/reports.md#hwpc-reports) in a MongoDB Database, within the HWPC Report Collection -3. A [SmartWatts](./reference/formulas/smartwatts.md) that streams the -[HWPC Reports](./reference/reports/reports.md#hwpc-reports) from the MongoDB -Database Collection, processes it and outputs its +3. A [SmartWatts](./reference/formulas/smartwatts.md) that streams the +[HWPC Reports](./reference/reports/reports.md#hwpc-reports) from the MongoDB +Database Collection, processes it and outputs its [Power Reports](./reference/reports/reports.md#power-reports) into CSV. -In this specific case, we don't use configuration file for the Sensor or the Formula and directly use the CLI parameters in the Docker compose file. - -After he result will be visible under the *csv* directory. - +In this specific use case, we use the CLI to set the configuration parameters in the Docker compose file. +After the execution of result will be visible under the *csv* directory. From 2d7c244012c5fc2a55f5b452952cf72ce1392e84 Mon Sep 17 00:00:00 2001 From: daniel Date: Mon, 12 May 2025 15:05:19 +0200 Subject: [PATCH 093/108] style(getting-started): Use relative path for links without / --- docs/getting_started.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/getting_started.md b/docs/getting_started.md index 0c9815f..7031be0 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -4,7 +4,7 @@ In this tutorial, we will guide you through the first steps to get started with The objective is to get a quick view of the capabilities of PowerAPI. A few things are required before we start: -- A compatible processor (you can see the compatible CPU architecture [here](./reference/sensors/hwpc-sensor.md#)). You can take a look on the following links to find your CPU architecture: +- A compatible processor (you can see the compatible CPU architecture [here](reference/sensors/hwpc-sensor.md#)). You can take a look on the following links to find your CPU architecture: * For [Intel Processor](https://en.wikipedia.org/wiki/List_of_Intel_processors). * For [Intel Xeon Processor](https://en.wikipedia.org/wiki/List_of_Intel_Xeon_processors). * For [AMD Processor](https://en.wikipedia.org/wiki/Table_of_AMD_processors). @@ -17,11 +17,11 @@ A few things are required before we start: The complete stack of PowerAPI is composed of: -- A Sensor and a Formula that enable us to define a Software Power Meter. The Sensor retrieves power consumption related metrics and the Formula compute an estimation of the power consumption. Currently, PowerAPI provides [HWPC-Sensor](./reference/sensors/hwpc-sensor.md) and [SmartWatts Formula](./reference/formulas/smartwatts.md) +- A Sensor and a Formula that enable us to define a Software Power Meter. The Sensor retrieves power consumption related metrics and the Formula compute an estimation of the power consumption. Currently, PowerAPI provides [HWPC-Sensor](reference/sensors/hwpc-sensor.md) and [SmartWatts Formula](reference/formulas/smartwatts.md) -- The Sensor and the Formula need an *output*. The supported *output* are listed [here](./reference/database/inputs_outputs.md). The Formula will also use the Sensor *output* as its *input*. +- The Sensor and the Formula need an *output*. The supported *output* are listed [here](reference/database/inputs_outputs.md). The Formula will also use the Sensor *output* as its *input*. -- Finally, they need a configuration as described in the [HWPC-Sensor](./reference/sensors/hwpc-sensor.md#global-parameters) and [SmartWatts](./reference/formulas/smartwatts.md#global-parameters) documentation. The configuration parameters can be defined via a configuration file or via the CLI. +- Finally, they need a configuration as described in the [HWPC-Sensor](./reference/sensors/hwpc-sensor.md#global-parameters) and [SmartWatts](reference/formulas/smartwatts.md#global-parameters) documentation. The configuration parameters can be defined via a configuration file or via the CLI. To learn more see the [overview section](./reference/overview.md). @@ -34,20 +34,20 @@ The following command allow you to setup and run a software power meter by using curl -sSL https://raw.githubusercontent.com/Inkedstinct/powerapi-ng.github.io/refs/heads/7_doc/nld_proofread/docs/script/getting_started/curl_version/start.sh | bash ``` -This command will run [this script](./script/getting_started/curl_version/start.sh). It will detect the CPU used, and download the appropriate Docker compose file and environment file for your configuration. +This command will run [this script](script/getting_started/curl_version/start.sh). It will detect the CPU used, and download the appropriate Docker compose file and environment file for your configuration. It will then execute the Docker compose file, deploying the following elements for 3 minutes: -1. A MongoDB instance to store the [Sensor](./reference/sensors/hwpc-sensor.md) reports. +1. A MongoDB instance to store the [Sensor](reference/sensors/hwpc-sensor.md) reports. -2. An [HWPC-Sensor](./reference/sensors/hwpc-sensor.md) that outputs its -[HWPC Reports](./reference/reports/reports.md#hwpc-reports) in a MongoDB Database, +2. An [HWPC-Sensor](reference/sensors/hwpc-sensor.md) that outputs its +[HWPC Reports](reference/reports/reports.md#hwpc-reports) in a MongoDB Database, within the HWPC Report Collection -3. A [SmartWatts](./reference/formulas/smartwatts.md) that streams the -[HWPC Reports](./reference/reports/reports.md#hwpc-reports) from the MongoDB +3. A [SmartWatts](reference/formulas/smartwatts.md) that streams the +[HWPC Reports](reference/reports/reports.md#hwpc-reports) from the MongoDB Database Collection, processes it and outputs its -[Power Reports](./reference/reports/reports.md#power-reports) into CSV. +[Power Reports](reference/reports/reports.md#power-reports) into CSV. In this specific use case, we use the CLI to set the configuration parameters in the Docker compose file. From 3f257573e4120464c120c9967d8ce93b12a2bd18 Mon Sep 17 00:00:00 2001 From: daniel Date: Mon, 12 May 2025 15:53:56 +0200 Subject: [PATCH 094/108] refactor(getting-started): Remove useless section related to databases --- docs/getting_started.md | 4 +- .../database/sources_destinations.md | 212 ------------------ 2 files changed, 2 insertions(+), 214 deletions(-) delete mode 100644 docs/reference/database/sources_destinations.md diff --git a/docs/getting_started.md b/docs/getting_started.md index 7031be0..2a3de1d 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -19,9 +19,9 @@ The complete stack of PowerAPI is composed of: - A Sensor and a Formula that enable us to define a Software Power Meter. The Sensor retrieves power consumption related metrics and the Formula compute an estimation of the power consumption. Currently, PowerAPI provides [HWPC-Sensor](reference/sensors/hwpc-sensor.md) and [SmartWatts Formula](reference/formulas/smartwatts.md) -- The Sensor and the Formula need an *output*. The supported *output* are listed [here](reference/database/inputs_outputs.md). The Formula will also use the Sensor *output* as its *input*. +- The Sensor and the Formula need an *output*. The supported *output* are listed [here](reference/sensors/hwpc-sensor.md/#output). The Formula will also use the Sensor *output* as its *input*. -- Finally, they need a configuration as described in the [HWPC-Sensor](./reference/sensors/hwpc-sensor.md#global-parameters) and [SmartWatts](reference/formulas/smartwatts.md#global-parameters) documentation. The configuration parameters can be defined via a configuration file or via the CLI. +- Finally, they need a configuration as described in the [HWPC-Sensor](reference/sensors/hwpc-sensor.md#global-parameters) and [SmartWatts](reference/formulas/smartwatts.md#global-parameters) documentation. The configuration parameters can be defined via a configuration file or via the CLI. To learn more see the [overview section](./reference/overview.md). diff --git a/docs/reference/database/sources_destinations.md b/docs/reference/database/sources_destinations.md deleted file mode 100644 index a8f482a..0000000 --- a/docs/reference/database/sources_destinations.md +++ /dev/null @@ -1,212 +0,0 @@ -# Storage Options - -Different storage options are available to serve different purpose both for [Sensors](../overview.md#Sensor) and [Formulas](../overview.md#Formula). - -Storage is needed to save reports produced by each components. -- Sensors store their usage reports -- Formulas retrieve usage reports and store energy consumption reports -- Visualization tools or individuals need to access reports for analysis - -## Summary - -The following table defines the existing storage options for Sensors usage reports : - -| Name | CLI `ouput` parameter value | JSON `type` tag parameter value| -| ------------ | --------------------------------------| -------------------------------------------| -| MongoDB | mongodb | mongodb | -| CSV | csv | csv | -| Socket | socket | socket | -| File Database | filedb | filedb | - - -## MongoDB - -If you want to use a Mongo Database in your Formula, you have to specify -`mongodb` as the `type` of a puller (Source) or a pusher (Destination). - -### Parameters - -The list of accepted parameters are: - -| Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | -| ------------- | ----- | ------------- | ------------- | ---------- | ------------------------------------ | -|`uri` | string | `u` (`U` for `HWPCSensor`) | N/A | Yes | The IP address of your MongoDB instance | -|`db` (`database` for `HWPCSensor`) | string | `d` (`D` for `HWPCSensor`) | N/A | Yes | The name of your database | -|`collection` | string | `c` (`C` for `HWPCSensor`) | N/A | Yes | The name of the collection inside `db` | -|`name` | string | `n` | `"puller_mongodb"` (Source), `pusher_mongodb` (Destination)| No | The related puller/pusher name. This parameter is not used by `HWPCSensor` | -|`model` | string | `m` | `"HWPC Report"` (Source), `Power Report` (Destination) | No | The Report type stored by the database | - -### JSON File Excerpt - -Below you find a configuration excerpt for this kind of Source/Destination. - -```json -{ - "type": "mongodb", - "uri": "mongodb://127.0.0.1", - "db": "test", - "collection": "prep" -} -``` -!!! info "The default port for MongoDB is 27017" - -## Influx DB 2 - -If you want to use InfluxDB 2 in your Formula as Destination, you have to specify -`influxedb2` as the `type` of a pusher. - -### Parameters - -The list of accepted parameters are: - -| Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | -| ------------- | ----- | ------------- | ------------- | ---------- | ------------------------------------ | -|`uri` | string | `u` | N/A | Yes | The IP address of your Influxdb instance. It can contain the port number| -|`db` | string | `d` | N/A | Yes | The name of your bucket (database) | -|`port` | int | `p` | None | N/A| The port of communication. It is not mandatory if it is indicated in the `uri` | -|`token` | string | `k` | N/A | Yes | The token for accessing the database. The token owner must have write/read permissions on the bucket | -|`org` | string | `g` | N/A | Yes | The name of the organization associated to the bucket | -|`tags` | string | `t` | N/A | No | List of metadata keys of the report separated by `,` that will be kept. `sensor` and `target` are always kept as report metadata | -|`name` | string | `n` | `"pusher_influxdb2"` | No | The related pusher name | -|`model` | string | `m` | `"Power Report"` | No | The Report type stored by the database | - - -InfluxDB2 can only be used as a Destination. - -### JSON File Excerpt - -Below you find an example of configuration excerpt for this kind of Destination. - -```json -{ - "model": "Power Report", - "type": "influxdb2", - "uri": "http://127.0.0.1", - "port": 8086, - "db": "influxdb2", - "org": "org_test", - "token": "mytoken" -} -``` - - -## CSV - -If you want to use a CSV file in your Formula as Source or Destination, you have to specify -`csv` as the `type` of a puller or a pusher. - -### Parameters - -The list of accepted parameters are: - -| Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | -| ------------- | ----- | ------------- | ------------- | ----------| ------------------------------------ | -|`files`(Source)| string | `f` | Empty list | No | The list of input CSV files with the format file1,file2,file3... | -|`directory` (Destination and `HWPCSensor`)| string |`d` (`U` for `HWPCSensor`) | Current directory | No |The directory where output CSV files will be written | -|`name` | string | `n` | `"puller_csv"` (Source), `"pusher_csv"` (Destination)| No | The related puller/pusher name. This parameter is not used by `HWPCSensor` | -|`model` | string | `m` | `"HWPC Report"` (Source), `"Power Report"` (Destination) | No | The Report type stored in CSV files. This parameter is not used by `HWPCSensor` | - -### JSON File Excerpt - -Below you find an example of configuration excerpt for this kind of Source/Destination. - -```json -{ - "type": "csv", - "directory": "/tmp/sensor_output/" -} -``` - -## Socket - -If you want to use a TCP socket in your Formula as Source, you have to specify -`socket` as the `type` of a puller. -This Source is made for `stream` mode active only. - -### Parameters - -The list of accepted parameters are: - -| Parameter | Type | CLI shortcut | Default Value| Mandatory | Description | -| ------------- | ----- | ------------- | -------------| ---------- | ------------------------------------ | -|`port` | int | `P` | N/A | Yes | The port of communication | -|`uri`/ `host` | int | `U` | N/A | Yes | The IP address of the machine running the socket | -|`name` | string | `n` | `"puller_socket"`| No | The related puller name | -|`model` | string | `m` | `"HWPC Report"` | No | The Report type managed by the socket | - - -### JSON File Excerpt - -Below you find an example of configuration excerpt for this kind of Source. - -```json -{ - "type": "socket", - "port": 8080, - "host": "127.0.0.1" -} -``` - -## File Database - -If you want to use a File Database as Source/Destination in your Formula your have to specify -`filedb` as the `type` of a puller or a pusher. -The File Database is made for stream mode only. It should contain only the last -report when used as a Destination. - -### Parameters - -The list of accepted parameters are: - -| Parameter | Type | CLI shortcut | Default Value| Mandatory | Description | -| ------------- | ----- | ------------- | -------------| ---------- | ------------------------------------ | -|`filename` | int | `f` | N/A | Yes | The name of the file | -|`name` | string | `n` | `"pusher_filedb"` | No | The related pusher name | -|`model` | string | `m` | `"HWPC Report"` (Source) `"Power Report"` (Destination)| No | The Report type stored in the file | - -### JSON File Excerpt - -Below you find an example of configuration excerpt for this kind of Source/Destination. - -```json -{ - "type": "filedb", - "filename": /tmp/database/input_file.json -} -``` - -## Prometheus - -If you want to use a Prometheus instance to expose reports to be scraped, you have to specify -`prometheus` as the `type` of a pusher in your formula configuration file. - -### Parameters - -The list of accepted parameters are: - -| Parameter | Type | CLI shortcut | Default Value | Mandatory | Description | -| ------------- | ----- | ------------- | ------------- | ---------- | ------------------------------------ | -|`uri` | string | `u` | `127.0.0.1` | No | The IP address of your Prometheus instance | -|`port` | int | `p` | N/A | Yes | The port of communication | -|`tags` | string | `t` | N/A | No | List of metadata keys of the report separated by `,` that will be kept. `sensor` and `target` are always kept as report metadata | -|`metric-name` | string | `M` | N/A | Yes | The exposed metric name | -|`metric-description` | string | `d` | `"energy consumption"` | No | The exposed metric description | -|`name` | string | `n` | `"pusher_prom"` | No | The related pusher name | -|`model` | string | `m` | `"Power Report"` | No | The Report type exposed by Prometheus | - - -Prometheus can only be used as a Destination that monitors reports but they will be not stored by this service. -The tags names are metadata keys of reports to be used as labels. If a report doesn't have a provide tag, it will be ignored by the Destination. - -### JSON File Excerpt - -Below you find an example of configuration excerpt for this kind of Destination. - -```json -{ - "type": "prometheus", - "uri": "127.0.0.1", - "port": 8080, - "metric-name": "test" -} -``` From a3742f6cda3613a5ea74d2ba5f08f6913097db3b Mon Sep 17 00:00:00 2001 From: daniel Date: Mon, 12 May 2025 15:56:13 +0200 Subject: [PATCH 095/108] fix(getting-started): Typos --- docs/getting_started.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/getting_started.md b/docs/getting_started.md index 2a3de1d..060088c 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -26,9 +26,9 @@ The complete stack of PowerAPI is composed of: To learn more see the [overview section](./reference/overview.md). -## Executing a Software Power Meter +## Executing a Software PowerMeter -The following command allow you to setup and run a software power meter by using HWPC-Sensor and SmartWatts Formula via Docker. +The following command allow you to setup and run a software PowerMeter by using HWPC-Sensor and SmartWatts Formula via Docker. ```sh curl -sSL https://raw.githubusercontent.com/Inkedstinct/powerapi-ng.github.io/refs/heads/7_doc/nld_proofread/docs/script/getting_started/curl_version/start.sh | bash From ec19bda5d77b384cb1e02d7b692c8bc749460371 Mon Sep 17 00:00:00 2001 From: daniel Date: Tue, 13 May 2025 10:13:58 +0200 Subject: [PATCH 096/108] style(overview): Change some phrases, Update global atchitecture image --- .../overview/global-architecture.jpg | Bin 1165642 -> 1059090 bytes docs/reference/overview.md | 23 ++++++++---------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/docs/assets/images/reference/overview/global-architecture.jpg b/docs/assets/images/reference/overview/global-architecture.jpg index e00b87ba6b430527691b570b6d6b005c83d9cbde..40ac2e5e72c624a26c17fbc132c332d6d8f826ee 100644 GIT binary patch literal 1059090 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 z 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 z Kx|q9Li9DRKXic1QcvPyM*J+1TdOQUJu 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