From patchwork Thu Apr 21 08:56:30 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Lezcano X-Patchwork-Id: 66345 Delivered-To: patch@linaro.org Received: by 10.140.93.198 with SMTP id d64csp15999qge; Thu, 21 Apr 2016 01:56:53 -0700 (PDT) X-Received: by 10.98.86.77 with SMTP id k74mr18641087pfb.28.1461229013722; Thu, 21 Apr 2016 01:56:53 -0700 (PDT) Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id 10si136764paw.114.2016.04.21.01.56.53; Thu, 21 Apr 2016 01:56:53 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752201AbcDUI4u (ORCPT + 29 others); Thu, 21 Apr 2016 04:56:50 -0400 Received: from mail-wm0-f48.google.com ([74.125.82.48]:35338 "EHLO mail-wm0-f48.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751240AbcDUI4r (ORCPT ); Thu, 21 Apr 2016 04:56:47 -0400 Received: by mail-wm0-f48.google.com with SMTP id e201so78490225wme.0 for ; Thu, 21 Apr 2016 01:56:46 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id; bh=zpMP6okkR70I3s6qzvFoaZ63AE/1njOUgxLwjH5GAsU=; b=GJMKjcQIM7jCYJy3D4xNLDmJ6oI5R3boo2EweEifq6Q47Q+oySgDV35d50Km6ZrhI0 n/NMTLZfnZAUtRzgShe98LSg5cu7llsWNxkgh+OG1r+iGroQwaDAIGjUixYU6Wb/qRn8 zidWcnpTJ9027SZYtu8QZLaz8BtdK6GRM6rRw= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=zpMP6okkR70I3s6qzvFoaZ63AE/1njOUgxLwjH5GAsU=; b=fo2HEjdAAdXv+T8lZf6dF25OxKOXDWpVjzkSvGM6bW05sTeFxZTvyvJDiFAT511iLA H3itUOORFv5Cek5N7eo0ESDRF1JTGatU6oxz5Od0JiUx5Hq1ftoAPcF6ob9avLCge4wW 0ivnqevyhZelq2GWTM6RomTUfbO0dNvg1LQfGy9sX9OzqtEIH6KgPqx1GuOMojOUZz6V havo3UGllt4XOt0Dauy887mwTAEeeD4kx0GTt0c5vAkmQsQmdrO6JKxquCU/00Nd5+ec isElh4kQ9l0lxE4cYnRM1crPVvdiCNDLRNosAzfXJMxRCW+uiIEaHgVvwrt9vcSFAU4B cU8Q== X-Gm-Message-State: AOPr4FXktVCKqX1W/wdq9weF19979vLgHOU+5o549VRXEL7xJI9eEW0xmZxsRGGZvHL0hrYC X-Received: by 10.194.21.131 with SMTP id v3mr12816583wje.132.1461229005729; Thu, 21 Apr 2016 01:56:45 -0700 (PDT) Received: from localhost.localdomain (sju31-1-78-210-255-2.fbx.proxad.net. [78.210.255.2]) by smtp.gmail.com with ESMTPSA id 188sm2161399wmk.6.2016.04.21.01.56.44 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 21 Apr 2016 01:56:45 -0700 (PDT) From: Daniel Lezcano To: rjw@rjwysocki.net Cc: peterz@infradead.org, mingo@kernel.org, linux-pm@vger.kernel.org (open list:CPUIDLE DRIVERS), linux-kernel@vger.kernel.org (open list) Subject: [PATCH V2] cpuidle: Change ktime_get() with local_clock() Date: Thu, 21 Apr 2016 10:56:30 +0200 Message-Id: <1461228990-28957-1-git-send-email-daniel.lezcano@linaro.org> X-Mailer: git-send-email 1.9.1 Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org The ktime_get() can have a non negligeable overhead, use local_clock() instead. In order to test the difference between ktime_get() and local_clock(), a quick hack has been added to trigger, via debugfs, 10000 times a call to ktime_get() and local_clock() and measure the elapsed time. Then the average value, the min and max is computed for each call. >From userspace, the test above was called 100 times every 2 seconds. So, ktime_get() and local_clock() have been called 1000000 times in total. The results are: ktime_get(): -- 1.9.1 ============ * average: 101 ns (stddev: 27.4) * maximum: 38313 ns * minimum: 65 ns local_clock(): ============== * average: 60 ns (stddev: 9.8) * maximum: 13487 ns * minimum: 46 ns The local_clock() is faster and more stable. Even if it is a drop in the ocean, changing the ktime_get() by the local_clock() allows to save 80ns at idle time (entry + exit). And in some circumstances, especially when there are several CPUs racing for the clock access, we save tens of microseconds. The idle duration resulting from a diff is converted from nanosec to microsec. This could be done with integer division (div 1000) - which is an expensive operation or by 10 bits shifting (div 1024) - which is fast but unprecise. The following table gives some results at the limits. ------------------------------------------ | nsec | div(1000) | div(1024) | ------------------------------------------ | 1e3 | 1 usec | 976 nsec | ------------------------------------------ | 1e6 | 1000 usec | 976 usec | ------------------------------------------ | 1e9 | 1000000 usec | 976562 usec | ------------------------------------------ There is a linear deviation of 2.34%. This loss of precision is acceptable in the context of the resulting diff which is used for statistics. These ones are processed to guess estimate an approximation of the duration of the next idle period which ends up into an idle state selection. The selection criteria takes into account the next duration based on large intervals, represented by the idle state's target residency. The 2^10 division is enough because the approximation regarding the 1e3 division is lost in all the approximations done for the next idle duration computation. Signed-off-by: Daniel Lezcano --- V2: - Explained in the changelog why div1024 is enough precise for our purpose. --- drivers/cpuidle/cpuidle.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index f996efc..78447bc 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -173,7 +173,7 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv, struct cpuidle_state *target_state = &drv->states[index]; bool broadcast = !!(target_state->flags & CPUIDLE_FLAG_TIMER_STOP); - ktime_t time_start, time_end; + u64 time_start, time_end; s64 diff; /* @@ -195,13 +195,13 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv, sched_idle_set_state(target_state); trace_cpu_idle_rcuidle(index, dev->cpu); - time_start = ktime_get(); + time_start = local_clock(); stop_critical_timings(); entered_state = target_state->enter(dev, drv, index); start_critical_timings(); - time_end = ktime_get(); + time_end = local_clock(); trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu); /* The cpu is no longer idle or about to enter idle. */ @@ -217,7 +217,11 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv, if (!cpuidle_state_is_coupled(drv, entered_state)) local_irq_enable(); - diff = ktime_to_us(ktime_sub(time_end, time_start)); + /* + * local_clock() returns the time in nanosecond, let's shift + * by 10 (divide by 1024) to have microsecond based time. + */ + diff = (time_end - time_start) >> 10; if (diff > INT_MAX) diff = INT_MAX;