qt5base-lts/tests/benchmarks
Dennis Oberst 3c38efbfe4 QByteArray: use new assign() in operator=(const char *)
operator=(~) and assign(~) share similar names but, until now, have not
shared the same functionality. This patch introduces the usage of
QByteArray::assign() within the non-sharing assignment operator to
effectively boost efficiency by reusing the available capacity.

Since these assignment operators are frequently used in many places,
both within Qt and non-Qt code, this patch comes with benchmarks.

The preview of the benchmark results are compared with this patch and
before this patch. The findings indicate a slight enhancement in
performance associated with the assignment operator. Despite the results
displaying only a minor improvement, progress has been made. Therefore
use assign(QByteArrayView) as replacement.

(x86_64-little_endian-lp64 shared (dynamic) release build (O3); by
gcc 13.2.1, endeavouros ; 13th Gen Intel(R) Core(TM) i9-13900K

benchmarks executed with -perf -iterations 1000000

  * The last value at the EOL represent the string size.

QByteArray &operator=(const char *ch) (current)
  65    cycles/iter; 317  instructions/iter; 16.0 nsec/iter (5)
  71.7  cycles/iter; 383  instructions/iter; 13.0 nsec/iter (10)
  59.8  cycles/iter; 318  instructions/iter; 10.9 nsec/iter (20)
  70.8  cycles/iter; 340  instructions/iter; 12.9 nsec/iter (50)
  80.2  cycles/iter; 419  instructions/iter; 14.6 nsec/iter (100)
  164.2 cycles/iter; 899  instructions/iter; 29.9 nsec/iter (500)
  260.5 cycles/iter; 1522 instructions/iter; 45.6 nsec/iter (1'000)

QByteArray &operator=(const char *ch) (before)
  66.8  cycles/iter; 317  instructions/iter; 16.9 nsec/iter (5)
  76.5  cycles/iter; 383  instructions/iter; 13.9 nsec/iter (10)
  63.7  cycles/iter; 318  instructions/iter; 11.6 nsec/iter (20)
  71.6  cycles/iter; 344  instructions/iter; 13.0 nsec/iter (50)
  77.5  cycles/iter; 419  instructions/iter; 14.1 nsec/iter (100)
  143.4 cycles/iter; 893  instructions/iter; 26.1 nsec/iter (500)
  270.8 cycles/iter; 1516 instructions/iter; 48.2 nsec/iter (1'000)

Task-number: QTBUG-106201
Change-Id: I0745c33f0f61f1d844a60960cc55f565320d5945
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
2023-08-11 14:11:44 +02:00
..
corelib QByteArray: use new assign() in operator=(const char *) 2023-08-11 14:11:44 +02:00
dbus tests: Remove remains of qmake conversion from CMakeLists.txt files 2023-02-17 21:56:49 +01:00
gui tests: Remove remains of qmake conversion from CMakeLists.txt files 2023-02-17 21:56:49 +01:00
network QTestEventLoop: add enterLoop(std::chrono::milliseconds) overload 2023-03-03 21:36:48 +02:00
plugins/imageformats/jpeg tests: Remove remains of qmake conversion from CMakeLists.txt files 2023-02-17 21:56:49 +01:00
sql SQL/Benchmarks: cleanup 2023-04-05 05:37:32 +01:00
testlib tests: Remove remains of qmake conversion from CMakeLists.txt files 2023-02-17 21:56:49 +01:00
widgets QWidget benchmark: port away from Q_FOREACH 2023-08-08 17:08:00 +02:00
CMakeLists.txt tests: Remove remains of qmake conversion from CMakeLists.txt files 2023-02-17 21:56:49 +01:00
README Whitespace cleanup: remove trailing whitespace 2013-03-16 20:22:50 +01:00

The most reliable way of running benchmarks is to do it in an otherwise idle
system. On a busy system, the results will vary according to the other tasks
demanding attention in the system.

We have managed to obtain quite reliable results by doing the following on
Linux (and you need root):

 - switching the scheduler to a Real-Time mode
 - setting the processor affinity to one single processor
 - disabling the other thread of the same core

This should work rather well for CPU-intensive tasks. A task that is in Real-
Time mode will simply not be preempted by the OS. But if you make OS syscalls,
especially I/O ones, your task will be de-scheduled. Note that this includes
page faults, so if you can, make sure your benchmark's warmup code paths touch
most of the data.

To do this you need a tool called schedtool (package schedtool), from
http://freequaos.host.sk/schedtool/

From this point on, we are using CPU0 for all tasks:

If you have a Hyperthreaded multi-core processor (Core-i5 and Core-i7), you
have to disable the other thread of the same core as CPU0. To discover which
one it is:

$ cat /sys/devices/system/cpu/cpu0/topology/thread_siblings_list

This will print something like 0,4, meaning that CPUs 0 and 4 are sibling
threads on the same core. So we'll turn CPU 4 off:

(as root)
# echo 0 > /sys/devices/system/cpu/cpu4/online

To turn it back on, echo 1 into the same file.

To run a task on CPU 0 exclusively, using FIFO RT priority 10, you run the
following:

(as root)
# schedtool -F -p 10 -a 1 -e ./taskname

For example:
# schedtool -F -p 10 -a 1 -e ./tst_bench_qstring -tickcounter

Warning: if your task livelocks or takes far too long to complete, your system
may be unusable for a long time, especially if you don't have other cores to
run stuff on. To prevent that, run it before schedtool and time it.

You can also limit the CPU time that the task is allowed to take. Run in the
same shell as you'll run schedtool:

$ ulimit -s 300
To limit to 300 seconds (5 minutes)

If your task runs away, it will get a SIGXCPU after consuming 5 minutes of CPU
time (5 minutes running at 100%).

If your app is multithreaded, you may want to give it more CPUs, like CPU0 and
CPU1 with -a 3  (it's a bitmask).

For best results, you should disable ALL other cores and threads of the same
processor. The new Core-i7 have one processor with 4 cores,
each core can run 2 threads; the older Mac Pros have two processors with 4
cores each. So on those Mac Pros, you'd disable cores 1, 2 and 3, while on the
Core-i7, you'll need to disable all other CPUs.

However, disabling just the sibling thread seems to produce very reliable
results for me already, with variance often below 0.5% (even though there are
some measurable spikes).

Other things to try:

Running the benchmark with highest priority, i.e. "sudo nice -19"
usually produces stable results on some machines. If the benchmark also
involves displaying something on the screen (on X11), running it with
"-sync" is a must. Though, in that case the "real" cost is not correct,
but it is useful to discover regressions.

Also; not many people know about ionice (1)
      ionice - get/set program io scheduling class and priority