Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ members = [
"scheds/rust/scx_tickless",
"scheds/experimental/scx_flow",
"scheds/experimental/scx_rlfifo",
"scheds/experimental/scx_lunar",
"tools/scx_characterize",
"tools/scxtop",
"tools/scx_forge_agent",
Expand Down
22 changes: 22 additions & 0 deletions scheds/experimental/scx_lunar/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[package]
name = "scx_lunar"
version = "1.2.0"
authors = ["Timon Stipkovits <timon2201@gmail.com>"]
edition = "2021"
description = "Multi-queue all purpose scheduler with per-CPU/per-LLC dispatch queues"
license = "GPL-2.0-only"

[dependencies]
anyhow = "1"
ctrlc = { version = "3", features = ["termination"] }
clap = { version = "4", features = ["derive", "env", "unicode", "wrap_help"] }
scx_utils = { path = "../../../rust/scx_utils", version = "1.1.2" }
libbpf-rs = "=0.26.2"
log = "0.4"
simplelog = "0.12"

[build-dependencies]
scx_cargo = { path = "../../../rust/scx_cargo", version = "1.1.2" }

[features]
enable_backtrace = []
1 change: 1 addition & 0 deletions scheds/experimental/scx_lunar/LICENSE
81 changes: 81 additions & 0 deletions scheds/experimental/scx_lunar/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@

# Lunar

## Introduction

Scx_lunar is a multipurpose scheduler which was originally invented with the goal to make frametimes in games as smooth as possible
But then it grew a little and changed to a desktop usage focused scheduler which focuses on IO bound threads.

Which makes the scheduler one of the best when it comes to responsiveness.

This scheduler uses only FIFO queues.

## Explanation

The scheduler works with accounting of duty.

Duty goes from 0 to 1023.

The higher the duty number the more the task hogs cpu power.
The lower the more it is sleeping or dependent on io.

The duty is calculated from a window of the last 100ms

It is calculated like this.

duty = sleep_time * 1024 /(run_time + sleep_time + 1)

The Tier are calculated as Percent of the 1024 max duty value.

It has 5 tiers. Which are:

1. LC with duty <= 5%
2. INTERACTIVE with duty <= 20%
3. NORMAL with duty <= 40%
4. BATCH with duty <= 90%
5. GREEDY with duty <= 100%

All new tasks get thrown into greedy. And start with duty of 1023.
There is also a min. sample rate of the duty value to be eligible for promotion into higher tiers.

Each tier also has a slice time of 500us.

When a lower tier task is running at the moment a higher tier gets enqueued then the current task gets kicked and preempted.

## MODES

This scheduler also has 2 modes.

`--mode dsqs_per_llc`


Where the above explained are available for each LLC. So more than one core pull from the same DSQs.

and:

`--mode dsqs_per_cpu`

DEFAULT MODE!
Where the above explained dsqs are available for each cpu core. So each core has its own queues.
This mode is used automatically when starting without start parameters.

## Dispatch

For mode `dsqs_per_cpu`
Each core first tries to run its own queued tasks, then from another core from the same llc and then from core of other llcs.
From which core the core startes stealing is randomized for better load distribution.

for mode `dsqs_per_llc`
Each core tries to first to run from the dsqs of the llc from the core. Then it tries to steal from other llcs.

## Testing

There where 2 design goals for this scheduler.

1. That music keeps playing normally when executing the cachyos benchmarker https://github.com/CachyOS/cachyos-benchmarker
2. To keep frametimes as smooth as possible with as little frametime spikes as possible.

As far as i have tested. Both modes do accomplish these tasks very well.

The only problem is i couldn't test the functionality with different llcs as i don't have such an cpu by hand.
The next thing is, that i mostly developed this scheduler with SMT disabled. As i found that SMT off works the best for this ryzen 5800x3d. But you can test both. Your mileage may vary.
7 changes: 7 additions & 0 deletions scheds/experimental/scx_lunar/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
fn main() {
scx_cargo::BpfBuilder::new()
.unwrap()
.enable_skel("src/bpf/main.bpf.c", "bpf")
.build()
.unwrap();
}
57 changes: 57 additions & 0 deletions scheds/experimental/scx_lunar/src/bpf/datatypes.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// SPDX-License-Identifier: GPL-2.0
//
// Author: Timon Stipkovits <timon2201@gmail.com>
//
// This software may be used and distributed according to the terms of the
// GNU General Public License version 2.

#ifndef DATATYPES_H
#define DATATYPES_H
#include "defines.h"

const volatile u32 nr_llcs = 1;
const volatile u32 cpu_to_llc[MAX_CPUS] = {};
const volatile u32 schedulerMode = SCHED_MODE_DSQ_PER_CPU;

extern const int CONFIG_HZ __kconfig;

struct task_ctx
{
u64 current_dsq_type;
u64 runtime_avg;
u64 current_runtime;
u64 blocked_at;
u64 runnable_at;
s64 duty;
u64 run_acc;
u64 sleep_acc;
u64 last_run_granted_slice;
bool first_runtime_avg_sample_taken;
u64 started_at;
u64 duty_samples;
};

struct dispatch_ctx
{
u64 current_task_deadline;
u64 current_task_dsq_type;
u64 last_kick_timestamp;
};

struct
{
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
__uint(max_entries, 1);
__type(key, u32);
__type(value, struct dispatch_ctx);
} dispatch_state SEC(".maps");

struct
{
__uint(type, BPF_MAP_TYPE_TASK_STORAGE);
__uint(map_flags, BPF_F_NO_PREALLOC);
__type(key, int);
__type(value, struct task_ctx);
} task_ctx_stor SEC(".maps");

#endif // DATATYPES_H
99 changes: 99 additions & 0 deletions scheds/experimental/scx_lunar/src/bpf/defines.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// SPDX-License-Identifier: GPL-2.0
//
// Author: Timon Stipkovits <timon2201@gmail.com>
//
// This software may be used and distributed according to the terms of the
// GNU General Public License version 2.

#ifndef DEFINES_H
#define DEFINES_H

#define SCHED_MODE_DSQ_PER_LLC 0
#define SCHED_MODE_DSQ_PER_CPU 1

#define NS_PER_US 1000ULL
#define NS_PER_MS (1000ULL * NS_PER_US)

#define NS_PER_MS_LL (s64) NS_PER_MS

#define SLICE_LC (500 * NS_PER_US)
#define SLICE_INTERACTIVE (500 * NS_PER_US)
#define SLICE_NORMAL (500 * NS_PER_US)
#define SLICE_BATCH (500 * NS_PER_US)
#define SLICE_GREEDY (500 * NS_PER_US)

#define RUNTIME_PRIO_BOUNDARY_LC (50 * NS_PER_US)
#define RUNTIME_PRIO_BOUNDARY_INTERACTIVE (500 * NS_PER_US)
#define RUNTIME_PRIO_BOUNDARY_NORMAL (2000 * NS_PER_US)
#define RUNTIME_PRIO_BOUNDARY_BATCH (8000 * NS_PER_US)

#define VLAG_PRIO_BOUNDARY_LC (12 * NS_PER_MS_LL)
#define VLAG_PRIO_BOUNDARY_INTERACTIVE (8 * NS_PER_MS_LL)
#define VLAG_PRIO_BOUNDARY_NORMAL (4 * NS_PER_MS_LL)
#define VLAG_PRIO_BOUNDARY_BATCH (0 * NS_PER_MS_LL)

#define MIN_RUN_BEFORE_PREEMPT 20000LL

#define RUNTIME_THRESH_PERCENT 5LL
#define VLAG_THRESH_PERCENT 5LL

#define DUTY_WINDOW_NS (100ULL * NS_PER_MS)
#define TASK_CLASSIFICATION_AGE_NS (50ULL * NS_PER_MS)
#define DUTY_SAMPLES_NEEDED 10
#define DUTY_SAMPLES_MAX 1000

#define DUTY_RANGE 1024

#define DUTY_EDGE_LC ((5 * DUTY_RANGE) / 100)
#define DUTY_EDGE_INTERACTIVE ((20 * DUTY_RANGE) / 100)
#define DUTY_EDGE_NORMAL ((40 * DUTY_RANGE) / 100)
#define DUTY_EDGE_BATCH ((90 * DUTY_RANGE) / 100)

#define DUTY_HYST_HIGH ((5 * DUTY_RANGE) / 100)
#define DUTY_HYST ((3 * DUTY_RANGE) / 100)

#define MAX_RT_PRIO 100

#define MIN_AVG_RUNTIME 50ULL
#define MAX_RUNTIME_PER_TASK (400 * NS_PER_MS)

#define HISTORIC_TASK_SAMPLES 4
#define AVG_RUNTIME_OVERRIDE_FACTOR 5

#define DSQ_TYPE_LC 1
#define DSQ_TYPE_INTERACTIVE 2
#define DSQ_TYPE_NORMAL 3
#define DSQ_TYPE_BATCH 4
#define DSQ_TYPE_GREEDY 5
#define DSQ_PRIO_QUEUE_AMOUNT 5
#define DSQ_TYPE_EMPTY 6

#define VLAG_PROMOTE_THRESH 2000000LL
#define VLAG_DEMOTE_THRESH -2000000LL

#define VLAG_MIN -14000000LL
#define VLAG_MAX 14000000LL
#define SLEEP_CREDIT_DIVISOR 1LL
#define MAX_CREDITABLE_SLEEP 100000000LL

#define DEFAULT_DSQ_LOCAL_ON 0xC000000000000000ULL

#define DSQ_CPU_QUEUE_BASE_LC 1536
#define DSQ_CPU_QUEUE_BASE_INTERACTIVE 2048
#define DSQ_CPU_QUEUE_BASE_NORMAL 2560
#define DSQ_CPU_QUEUE_BASE_BATCH 3072
#define DSQ_CPU_QUEUE_BASE_GREEDY 3584

#define DSQ_LLC_QUEUE_BASE_LC 4224
#define DSQ_LLC_QUEUE_BASE_INTERACTIVE 4288
#define DSQ_LLC_QUEUE_BASE_NORMAL 4352
#define DSQ_LLC_QUEUE_BASE_BATCH 4416
#define DSQ_LLC_QUEUE_BASE_GREEDY 4480

#define QUEUE_START DSQ_TYPE_GREEDY

#define AVG_RUNTIME_START (500 * NS_PER_US)

#define MAX_CPUS 512

#endif // DEFINES_H
Loading