Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unhardcode tick-to-ms conversion #381

Closed
wants to merge 1 commit into from

Conversation

amomchilov
Copy link
Contributor

Division by 100000.0 worked because sysconf(_SC_CLK_TCK) happened to be 100.

By unhardcoding:

  1. It becomes more clear what this 10000.0 figure comes from.
  2. It protects against bugs in the case sysconf(_SC_CLK_TCK) ever changes.

@@ -240,6 +240,12 @@ void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps,
proc->updated = true;
}

double ticks_to_nanoseconds(const double ticks) {
const double secs_per_tick = 1.0 / (double)sysconf(_SC_CLK_TCK);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably best to cache the results of this sysconf call so that we're not recalculating it on every sample? (perhaps a global 'hertz' var set from either the darwin Platform_init or ProcessList_new routine)

sysconf can also fail, so doing this call once on startup will make error checking / handling easier.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed! Though I'm not a fan of herz, I'd rather something like clock_ticks_per_s. Thoughts?

I just feeling out if this PR is a move in the right direction.

I noticed that a lot of this code-base is unclear about units, exact derivations, etc., so I'm not sure if this kind of change would be desired or not.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed! Though I'm not a fan of herz, I'd rather something like clock_ticks_per_s. Thoughts?

Sure, doesn't have to be hertz - if its multi-words like that though camel case is more commonly used in htop (see darwin/Platform.c for some examples).

I just feeling out if this PR is a move in the right direction.

Looks like its moving in a less broken direction - so two thumbs up from me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes, I didn't notice. Looks like local vars are snake case, and functions/types are camel case. I'll change my identifiers to match.

Some other questions:

  1. Should I add an explicit but technically redundant #include <unistd.h>? It's what defines sysconf, but it's also already indirectly included (probably by CoreFoundation, but I haven't checked.

  2. The return type of sysconf is long. Should the cache variable match that and be a long, or use uint64_t like the rest? Or should I cache it after conversion to double?

  3. I saw that sysconf(_SC_CLK_TCK) is also called in LinuxProcess_adjustTime. They called the cache var jiffy. It appears that they use a lazy-initialization pattern, that makes sysconf(_SC_CLK_TCK) called upon the first call to LinuxProcess_adjustTime.

    They fail silently if the errno is set, by just assuming 100 Hz. That seems... like the wrong choice to me. Wouldn't it be better to fail loudly than to assume a potentially wrong value? I feel like I'm pulling a thread here haha.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1. Should I add an explicit but technically redundant `#include <unistd.h>`? It's what defines `sysconf`, but it's also already indirectly included (probably by CoreFoundation, but I haven't checked.

I'd not add it explicitly unless its needed to compile - as you say, chances are its included indirectly already.

2. The return type of `sysconf` is `long`. Should the cache variable match that and be a `long`, or use `uint64_t` like the rest? Or should I cache it after conversion to `double`?

I recommend you do as much work up front as possible, at startup, and as little work as possible during each sample.
Thus, I think its best to store it as a double (like you had it in the original patch).

3. I saw that `sysconf(_SC_CLK_TCK)` is also called in `LinuxProcess_adjustTime`. They called the cache var `jiffy`. It appears that they use a lazy-initialization pattern, that makes `sysconf(_SC_CLK_TCK)` called upon the first call to `LinuxProcess_adjustTime`.

There's a good chance this code can be improved also.

   They fail silently if the `errno` is set, by just assuming 100 Hz. That seems... like the wrong choice to me. Wouldn't it be better to fail loudly than to assume a potentially wrong value? I feel like I'm pulling a thread here haha.

Heh - yep. It definitely would be better to fail htop at startup if this call fails - on Linux too, IMO.

Copy link
Contributor Author

@amomchilov amomchilov Dec 11, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recommend you do as much work up front as possible, at startup, and as little work as possible during each sample.
Thus, I think its best to store it as a double (like you had it in the original patch).

I had a change of mind on this. Though double suits my usecase, some other feature might end up re-using it and converting it to uint64_t, so I was leaning towards keeping it as its original long. It's just one cvttsd2si instruction, and the compiler could probably lift the conversion of out of the loop anyway.

Heh - yep. It definitely would be better to fail htop at startup if this call fails - on Linux too, IMO.

Opened #384 to track this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@BenBE
Copy link
Member

BenBE commented Dec 11, 2020

Might help resolve #368

darwin/Platform.c Outdated Show resolved Hide resolved
@amomchilov amomchilov force-pushed the darwin-cpu-tick-to-s branch 2 times, most recently from 13945a1 to b5c13dd Compare December 11, 2020 17:18
Copy link
Member

@BenBE BenBE left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if all the explicit double casts are technically required.

darwin/DarwinProcess.c Outdated Show resolved Hide resolved
darwin/Platform.c Outdated Show resolved Hide resolved
@@ -31,6 +31,8 @@ extern const unsigned int Platform_numberOfSignals;

extern const MeterClass* const Platform_meterTypes[];

extern long Platform_clockTicksPerSec;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably something for another PR, but if multiple platforms have this (Darwin + Linux, but basically *BSD too), this should most likely go into a global platform header (cf. #306) for some central interface for such universally needed information.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. See also: #384

Copy link
Member

@BenBE BenBE Dec 12, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment was mostly about this declaration itself, which is probably better suited to have in some platform-independent header and not with every platform.

That's different from #384, but could easily be resolved there.

@@ -6,6 +6,7 @@ in the source distribution for its full text.
*/

#include "DarwinProcess.h"
#include "Platform.h"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cf. styleguide on include order.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I got it right this time, let me know.

/ ((double)dpl->global_diff * 100000.0);
double time_interval = ticksToNanoseconds(dpl->global_diff) / (double) dpl->super.cpuCount;

proc->super.percent_cpu = ((double)diff / time_interval) * 100.0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should include some check for time_interval to not be too small. Epsilon check for interval > 1e-6 should be fine.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the meaning if interval is so small? Two back-to-back updates were done in very short succession?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For example. Another possibility is, when the notebook was in standby for a certain time. @cgzones fixed a similar issue for Linux recently, cf. #353

darwin/Platform.c Show resolved Hide resolved
@BenBE
Copy link
Member

BenBE commented Dec 13, 2020

I rebased you PR onto #392 to include the changes from there.
Resolved the inevitable merge conflicts in the process.

@@ -240,23 +241,28 @@ void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps,
proc->updated = true;
}

static double ticksToNanoseconds(const double ticks) {
const double nanos_per_sec = 1e9;
return ticks / (double) Platform_clockTicksPerSec * Platform_timebaseToNS * nanos_per_sec;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Platform_clockTicksPerSec * Platform_timebaseToNS * nanos_per_sec is a runtime constant.
Maybe pre-compute it

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean something like?

   static const double tickToNanosecondsScale = (double) Platform_clockTicksPerSec * Platform_timebaseToNS * 1e9;
   return ticks / tickToNanosecondsScale;

Copy link
Member

@cgzones cgzones Dec 13, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this would not work, as static variables are initialized before main() (and Platform_clockTicksPerSec and Platform_timebaseToNS are not yet set at this point).

Maybe it's OK to compute once every cycle when caching time_interval (see below).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

k, had to recheck on the actual initialization semantics here. Would take a second (bool) variable to act as lazy initialization guard to calculate this constant on first entry.

But as both values are set in Platform_init we can just initialize that scaling factor there.

if (0 != proc->utime || 0 != proc->stime) {
uint64_t diff = (pti.pti_total_system - proc->stime)
+ (pti.pti_total_user - proc->utime);
double time_interval = ticksToNanoseconds(dpl->global_diff) / (double) dpl->super.cpuCount;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

double time_interval = ticksToNanoseconds(dpl->global_diff) / (double) dpl->super.cpuCount; is constant for all processes of a single update cycle.
Maybe pre-compute it in DarwinProcessList.c::ProcessList_goThroughEntries()

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then this should be passed in as parameter to DarwinProcess_setFromLibprocPidinfo to avoid depending on global state.

@cgzones cgzones added this to the 3.0.4 milestone Dec 13, 2020
Division by 100000.0 worked because `sysconf(_SC_CLK_TCK)` happened to be 100.

By unhardcoding:

1) It becomes more clear what this 100000.0 figure comes from.
2) It protects against bugs in the case `sysconf(_SC_CLK_TCK)` ever changes.
@cgzones
Copy link
Member

cgzones commented Dec 19, 2020

Applied via 67ccd6b

Thanks for the contribution!

@cgzones cgzones closed this Dec 19, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants