Returns information describing how much time normal and dirty CPU schedulers in the system have been busy. This value is normally a better indicator of how much load an Erlang node is under instead of looking at the CPU utilization provided by tools such as top or sysstat. This is because scheduler_wall_time also includes time where the scheduler is waiting for some other reasource (such as an internal mutex) to be available but does not use the CPU. In order to better understand what a scheduler is busy doing you can use microstate accounting.
The definition of a busy scheduler is when it is not idle and not busy waiting for new work, that is:
- Executing process code
- Executing linked-in driver or NIF code
- Executing BIFs, or any other runtime handling
- Garbage collecting
- Handling any other memory management
Notice that a scheduler can also be busy even if the OS has scheduled out the scheduler thread.
Note
It is recommended to use the module scheduler instead of this function directly as it provides an easier way to get the information that you usually want.
If enabled this function returns a list of tuples with {SchedulerId, ActiveTime, TotalTime}, where SchedulerId is an integer ID of the scheduler, ActiveTime is the duration the scheduler has been busy, and TotalTime is the total time duration since scheduler_wall_time activation for the specific scheduler. The time unit returned is undefined and can be subject to change between releases, OSs, and system restarts. scheduler_wall_time is only to be used to calculate relative values for scheduler utilization. The ActiveTime can never exceed TotalTime. The list of scheduler information is unsorted and can appear in different order between calls.
The disabled this function returns undefined.
The activation time can differ significantly between schedulers. Currently dirty schedulers are activated at system start while normal schedulers are activated some time after the scheduler_wall_time functionality is enabled.
Only information about schedulers that are expected to handle CPU bound work is included in the return values from this function. If you also want information about dirty I/O schedulers, use statistics(scheduler_wall_time_all) instead.
Normal schedulers will have scheduler identifiers in the range 1 =< SchedulerId =< erlang:system_info(schedulers). Dirty CPU schedulers will have scheduler identifiers in the range erlang:system_info(schedulers) < SchedulerId =< erlang:system_info(schedulers) + erlang:system_info(dirty_cpu_schedulers).
Note
The different types of schedulers handle specific types of jobs. Every job is assigned to a specific scheduler type. Jobs can migrate between different schedulers of the same type, but never between schedulers of different types. This fact has to be taken under consideration when evaluating the result returned.
You can use scheduler_wall_time to calculate scheduler utilization. First you take a sample of the values returned by erlang:statistics(scheduler_wall_time).
> erlang:system_flag(scheduler_wall_time, true).
false
> Ts0 = lists:sort(erlang:statistics(scheduler_wall_time)), ok.
ok
Some time later the user takes another snapshot and calculates scheduler utilization per scheduler, for example:
> Ts1 = lists:sort(erlang:statistics(scheduler_wall_time)), ok.
ok
> lists:map(fun({{I, A0, T0}, {I, A1, T1}}) -> {I, (A1 - A0)/(T1 - T0)} end, lists:zip(Ts0,Ts1)).
[{1,0.9743474730177548},
{2,0.9744843782751444},
{3,0.9995902361669045},
{4,0.9738012596572161},
{5,0.9717956667018103},
{6,0.9739235846420741},
{7,0.973237033077876},
{8,0.9741297293248656}]
Using the same snapshots to calculate a total scheduler utilization:
> {A, T} = lists:foldl(fun({{_, A0, T0}, {_, A1, T1}}, {Ai,Ti}) -> {Ai + (A1 - A0), Ti + (T1 - T0)} end, {0, 0}, lists:zip(Ts0,Ts1)), TotalSchedulerUtilization = A/T.
0.9769136803764825
Total scheduler utilization will equal 1.0 when all schedulers have been active all the time between the two measurements.
Another (probably more) useful value is to calculate total scheduler utilization weighted against maximum amount of available CPU time:
> WeightedSchedulerUtilization = (TotalSchedulerUtilization * (erlang:system_info(schedulers) + erlang:system_info(dirty_cpu_schedulers))) / erlang:system_info(logical_processors_available).
0.9769136803764825
This weighted scheduler utilization will reach 1.0 when schedulers are active the same amount of time as maximum available CPU time. If more schedulers exist than available logical processors, this value may be greater than 1.0.
As of ERTS version 9.0, the Erlang runtime system will as default have more schedulers than logical processors. This due to the dirty schedulers.