시간을 병렬화할 수 있는 방법이 있습니까?바둑에서 효과적인 실행 시간을 지키면서 잠은 자나요?
저는 Golang에서 느린 쿼리 로그 재생기와 관련된 느린 쿼리 로그 파서 패키지를 개발하고 있습니다.재생기의 경우 다음 코드를 사용합니다(가독성을 위해 주석을 추가했습니다).
for {
// method from my package that returns a Query object, containing headers values
// and the query itself
q := p.GetNext()
if q == (query.Query{}) {
break
}
db.logger.Tracef("query: %s", q.Query)
// we send the SQL query to a chan that is read by workers.
// workers just execute the query on the database, that's all.
// results from the db query are handled in another piece of the code, it doesn't really
// matter here
queries <- q.Query
// We need a reference time
if firstPass {
firstPass = false
previousDate = q.Time
continue
}
// Time field contains the Time: field value in the query header
now := q.Time
sleeping := now.Sub(previousDate)
db.logger.Tracef("next sleeping time: %s", sleeping)
time.Sleep(sleeping) // Here is my issue. For MariaDB the value is < 0 so no sleep is done
// For MariaDB, when there is multiple queries in a short amount of
// time, the Time field is not repeated, so we do not have to update
// the previous date.
if now != (time.Time{}) {
previousDate = now
}
}
저는 흥미로운 문제에 부딪혔습니다.MariaDB 느린 쿼리 로그의 경우 두 개 이상의 쿼리가 서로 근접하면 헤더에 시간: 필드가 없으므로 다음과 같은 필드 수가 줄어듭니다.time.Sleep(sleeping)
이전 코드 조각에 있습니다.그러나 MySQL 스타일의 느린 쿼리 로그에서는 쿼리 헤더에 항상 시간: 필드가 있습니다. 이는 각 쿼리에 대해 절전 모드가 수행됨을 의미합니다(의 절전 모드 기간에도 마찬가지임).
MariaDB와 MySQL 로그 간의 재생 시간 차이가 매우 크다는 것을 알게 되었습니다. MariaDB 재생 시간은 실시간(로그 파일의 첫 번째 쿼리와 마지막 쿼리 간의 시간 차이)과 상당히 비슷했지만 MySQL 재생 시간은 IRL보다 상당히 높았습니다.가지고 놀고 난 후에pprof
나는 문제가 발생했다는 것을 알아차렸습니다.time.Sleep
특히 로부터runtime.Futex
CPU 시간이 많이 소요됩니다.
벤치마킹을 좀 해봤는데, 기간 결과가 고객의 수와 관련이 있습니다.time.Sleep
완료되었습니다(MariaDB보다 MySQL에서 더 높음).
그래서 모든 것을 하는 대신에.time.Sleep
단일 스레드에서 유효 시간을 변경하지 않고 병렬로 실행할 수 있는 다른 방법을 찾고 있지만, 이 방법을 찾을 수 없습니다.
제가 제시한 해결책은 다음과 같습니다.
type job struct {
query string
idle time.Time
}
...
var reference time.Time
start := time.Now()
for {
q := p.GetNext()
if q == (query.Query{}) {
s.Stop()
break
}
db.logger.Tracef("query: %s", q.Query)
r.queries++
s.Suffix = " queries replayed: " + strconv.Itoa(r.queries)
// We need a reference time
if firstPass {
firstPass = false
reference = q.Time
}
var j job
delta := q.Time.Sub(reference)
j.idle = start.Add(delta)
j.query = q.Query
db.logger.Tracef("next sleeping time: %s", j.idle)
jobs <- j
}
...
func (db database) worker(jobs chan job, errors chan error, wg *sync.WaitGroup) {
defer wg.Done()
for {
j, ok := <-jobs
if !ok {
db.logger.Trace("channel closed, worker exiting")
return
}
sleep := time.Until(j.idle)
if sleep > 0 {
time.Sleep(sleep)
}
rows, err := db.drv.Query(j.query)
if err != nil {
errors <- err
db.logger.Debugf("failed to execute query:\n%s\nerror: %s", j.query, err)
}
if rows != nil {
rows.Close()
}
}
}
설명:
프로그램 시작을 변수에 저장합니다(여기).start
. 다음으로 기준 시간을 설정합니다.reference
느린 쿼리 로그 파일의 첫 번째 타임스탬프입니다.절대 변하지 않을 것입니다.
그런 다음 각 새로운 쿼리에서 다음 기간을 계산합니다.reference
및 현재 쿼리 타임스탬프q.Time
저장해 두자delta
.
추가합니다delta
로.start
타임라인에 타임스탬프가 있습니다(느린 쿼리 로그 파일에서처럼 과거에는 없음).우리는 이 타임스탬프를 우리가 만든 새로운 구조의 쿼리 옆에 있는 작업자에게 보냅니다.job
.
작업자가 채널을 통해 작업을 수신하면 쿼리를 수행할 수 있을 때까지 기다리는 시간을 계산합니다.<= 0이면 쿼리를 즉시 실행하고, 그렇지 않으면 대기합니다.
언급URL : https://stackoverflow.com/questions/66982240/is-there-a-way-to-parallelise-time-sleep-but-keeping-the-effective-execution-tim
'source' 카테고리의 다른 글
URL에서 파일이 있는지 확인하는 방법 (0) | 2023.08.18 |
---|---|
jdiv 내의 요소만 직렬화하기 위한 쿼리 (0) | 2023.08.18 |
값 오류: 알 수 없는 투영 '3d' (0) | 2023.08.18 |
addEventListener 및 getElementsByClassName을 사용하여 요소 ID 전달 (0) | 2023.08.18 |
jquery/javascript를 통해 추가하는 방법은 무엇입니까? (0) | 2023.08.18 |