[PATCH] qemu: Optimize monitor event name lookup with hash tables

Rayhan Faizel posted 1 patch 6 months ago
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/libvirt tags/patchew/20240521120648.610648-1-rayhan.faizel@gmail.com
docs/kbase/internals/qemu-event-handlers.rst |  13 +-
meson.build                                  |   2 +
src/qemu/meson.build                         |   9 +
src/qemu/qemu_monitor_event.gperf            |  68 ++++++++
src/qemu/qemu_monitor_json.c                 | 171 +++++--------------
src/qemu/qemu_monitor_json.h                 |  45 +++++
6 files changed, 170 insertions(+), 138 deletions(-)
create mode 100644 src/qemu/qemu_monitor_event.gperf
[PATCH] qemu: Optimize monitor event name lookup with hash tables
Posted by Rayhan Faizel 6 months ago
Currently, monitor event names are looked up using binary search which has
O(log(n)) time complexity. This can be optimized even further with a
compile-time static hash table generated by the gperf tool. As gperf ensures
perfect hashing, lookup times are guaranteed to be O(1).

This patch also makes gperf a requirement for compiling libvirt if the QEMU
driver is enabled.

Signed-off-by: Rayhan Faizel <rayhan.faizel@gmail.com>
---
 docs/kbase/internals/qemu-event-handlers.rst |  13 +-
 meson.build                                  |   2 +
 src/qemu/meson.build                         |   9 +
 src/qemu/qemu_monitor_event.gperf            |  68 ++++++++
 src/qemu/qemu_monitor_json.c                 | 171 +++++--------------
 src/qemu/qemu_monitor_json.h                 |  45 +++++
 6 files changed, 170 insertions(+), 138 deletions(-)
 create mode 100644 src/qemu/qemu_monitor_event.gperf

diff --git a/docs/kbase/internals/qemu-event-handlers.rst b/docs/kbase/internals/qemu-event-handlers.rst
index 3589c4c48c..5ec11e28bd 100644
--- a/docs/kbase/internals/qemu-event-handlers.rst
+++ b/docs/kbase/internals/qemu-event-handlers.rst
@@ -23,16 +23,15 @@ QEMU monitor events
 
 Any event emitted by qemu is received by
 ``qemu_monitor_json.c:qemuMonitorJSONIOProcessEvent()``. It looks up the
-event by name in the table ``eventHandlers`` (in the same file), which
-should have an entry like this for each event that libvirt
-understands::
+event by name from a hash-table, generated at compile-time by ``gperf``
+based on a list of entries defined in ``qemu_monitor_event.gperf``.
+``qemu_monitor_event.gperf`` should should have an entry like this
+for each event that libvirt understands::
 
-    { "NIC_RX_FILTER_CHANGED", qemuMonitorJSONHandleNicRxFilterChanged, },
-
-NB: This table is searched with bsearch, so it *must* be alphabetically sorted.
+    "NIC_RX_FILTER_CHANGED", qemuMonitorJSONHandleNicRxFilterChanged
 
 ``qemuMonitorJSONIOProcessEvent`` calls the function listed in
-``eventHandlers``, e.g.::
+``qemu_monitor_event.gperf``, e.g.::
 
    qemu_monitor_json.c:qemuMonitorJSONHandleNicRxFilterChanged()
 
diff --git a/meson.build b/meson.build
index e8b0094b91..03f1f0d56d 100644
--- a/meson.build
+++ b/meson.build
@@ -1708,6 +1708,8 @@ if not get_option('driver_qemu').disabled()
       qemu_slirp_path = '/usr/bin/slirp-helper'
     endif
     conf.set_quoted('QEMU_SLIRP_HELPER', qemu_slirp_path)
+
+    gperf_prog = find_program('gperf')
   endif
 endif
 
diff --git a/src/qemu/meson.build b/src/qemu/meson.build
index 907893d431..2440476a7b 100644
--- a/src/qemu/meson.build
+++ b/src/qemu/meson.build
@@ -88,11 +88,20 @@ qemu_shim_sources = files(
   'qemu_shim.c',
 )
 
