크래프톤 정글 TIL : 0831

lazyArtisan·2024년 8월 31일
0

정글 TIL

목록 보기
62/147

🖥️ PintOS

🔷 Advanced scheduler


sleep_list, lock_list에 있는 놈들까지도 전부 더해줘야 함 (ready_list에 있는 것만이 아님)

thread_create에서 all_thread_list에 추가
do_schedule이나 schedule에서 지워버리기 전에 all_thread_list에서 삭제
-> 이렇게 했더니 list 순회할 때 터져버림. 다 삭제. list가 외부에 있어서 그런건가? 했는데 ready_list도 원래 외부 리스트임. 그것 때문에 터진 건 아님.

all_thread_elem을 위한 elem을 만들어야 list_remove할 때 순회가 안 터질거라고 함.

recent cpu를 일괄적으로 0으로 만들면 안되고 nice 값을 반영해야 함!
negative recent cpu를 0으로 clamp하는 식은 안됨.

혹시나 해서 set_nice의 unused 삭제 -> 의미 없었음

thread_calculate_priority에서 63 초과 0 미만 값들 각각 63과 0으로 clamp
-> mlfqs-nice-2 해결.

버전 관리하다가 고쳤던거 다시 되돌려진 1틱마다 recent_cpu 증가할 때 (1 << 14) 더해줘야 하는거 다시 1에서 (1 << 14)로 바꿈 -> 헛소리. ready_list는 list_size로 계산했다. 그냥 대놓고 정수. 고정 소수점 아님.

all_thread_list는 모든 쓰레드의 recent_cpu랑 priority를 갱신할 때만 쓰는 거고,
recent_cpu 계산할 땐 ready_list만? -> 맞긴 한데 여전히 통과 못함

4틱마다 모든 쓰레드 pirority 재계산 할 때 현재 실행중인 쓰레드의 우선순위가 낮으면 yield()하는 check_priority_and_yield() 함수 넣었는데, 생각해보니 timer_interrupt는 interrupt니까 yield에 있는 ASSERT(!intr_context());를 통과 못함.

// 현재 실행중인 쓰레드의 우선순위가 낮으면 yield();
void check_priority_and_yield(void)
{
	if (!list_empty(&ready_list))
	{
		list_sort(&ready_list, for_descending_priority, NULL);								   // 혹시나 해서 넣었지만 효율에 안 좋음. 빼보기.
		struct thread *maxE_thread = list_entry(list_front(&ready_list), struct thread, elem); // 최대 priority 가진 쓰레드
		int maxE_priority = maxE_thread->priority;											   // 대기 리스트에서 최대 priority

		if (thread_current()->priority < maxE_priority) // 대기 최대 priority가 현재 priority보다 크다면
			if (!thread_mlfqs)
				thread_yield(); // 현재 쓰레드 꺼버리기 (thread_yield도 수정함)
			else
				intr_yield_on_return(); // mlfqs면 timer 인터럽트에서 이 함수 부른다
	}
}

intr_yield_ont_return을 넣어줌. 근데 이러디까 다 오류남.
굳이 할 필요 없었는듯.

/* Starts 60 threads that each sleep for 10 seconds, then spin in
   a tight loop for 60 seconds, and sleep for another 60 seconds.
   Every 2 seconds after the initial sleep, the main thread
   prints the load average.

각 테케 위에 설명 주석 달려있음. 진작 읽어볼걸.

gpt한테 load-60 주석 설명 해달라고 해봄.

  1. 60개의 스레드 생성: 프로그램은 60개의 스레드를 생성합니다. 스레드는 경량 프로세스처럼 작동하며, 각 스레드는 독립적인 실행 흐름을 가지고 있습니다.

  2. 각 스레드가 10초 동안 sleep: 생성된 각 스레드는 처음에 10초 동안 잠을 잡니다. sleep 상태는 CPU를 사용하지 않고 기다리는 상태를 의미합니다.

  3. 60초 동안 바쁘게 루프를 돌기: 스레드가 깨어나면, 60초 동안 "tight loop"에 진입합니다. 이 루프는 스레드가 계속해서 CPU를 사용하도록 만드는 바쁜 대기 상태입니다. "tight loop"은 아무 작업도 하지 않고 계속해서 루프를 반복하는 상태를 의미하며, CPU를 점유합니다.

  4. 다시 60초 동안 sleep: 60초 동안 바쁘게 루프를 돌고 난 후, 각 스레드는 다시 60초 동안 잠에 들어갑니다.

  5. 메인 스레드는 2초마다 load average를 출력: 스레드들이 작업을 수행하는 동안, 메인 스레드는 매 2초마다 load average를 출력합니다. load average는 시스템에 몇 개의 스레드가 활성 상태(실행 중 또는 대기 중)인지 나타내는 값으로, 일반적으로 시스템의 부하를 측정하는 데 사용됩니다.

ready_thread 최적화

		/* 1초마다 모든 쓰레드의 recent_cpu 갱신 */
		if (timer_ticks() % TIMER_FREQ == 0)
		{
			calculate_ready_thread();

			for (e = list_begin(&all_thread_list); e != list_end(&all_thread_list); e = list_next(e))
			{
				t = list_entry(e, struct thread, all_elem);
				update_recent_cpu(t);
			}
		}
