# Performance Measurements in Linux with `perf_events` Time-based metrics for performance measurement are often inaccurate for extremely fast operations. Hardware counters provide a more precise alternative and can be accessed through the [`perf_events`](https://docs.kernel.org/admin-guide/perf-security.html) interface in the Linux kernel. This document explains how to use this interface, using a simple parsing task in µD3TN as an example. ## Header Definitions ```c #include #include #include #include #include #include static int perf_fd; long long count; long long total_count; static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid, int cpu, int group_fd, unsigned long flags) { return syscall(__NR_perf_event_open, hw_event, pid, cpu, group_fd, flags); } #define PERF(expr) do { \ ioctl(perf_fd, PERF_EVENT_IOC_RESET, 0); \ ioctl(perf_fd, PERF_EVENT_IOC_ENABLE, 0); \ expr; \ ioctl(perf_fd, PERF_EVENT_IOC_DISABLE, 0); \ count = 0; \ read(perf_fd, &count, sizeof(long long)); \ total_count += count; \ } while (0) ``` You have to initialize `perf_events` in your task initializer function: ```c void my_task(void * const param) { // ... // ------------------- // Parsing Performance // ------------------- struct perf_event_attr pe; long long count; memset(&pe, 0, sizeof(struct perf_event_attr)); pe.type = PERF_TYPE_HARDWARE; pe.size = sizeof(struct perf_event_attr); pe.config = PERF_COUNT_HW_CPU_CYCLES; pe.disabled = 1; pe.exclude_kernel = 1; pe.exclude_hv = 1; perf_fd = perf_event_open(&pe, 0, -1, -1, 0); if (perf_fd == -1) { fprintf(stderr, "Error opening leader %llx\n", pe.config); exit(EXIT_FAILURE); } // ----------------------- // End Parsing Performance // ----------------------- // Do your heavy lifiting ... } ``` Now you can count hardware cycles for specific parts of your code with the `PERF` helper: ```c static void forward_to_specific_parser(uint8_t byte) { if (input_parser.stage != INPUT_EXPECT_DATA) { /* Current parser is input_parser */ input_parser_read_byte(&input_parser, byte); } else { switch (input_parser.type) { case INPUT_TYPE_ROUTER_COMMAND_DATA: cur_parser = router_parser.basedata; router_parser_read_byte(&router_parser, byte); break; case INPUT_TYPE_BUNDLE_VERSION: PERF(select_bundle_parser_version(byte)); break; case INPUT_TYPE_BUNDLE_V6: cur_parser = bundle6_parser.basedata; PERF(bundle6_parser_read_byte(&bundle6_parser, byte)); break; case INPUT_TYPE_BUNDLE_V7: cur_parser = bundle7_parser.basedata; PERF(bundle7_parser_read_byte(&bundle7_parser, byte)); break; default: reset_parser(); return; } } } static void reset_parser(void) { // ... // Read and reset performance counter printf("COUNT_HW_CPU_CYCLES: %lld", count); ioctl(perf_fd, PERF_EVENT_IOC_RESET, 0); total_count = 0; } ```