+qemu_driver_gperf_sources = []
+
+qemu_driver_gperf_sources += custom_target(
+  'qemu_monitor_event.c',
+  input : 'qemu_monitor_event.gperf',
+  output : 'qemu_monitor_event.c',
+  command : [gperf_prog, '@INPUT@', '--output-file', '@OUTPUT@'])
+
 if conf.has('WITH_QEMU')
   qemu_driver_impl = static_library(
     'virt_driver_qemu_impl',
     [
       qemu_driver_sources,
+      qemu_driver_gperf_sources,
       qemu_dtrace_gen_headers,
     ],
     dependencies: [
diff --git a/src/qemu/qemu_monitor_event.gperf b/src/qemu/qemu_monitor_event.gperf
new file mode 100644
index 0000000000..de2f958b9e
--- /dev/null
+++ b/src/qemu/qemu_monitor_event.gperf
@@ -0,0 +1,68 @@
+/*
+ * qemu_monitor_event.gperf: QEMU Monitor Event Lookup Table
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+%{
+#include "qemu_monitor_json.h"
+%}
+qemuEventHandler;
+%null_strings
+%define initializer-suffix ,NULL
+%language=ANSI-C
+%define slot-name type
+%define lookup-function-name qemu_monitor_event_lookup
+%define hash-function-name qemu_monitor_event_hash
+%readonly-tables
+%omit-struct-type
+%struct-type
+%%
+"ACPI_DEVICE_OST", qemuMonitorJSONHandleAcpiOstInfo
+"BALLOON_CHANGE", qemuMonitorJSONHandleBalloonChange
+"BLOCK_IO_ERROR", qemuMonitorJSONHandleIOError
+"BLOCK_WRITE_THRESHOLD", qemuMonitorJSONHandleBlockThreshold
+"DEVICE_DELETED", qemuMonitorJSONHandleDeviceDeleted
+"DEVICE_TRAY_MOVED", qemuMonitorJSONHandleTrayChange
+"DEVICE_UNPLUG_GUEST_ERROR", qemuMonitorJSONHandleDeviceUnplugErr
+"DUMP_COMPLETED", qemuMonitorJSONHandleDumpCompleted
+"GUEST_CRASHLOADED", qemuMonitorJSONHandleGuestCrashloaded
+"GUEST_PANICKED", qemuMonitorJSONHandleGuestPanic
+"JOB_STATUS_CHANGE", qemuMonitorJSONHandleJobStatusChange
+"MEMORY_DEVICE_SIZE_CHANGE", qemuMonitorJSONHandleMemoryDeviceSizeChange
+"MEMORY_FAILURE", qemuMonitorJSONHandleMemoryFailure
+"MIGRATION", qemuMonitorJSONHandleMigrationStatus
+"MIGRATION_PASS", qemuMonitorJSONHandleMigrationPass
+"NETDEV_STREAM_DISCONNECTED", qemuMonitorJSONHandleNetdevStreamDisconnected
+"NIC_RX_FILTER_CHANGED", qemuMonitorJSONHandleNicRxFilterChanged
+"PR_MANAGER_STATUS_CHANGED", qemuMonitorJSONHandlePRManagerStatusChanged
+"RDMA_GID_STATUS_CHANGED", qemuMonitorJSONHandleRdmaGidStatusChanged
+"RESET", qemuMonitorJSONHandleReset
+"RESUME", qemuMonitorJSONHandleResume
+"RTC_CHANGE", qemuMonitorJSONHandleRTCChange
+"SHUTDOWN", qemuMonitorJSONHandleShutdown
+"SPICE_CONNECTED", qemuMonitorJSONHandleSPICEConnect
+"SPICE_DISCONNECTED", qemuMonitorJSONHandleSPICEDisconnect
+"SPICE_INITIALIZED", qemuMonitorJSONHandleSPICEInitialize
+"SPICE_MIGRATE_COMPLETED", qemuMonitorJSONHandleSpiceMigrated
+"STOP", qemuMonitorJSONHandleStop
+"SUSPEND", qemuMonitorJSONHandlePMSuspend
+"SUSPEND_DISK", qemuMonitorJSONHandlePMSuspendDisk
+"VNC_CONNECTED", qemuMonitorJSONHandleVNCConnect
+"VNC_DISCONNECTED", qemuMonitorJSONHandleVNCDisconnect
+"VNC_INITIALIZED", qemuMonitorJSONHandleVNCInitialize
+"VSERPORT_CHANGE", qemuMonitorJSONHandleSerialChange
+"WAKEUP", qemuMonitorJSONHandlePMWakeup
+"WATCHDOG", qemuMonitorJSONHandleWatchdog
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
index eb84a3d938..93df4490ad 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -49,102 +49,12 @@ VIR_LOG_INIT("qemu.qemu_monitor_json");
 
 #define LINE_ENDING "\r\n"
 
-static void qemuMonitorJSONHandleShutdown(qemuMonitor *mon, virJSONValue *data);
-static void qemuMonitorJSONHandleReset(qemuMonitor *mon, virJSONValue *data);
-static void qemuMonitorJSONHandleStop(qemuMonitor *mon, virJSONValue *data);
-static void qemuMonitorJSONHandleResume(qemuMonitor *mon, virJSONValue *data);
-static void qemuMonitorJSONHandleRTCChange(qemuMonitor *mon, virJSONValue *data);
-static void qemuMonitorJSONHandleWatchdog(qemuMonitor *mon, virJSONValue *data);
-static void qemuMonitorJSONHandleIOError(qemuMonitor *mon, virJSONValue *data);
-static void qemuMonitorJSONHandleVNCConnect(qemuMonitor *mon, virJSONValue *data);
-static void qemuMonitorJSONHandleVNCInitialize(qemuMonitor *mon, virJSONValue *data);
-static void qemuMonitorJSONHandleVNCDisconnect(qemuMonitor *mon, virJSONValue *data);
-static void qemuMonitorJSONHandleSPICEConnect(qemuMonitor *mon, virJSONValue *data);
-static void qemuMonitorJSONHandleSPICEInitialize(qemuMonitor *mon, virJSONValue *data);
-static void qemuMonitorJSONHandleSPICEDisconnect(qemuMonitor *mon, virJSONValue *data);
-static void qemuMonitorJSONHandleTrayChange(qemuMonitor *mon, virJSONValue *data);
-static void qemuMonitorJSONHandlePMWakeup(qemuMonitor *mon, virJSONValue *data);
-static void qemuMonitorJSONHandlePMSuspend(qemuMonitor *mon, virJSONValue *data);
-static void qemuMonitorJSONHandleJobStatusChange(qemuMonitor *mon, virJSONValue *data);
-static void qemuMonitorJSONHandleBalloonChange(qemuMonitor *mon, virJSONValue *data);
-static void qemuMonitorJSONHandlePMSuspendDisk(qemuMonitor *mon, virJSONValue *data);
-static void qemuMonitorJSONHandleGuestCrashloaded(qemuMonitor *mon, virJSONValue *data);
-static void qemuMonitorJSONHandleGuestPanic(qemuMonitor *mon, virJSONValue *data);
-static void qemuMonitorJSONHandleDeviceDeleted(qemuMonitor *mon, virJSONValue *data);
-static void qemuMonitorJSONHandleNicRxFilterChanged(qemuMonitor *mon, virJSONValue *data);
-static void qemuMonitorJSONHandleSerialChange(qemuMonitor *mon, virJSONValue *data);
-static void qemuMonitorJSONHandleSpiceMigrated(qemuMonitor *mon, virJSONValue *data);
-static void qemuMonitorJSONHandleMigrationStatus(qemuMonitor *mon, virJSONValue *data);
-static void qemuMonitorJSONHandleMigrationPass(qemuMonitor *mon, virJSONValue *data);
-static void qemuMonitorJSONHandleAcpiOstInfo(qemuMonitor *mon, virJSONValue *data);
-static void qemuMonitorJSONHandleBlockThreshold(qemuMonitor *mon, virJSONValue *data);
-static void qemuMonitorJSONHandleDumpCompleted(qemuMonitor *mon, virJSONValue *data);
-static void qemuMonitorJSONHandlePRManagerStatusChanged(qemuMonitor *mon, virJSONValue *data);
-static void qemuMonitorJSONHandleRdmaGidStatusChanged(qemuMonitor *mon, virJSONValue *data);
-static void qemuMonitorJSONHandleMemoryFailure(qemuMonitor *mon, virJSONValue *data);
-static void qemuMonitorJSONHandleMemoryDeviceSizeChange(qemuMonitor *mon, virJSONValue *data);
-static void qemuMonitorJSONHandleDeviceUnplugErr(qemuMonitor *mon, virJSONValue *data);
-static void qemuMonitorJSONHandleNetdevStreamDisconnected(qemuMonitor *mon, virJSONValue *data);
-
-typedef struct {
-    const char *type;
-    void (*handler)(qemuMonitor *mon, virJSONValue *data);
-} qemuEventHandler;
-
-static qemuEventHandler eventHandlers[] = {
-    { "ACPI_DEVICE_OST", qemuMonitorJSONHandleAcpiOstInfo, },
-    { "BALLOON_CHANGE", qemuMonitorJSONHandleBalloonChange, },
-    { "BLOCK_IO_ERROR", qemuMonitorJSONHandleIOError, },
-    { "BLOCK_WRITE_THRESHOLD", qemuMonitorJSONHandleBlockThreshold, },
-    { "DEVICE_DELETED", qemuMonitorJSONHandleDeviceDeleted, },
-    { "DEVICE_TRAY_MOVED", qemuMonitorJSONHandleTrayChange, },
-    { "DEVICE_UNPLUG_GUEST_ERROR", qemuMonitorJSONHandleDeviceUnplugErr, },
-    { "DUMP_COMPLETED", qemuMonitorJSONHandleDumpCompleted, },
-    { "GUEST_CRASHLOADED", qemuMonitorJSONHandleGuestCrashloaded, },
-    { "GUEST_PANICKED", qemuMonitorJSONHandleGuestPanic, },
-    { "JOB_STATUS_CHANGE", qemuMonitorJSONHandleJobStatusChange, },
-    { "MEMORY_DEVICE_SIZE_CHANGE", qemuMonitorJSONHandleMemoryDeviceSizeChange, },
-    { "MEMORY_FAILURE", qemuMonitorJSONHandleMemoryFailure, },
-    { "MIGRATION", qemuMonitorJSONHandleMigrationStatus, },
-    { "MIGRATION_PASS", qemuMonitorJSONHandleMigrationPass, },
-    { "NETDEV_STREAM_DISCONNECTED", qemuMonitorJSONHandleNetdevStreamDisconnected, },
-    { "NIC_RX_FILTER_CHANGED", qemuMonitorJSONHandleNicRxFilterChanged, },
-    { "PR_MANAGER_STATUS_CHANGED", qemuMonitorJSONHandlePRManagerStatusChanged, },
-    { "RDMA_GID_STATUS_CHANGED", qemuMonitorJSONHandleRdmaGidStatusChanged, },
-    { "RESET", qemuMonitorJSONHandleReset, },
-    { "RESUME", qemuMonitorJSONHandleResume, },
-    { "RTC_CHANGE", qemuMonitorJSONHandleRTCChange, },
-    { "SHUTDOWN", qemuMonitorJSONHandleShutdown, },
-    { "SPICE_CONNECTED", qemuMonitorJSONHandleSPICEConnect, },
-    { "SPICE_DISCONNECTED", qemuMonitorJSONHandleSPICEDisconnect, },
-    { "SPICE_INITIALIZED", qemuMonitorJSONHandleSPICEInitialize, },
-    { "SPICE_MIGRATE_COMPLETED", qemuMonitorJSONHandleSpiceMigrated, },
-    { "STOP", qemuMonitorJSONHandleStop, },
-    { "SUSPEND", qemuMonitorJSONHandlePMSuspend, },
-    { "SUSPEND_DISK", qemuMonitorJSONHandlePMSuspendDisk, },
-    { "VNC_CONNECTED", qemuMonitorJSONHandleVNCConnect, },
-    { "VNC_DISCONNECTED", qemuMonitorJSONHandleVNCDisconnect, },
-    { "VNC_INITIALIZED", qemuMonitorJSONHandleVNCInitialize, },
-    { "VSERPORT_CHANGE", qemuMonitorJSONHandleSerialChange, },
-    { "WAKEUP", qemuMonitorJSONHandlePMWakeup, },
-    { "WATCHDOG", qemuMonitorJSONHandleWatchdog, },
-    /* We use bsearch, so keep this list sorted.  */
-};
-
-static int
-qemuMonitorEventCompare(const void *key, const void *elt)
-{
-    const char *type = key;
-    const qemuEventHandler *handler = elt;
-    return strcmp(type, handler->type);
-}
-
 static int
 qemuMonitorJSONIOProcessEvent(qemuMonitor *mon,
                               virJSONValue *obj)
 {
     const char *type;
-    qemuEventHandler *handler;
+    const qemuEventHandler *handler;
     virJSONValue *data;
     g_autofree char *details = NULL;
     virJSONValue *timestamp;
@@ -171,8 +81,7 @@ qemuMonitorJSONIOProcessEvent(qemuMonitor *mon,
     }
     qemuMonitorEmitEvent(mon, type, seconds, micros, details);
 
-    handler = bsearch(type, eventHandlers, G_N_ELEMENTS(eventHandlers),
-                      sizeof(eventHandlers[0]), qemuMonitorEventCompare);
+    handler = qemu_monitor_event_lookup(type, strlen(type));
     if (handler) {
         VIR_DEBUG("handle %s handler=%p data=%p", type,
                   handler->handler, data);
@@ -543,7 +452,7 @@ qemuMonitorJSONMakeCommand(const char *cmdname,
 }
 
 
-static void qemuMonitorJSONHandleShutdown(qemuMonitor *mon, virJSONValue *data)
+void qemuMonitorJSONHandleShutdown(qemuMonitor *mon, virJSONValue *data)
 {
     bool guest = false;
     virTristateBool guest_initiated = VIR_TRISTATE_BOOL_ABSENT;
@@ -554,17 +463,17 @@ static void qemuMonitorJSONHandleShutdown(qemuMonitor *mon, virJSONValue *data)
     qemuMonitorEmitShutdown(mon, guest_initiated);
 }
 
-static void qemuMonitorJSONHandleReset(qemuMonitor *mon, virJSONValue *data G_GNUC_UNUSED)
+void qemuMonitorJSONHandleReset(qemuMonitor *mon, virJSONValue *data G_GNUC_UNUSED)
 {
     qemuMonitorEmitReset(mon);
 }
 
-static void qemuMonitorJSONHandleStop(qemuMonitor *mon, virJSONValue *data G_GNUC_UNUSED)
+void qemuMonitorJSONHandleStop(qemuMonitor *mon, virJSONValue *data G_GNUC_UNUSED)
 {
     qemuMonitorEmitStop(mon);
 }
 
-static void qemuMonitorJSONHandleResume(qemuMonitor *mon, virJSONValue *data G_GNUC_UNUSED)
+void qemuMonitorJSONHandleResume(qemuMonitor *mon, virJSONValue *data G_GNUC_UNUSED)
 {
     qemuMonitorEmitResume(mon);
 }
@@ -635,7 +544,7 @@ qemuMonitorJSONGuestPanicExtractInfo(virJSONValue *data)
 }
 
 
-static void
+void
 qemuMonitorJSONHandleGuestPanic(qemuMonitor *mon,
                                 virJSONValue *data)
 {
@@ -649,7 +558,7 @@ qemuMonitorJSONHandleGuestPanic(qemuMonitor *mon,
 }
 
 
-static void qemuMonitorJSONHandleRTCChange(qemuMonitor *mon, virJSONValue *data)
+void qemuMonitorJSONHandleRTCChange(qemuMonitor *mon, virJSONValue *data)
 {
     long long offset = 0;
     if (virJSONValueObjectGetNumberLong(data, "offset", &offset) < 0) {
@@ -665,7 +574,7 @@ VIR_ENUM_IMPL(qemuMonitorWatchdogAction,
               "none", "pause", "reset", "poweroff", "shutdown", "debug", "inject-nmi",
 );
 
-static void qemuMonitorJSONHandleWatchdog(qemuMonitor *mon, virJSONValue *data)
+void qemuMonitorJSONHandleWatchdog(qemuMonitor *mon, virJSONValue *data)
 {
     const char *action;
     int actionID;
@@ -689,7 +598,7 @@ VIR_ENUM_IMPL(qemuMonitorIOErrorAction,
 );
 
 
-static void
+void
 qemuMonitorJSONHandleIOError(qemuMonitor *mon, virJSONValue *data)
 {
     const char *device;
@@ -802,19 +711,19 @@ qemuMonitorJSONHandleGraphicsVNC(qemuMonitor *mon,
                             authScheme, x509dname, saslUsername);
 }
 
-static void qemuMonitorJSONHandleVNCConnect(qemuMonitor *mon, virJSONValue *data)
+void qemuMonitorJSONHandleVNCConnect(qemuMonitor *mon, virJSONValue *data)
 {
     qemuMonitorJSONHandleGraphicsVNC(mon, data, VIR_DOMAIN_EVENT_GRAPHICS_CONNECT);
 }
 
 
-static void qemuMonitorJSONHandleVNCInitialize(qemuMonitor *mon, virJSONValue *data)
+void qemuMonitorJSONHandleVNCInitialize(qemuMonitor *mon, virJSONValue *data)
 {
     qemuMonitorJSONHandleGraphicsVNC(mon, data, VIR_DOMAIN_EVENT_GRAPHICS_INITIALIZE);
 }
 
 
-static void qemuMonitorJSONHandleVNCDisconnect(qemuMonitor *mon, virJSONValue *data)
+void qemuMonitorJSONHandleVNCDisconnect(qemuMonitor *mon, virJSONValue *data)
 {
     qemuMonitorJSONHandleGraphicsVNC(mon, data, VIR_DOMAIN_EVENT_GRAPHICS_DISCONNECT);
 }
@@ -884,24 +793,24 @@ qemuMonitorJSONHandleGraphicsSPICE(qemuMonitor *mon,
 }
 
 
-static void qemuMonitorJSONHandleSPICEConnect(qemuMonitor *mon, virJSONValue *data)
+void qemuMonitorJSONHandleSPICEConnect(qemuMonitor *mon, virJSONValue *data)
 {
     qemuMonitorJSONHandleGraphicsSPICE(mon, data, VIR_DOMAIN_EVENT_GRAPHICS_CONNECT);
 }
 
 
-static void qemuMonitorJSONHandleSPICEInitialize(qemuMonitor *mon, virJSONValue *data)
+void qemuMonitorJSONHandleSPICEInitialize(qemuMonitor *mon, virJSONValue *data)
 {
     qemuMonitorJSONHandleGraphicsSPICE(mon, data, VIR_DOMAIN_EVENT_GRAPHICS_INITIALIZE);
 }
 
 
-static void qemuMonitorJSONHandleSPICEDisconnect(qemuMonitor *mon, virJSONValue *data)
+void qemuMonitorJSONHandleSPICEDisconnect(qemuMonitor *mon, virJSONValue *data)
 {
     qemuMonitorJSONHandleGraphicsSPICE(mon, data, VIR_DOMAIN_EVENT_GRAPHICS_DISCONNECT);
 }
 
-static void
+void
 qemuMonitorJSONHandleJobStatusChange(qemuMonitor *mon,
                                      virJSONValue *data)
 {
@@ -924,7 +833,7 @@ qemuMonitorJSONHandleJobStatusChange(qemuMonitor *mon,
 }
 
 
-static void
+void
 qemuMonitorJSONHandleTrayChange(qemuMonitor *mon,
                                 virJSONValue *data)
 {
@@ -955,14 +864,14 @@ qemuMonitorJSONHandleTrayChange(qemuMonitor *mon,
     qemuMonitorEmitTrayChange(mon, devAlias, devid, reason);
 }
 
-static void
+void
 qemuMonitorJSONHandlePMWakeup(qemuMonitor *mon,
                               virJSONValue *data G_GNUC_UNUSED)
 {
     qemuMonitorEmitPMWakeup(mon);
 }
 
-static void
+void
 qemuMonitorJSONHandlePMSuspend(qemuMonitor *mon,
                                virJSONValue *data G_GNUC_UNUSED)
 {
@@ -970,7 +879,7 @@ qemuMonitorJSONHandlePMSuspend(qemuMonitor *mon,
 }
 
 
-static void
+void
 qemuMonitorJSONHandleBalloonChange(qemuMonitor *mon,
                                    virJSONValue *data)
 {
@@ -983,14 +892,14 @@ qemuMonitorJSONHandleBalloonChange(qemuMonitor *mon,
     qemuMonitorEmitBalloonChange(mon, actual);
 }
 
-static void
+void
 qemuMonitorJSONHandlePMSuspendDisk(qemuMonitor *mon,
                                    virJSONValue *data G_GNUC_UNUSED)
 {
     qemuMonitorEmitPMSuspendDisk(mon);
 }
 
-static void
+void
 qemuMonitorJSONHandleDeviceDeleted(qemuMonitor *mon, virJSONValue *data)
 {
     const char *device;
@@ -1004,7 +913,7 @@ qemuMonitorJSONHandleDeviceDeleted(qemuMonitor *mon, virJSONValue *data)
 }
 
 
-static void
+void
 qemuMonitorJSONHandleDeviceUnplugErr(qemuMonitor *mon, virJSONValue *data)
 {
     const char *device;
@@ -1021,7 +930,7 @@ qemuMonitorJSONHandleDeviceUnplugErr(qemuMonitor *mon, virJSONValue *data)
 }
 
 
-static void
+void
 qemuMonitorJSONHandleNetdevStreamDisconnected(qemuMonitor *mon, virJSONValue *data)
 {
     const char *name;
@@ -1035,7 +944,7 @@ qemuMonitorJSONHandleNetdevStreamDisconnected(qemuMonitor *mon, virJSONValue *da
 }
 
 
-static void
+void
 qemuMonitorJSONHandleNicRxFilterChanged(qemuMonitor *mon, virJSONValue *data)
 {
     const char *name;
@@ -1049,7 +958,7 @@ qemuMonitorJSONHandleNicRxFilterChanged(qemuMonitor *mon, virJSONValue *data)
 }
 
 
-static void
+void
 qemuMonitorJSONHandleSerialChange(qemuMonitor *mon,
                                   virJSONValue *data)
 {
@@ -1070,7 +979,7 @@ qemuMonitorJSONHandleSerialChange(qemuMonitor *mon,
 }
 
 
-static void
+void
 qemuMonitorJSONHandleSpiceMigrated(qemuMonitor *mon,
                                    virJSONValue *data G_GNUC_UNUSED)
 {
@@ -1078,7 +987,7 @@ qemuMonitorJSONHandleSpiceMigrated(qemuMonitor *mon,
 }
 
 
-static void
+void
 qemuMonitorJSONHandleMemoryDeviceSizeChange(qemuMonitor *mon,
                                             virJSONValue *data)
 {
@@ -1100,7 +1009,7 @@ qemuMonitorJSONHandleMemoryDeviceSizeChange(qemuMonitor *mon,
 }
 
 
-static void
+void
 qemuMonitorJSONHandleMemoryFailure(qemuMonitor *mon,
                                    virJSONValue *data)
 {
@@ -1147,7 +1056,7 @@ qemuMonitorJSONHandleMemoryFailure(qemuMonitor *mon,
 }
 
 
-static void
+void
 qemuMonitorJSONHandleMigrationStatus(qemuMonitor *mon,
                                      virJSONValue *data)
 {
@@ -1168,7 +1077,7 @@ qemuMonitorJSONHandleMigrationStatus(qemuMonitor *mon,
 }
 
 
-static void
+void
 qemuMonitorJSONHandleMigrationPass(qemuMonitor *mon,
                                    virJSONValue *data)
 {
@@ -1183,7 +1092,7 @@ qemuMonitorJSONHandleMigrationPass(qemuMonitor *mon,
 }
 
 
-static void
+void
 qemuMonitorJSONHandleAcpiOstInfo(qemuMonitor *mon, virJSONValue *data)
 {
     virJSONValue *info;
@@ -1220,7 +1129,7 @@ qemuMonitorJSONHandleAcpiOstInfo(qemuMonitor *mon, virJSONValue *data)
 }
 
 
-static void
+void
 qemuMonitorJSONHandleBlockThreshold(qemuMonitor *mon, virJSONValue *data)
 {
     const char *nodename;
@@ -1280,7 +1189,7 @@ qemuMonitorJSONExtractDumpStats(virJSONValue *result,
 }
 
 
-static void
+void
 qemuMonitorJSONHandleDumpCompleted(qemuMonitor *mon,
                                    virJSONValue *data)
 {
@@ -1302,8 +1211,8 @@ qemuMonitorJSONHandleDumpCompleted(qemuMonitor *mon,
 }
 
 
-static void qemuMonitorJSONHandlePRManagerStatusChanged(qemuMonitor *mon,
-                                                        virJSONValue *data)
+void qemuMonitorJSONHandlePRManagerStatusChanged(qemuMonitor *mon,
+                                                 virJSONValue *data)
 {
     const char *name;
     bool connected;
@@ -1323,8 +1232,8 @@ static void qemuMonitorJSONHandlePRManagerStatusChanged(qemuMonitor *mon,
 }
 
 
-static void qemuMonitorJSONHandleRdmaGidStatusChanged(qemuMonitor *mon,
-                                                      virJSONValue *data)
+void qemuMonitorJSONHandleRdmaGidStatusChanged(qemuMonitor *mon,
+                                               virJSONValue *data)
 {
     const char *netdev;
     bool gid_status;
@@ -1357,7 +1266,7 @@ static void qemuMonitorJSONHandleRdmaGidStatusChanged(qemuMonitor *mon,
 }
 
 
-static void
+void
 qemuMonitorJSONHandleGuestCrashloaded(qemuMonitor *mon,
                                       virJSONValue *data)
 {
diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h
index 9684660d86..5bd4b21b7d 100644
--- a/src/qemu/qemu_monitor_json.h
+++ b/src/qemu/qemu_monitor_json.h
@@ -27,6 +27,51 @@
 #include "cpu/cpu.h"
 #include "util/virgic.h"
 
+typedef struct {
+    const char *type;
+    void (*handler)(qemuMonitor *mon, virJSONValue *data);
+} qemuEventHandler;
+
+const qemuEventHandler *
+qemu_monitor_event_lookup(const char *str, size_t len);
+
+void qemuMonitorJSONHandleShutdown(qemuMonitor *mon, virJSONValue *data);
+void qemuMonitorJSONHandleReset(qemuMonitor *mon, virJSONValue *data);
+void qemuMonitorJSONHandleStop(qemuMonitor *mon, virJSONValue *data);
+void qemuMonitorJSONHandleResume(qemuMonitor *mon, virJSONValue *data);
+void qemuMonitorJSONHandleRTCChange(qemuMonitor *mon, virJSONValue *data);
+void qemuMonitorJSONHandleWatchdog(qemuMonitor *mon, virJSONValue *data);
+void qemuMonitorJSONHandleIOError(qemuMonitor *mon, virJSONValue *data);
+void qemuMonitorJSONHandleVNCConnect(qemuMonitor *mon, virJSONValue *data);
+void qemuMonitorJSONHandleVNCInitialize(qemuMonitor *mon, virJSONValue *data);
+void qemuMonitorJSONHandleVNCDisconnect(qemuMonitor *mon, virJSONValue *data);
+void qemuMonitorJSONHandleSPICEConnect(qemuMonitor *mon, virJSONValue *data);
+void qemuMonitorJSONHandleSPICEInitialize(qemuMonitor *mon, virJSONValue *data);
+void qemuMonitorJSONHandleSPICEDisconnect(qemuMonitor *mon, virJSONValue *data);
+void qemuMonitorJSONHandleTrayChange(qemuMonitor *mon, virJSONValue *data);
+void qemuMonitorJSONHandlePMWakeup(qemuMonitor *mon, virJSONValue *data);
+void qemuMonitorJSONHandlePMSuspend(qemuMonitor *mon, virJSONValue *data);
+void qemuMonitorJSONHandleJobStatusChange(qemuMonitor *mon, virJSONValue *data);
+void qemuMonitorJSONHandleBalloonChange(qemuMonitor *mon, virJSONValue *data);
+void qemuMonitorJSONHandlePMSuspendDisk(qemuMonitor *mon, virJSONValue *data);
+void qemuMonitorJSONHandleGuestCrashloaded(qemuMonitor *mon, virJSONValue *data);
+void qemuMonitorJSONHandleGuestPanic(qemuMonitor *mon, virJSONValue *data);
+void qemuMonitorJSONHandleDeviceDeleted(qemuMonitor *mon, virJSONValue *data);
+void qemuMonitorJSONHandleNicRxFilterChanged(qemuMonitor *mon, virJSONValue *data);
+void qemuMonitorJSONHandleSerialChange(qemuMonitor *mon, virJSONValue *data);
+void qemuMonitorJSONHandleSpiceMigrated(qemuMonitor *mon, virJSONValue *data);
+void qemuMonitorJSONHandleMigrationStatus(qemuMonitor *mon, virJSONValue *data);
+void qemuMonitorJSONHandleMigrationPass(qemuMonitor *mon, virJSONValue *data);
+void qemuMonitorJSONHandleAcpiOstInfo(qemuMonitor *mon, virJSONValue *data);
+void qemuMonitorJSONHandleBlockThreshold(qemuMonitor *mon, virJSONValue *data);
+void qemuMonitorJSONHandleDumpCompleted(qemuMonitor *mon, virJSONValue *data);
+void qemuMonitorJSONHandlePRManagerStatusChanged(qemuMonitor *mon, virJSONValue *data);
+void qemuMonitorJSONHandleRdmaGidStatusChanged(qemuMonitor *mon, virJSONValue *data);
+void qemuMonitorJSONHandleMemoryFailure(qemuMonitor *mon, virJSONValue *data);
+void qemuMonitorJSONHandleMemoryDeviceSizeChange(qemuMonitor *mon, virJSONValue *data);
+void qemuMonitorJSONHandleDeviceUnplugErr(qemuMonitor *mon, virJSONValue *data);
+void qemuMonitorJSONHandleNetdevStreamDisconnected(qemuMonitor *mon, virJSONValue *data);
+
 int
 qemuMonitorJSONIOProcessLine(qemuMonitor *mon,
                              const char *line,
-- 
2.34.1
Re: [PATCH] qemu: Optimize monitor event name lookup with hash tables
Posted by Peter Krempa 6 months ago
On Tue, May 21, 2024 at 17:36:48 +0530, Rayhan Faizel wrote:
> Currently, monitor event names are looked up using binary search which has
> O(log(n)) time complexity. This can be optimized even further with a
> compile-time static hash table generated by the gperf tool. As gperf ensures
> perfect hashing, lookup times are guaranteed to be O(1).

As a first point I'd like to ask if you are seeing any specific
performance problems with the existing code and ideally elaborate in
which cases.

This is a non-trivial change and with a justification in this commit
message I'm not really convinced that it's needed.

Unless QEMU would be firing a massive amount of events it is unlikely
that the lookup of the event will even have measurable impact.

It's also very likely that the lookup of the event handler is
overshadowed by the complexity of the actual handler, thus is the impact
even measurable?

> This patch also makes gperf a requirement for compiling libvirt if the QEMU
> driver is enabled.

Based on the above question I'm not persuaded that this is really
justified at this point.

Note that such change will also require update to the .spec. file
libvirt ships in the repo.

> 
> Signed-off-by: Rayhan Faizel <rayhan.faizel@gmail.com>
> ---
>  docs/kbase/internals/qemu-event-handlers.rst |  13 +-
>  meson.build                                  |   2 +
>  src/qemu/meson.build                         |   9 +
>  src/qemu/qemu_monitor_event.gperf            |  68 ++++++++
>  src/qemu/qemu_monitor_json.c                 | 171 +++++--------------
>  src/qemu/qemu_monitor_json.h                 |  45 +++++
>  6 files changed, 170 insertions(+), 138 deletions(-)
>  create mode 100644 src/qemu/qemu_monitor_event.gperf