void calculate_ready_thread(void)
{
	ready_threads = list_size(&ready_list);
	if (thread_current() != idle_thread)
		ready_threads = ready_threads + 1;

	load_avg = MUL_F(DIV_F(I_T_F(59), I_T_F(60)), load_avg) + MUL_F(DIV_F(I_T_F(1), I_T_F(60)), I_T_F(ready_threads));
}

/* 현재 쓰레드의 recent_cpu 계산 */
void update_recent_cpu(struct thread *t)
{
	// recent_cpu 계산
	t->recent_cpu = MUL_F(DIV_F(MUL_F(I_T_F(2), load_avg), SUB_F_I(MUL_F(I_T_F(2), load_avg), 1)), ADD_F_I(t->recent_cpu, t->nice));
}

ready_thread를 쓰레드마다 똑같은 값을 매번 계산하던 걸 순회 전에 한 번만 계산하게 바꿔줌.

load_avg 과계산 수정

엄청 큰 오류 수정.

load_avg는 전역 변수. 각 쓰레드마다 갖고 있는게 아님.
어제 듣고 고치긴 했는데 로직은 안 고쳤었음.
calculate_ready_thread 안에 넣어줌.

mlfqs-load-60 통과함.

recent_cpu 과계산?

FAIL tests/threads/mlfqs/mlfqs-recent-1
Some recent_cpu values were missing or differed from those expected by more than 2.5.
  time   actual <-> expected explanation
------ -------- --- -------- ----------------------------------------
     2    undef     6.40     Missing value.
     4    undef     12.60    Missing value.
     6    undef     18.61    Missing value.
     8    undef     24.44    Missing value.
    10    undef     30.08    Missing value.
    12    undef     35.54    Missing value.
    14    90.56 >>> 40.83    Too big, by 47.23.
    16   290.56 >>> 45.96    Too big, by 242.10.
    18   490.56 >>> 50.92    Too big, by 437.14.
    20   690.56 >>> 55.73    Too big, by 632.33.
    22   890.56 >>> 60.39    Too big, by 827.67.
    24  1090.56 >>> 64.90    Too big, by 1023.16.
    26  1290.56 >>> 69.27    Too big, by 1218.79.
    28    undef     73.50    Missing value.
    30    undef     77.60    Missing value.
    32    undef     81.56    Missing value.
    34    undef     85.40    Missing value.
    36    undef     89.12    Missing value.
    38    undef     92.72    Missing value.
    40    69.12 <<< 96.20    Too small, by 24.58.
    42   269.12 >>> 99.57    Too big, by 167.05.
    44   469.12 >>> 102.84   Too big, by 363.78.
    46   669.12 >>> 106.00   Too big, by 560.62.
    48   869.12 >>> 109.06   Too big, by 757.56.
    50  1069.12 >>> 112.02   Too big, by 954.60.
    52  1269.12 >>> 114.89   Too big, by 1151.73.

recent_cpu가 기하급수적으로 커짐.

t->recent_cpu = MUL_F(DIV_F(MUL_F(I_T_F(2), load_avg), SUB_F_I(MUL_F(I_T_F(2), load_avg), 1)), ADD_F_I(t->recent_cpu, t->nice)); 아 recent cpu 식 잘못됨. nice를 맨 마지막에 더했어야 함.
아니 그리고 1 더해야 되는걸 왜 -1로 계산함?

그 후로 많은 시간이 지나고...

많은 변경사항이 있었음.

이리저리 고쳐보다가

recent-1이라는 통곡의 벽에 가로막혀 하루를 통으로 날림.
이거빼고 다 됨.

먼저 한 사람들이 같이 많이 봐줬는데도 여기까지밖에 못함.

다른 사람 정답 코드 보고 뭐가 다른건지 하나하나 조지면 될듯.

라고 생각했는데

나머지 테스트들 주석 풀고 다시 해봤더니 코드 고친 것도 없는데 갑자기 올패스 뜸.
???????

ctrl+z 눌러보니 list_push_back(&all_thread_list, &t->all_elem); 이걸
thread_create에서 하는게 아니라 그 안에서 호출하는 init_thread로 위치를 옮겨줬었음.
이것 때문이라고?? 진짜??

대조 실험 해보니까 진짜 이것 때문 맞음...

0개의 댓글