리팩터링 2판을 읽다 궁금해서 해보고 싶은 것들이 있었다.
나중에 해봐야지 해봐야지 하다가 오늘 드디어 해봤고...
이 글이 도움이 될지.... 다른 사람들이 볼지... 는 모르겠지만, 혹시 나처럼 궁금한 사람들도 있을 수도 있지 않을까?
하는 생각으로 블로그에 작성하기로 했다.
확인해보고 싶었던 부분
1. 반복문을 분리해서 두 번 실행하는 방식으로 로직을 나눠도 성능에 큰 영향은 없다.
2. 반복문을 파이프라인 방식으로 변경하기
1번에서 궁금한건 그냥 진짜 한 번에 하는 거랑 분리해서 두 번 하는 거랑 큰 차이가 없나? 이 정도였고,
2번이 궁금한건 파이프라인 방식으로 filter와 map을 이어 붙이는 방식과 for문 방식의 처리 속도 차이를 직접 확인해보고 싶었다.
궁금한 건 또 직접 해서 눈으로 봐야 더 오래 기억에 남기도 하기 때문에 한 번 테스트해봤다.
테스트 데이터
데이터는 ElasticSearch에 있는 데이터 50만 개를 가지고와서 테스트를 했다.
회사에 있는 데이터라 데이터를 여기에 올릴 순 없고....
한 개의 도큐먼트에 28개의 필드로 구성되어 있고, 11개는 문자열 데이터, 나머지 17개는 정수형 데이터로 되어있다.
데이터 50만개 가져오기
async function getData() {
try {
const query = {
"size":10000,
"query":{
"term":{
"type":"flw"
}
}
}
let rs = await es_client.search({
index: process.env.index_name,
body: query,
scroll: '1m'
})
let count = 0;
const data = [];
while(count < 500000) {
count += rs.hits.hits.length;
rs.hits.hits.reduce( (acc, doc, idx) => {
acc.push(doc._source);
return acc;
}, data);
rs = await es_client.scroll({ scrollId: rs._scroll_id, scroll: '1m'});
}
return data;
} catch (err) {
console.error('getData Error : ', err);
}
}
데이터 50만개를 가져오는 건 scroll 방식으로 데이터를 가져왔다.
해당 인덱스에 총 72만 개가 있는데 50만 개만 가져오게 하기 위해 이렇게 작성했다.
테스트 1
우선 반복문 1개 안에다 50만 개의 데이터 중 특정 조건의 데이터만 일정 필드, 데이터를 배열에 담는 소스다.
async function forTest() {
try {
const result = [];
const data = await getData();
console.time('forTest');
console.log('forTest data.length : ', data.length);
for (let item of data) {
if (item.dir === 1) {
result.push({
create_date: item.create_date,
time_start: item.time_start,
time_end: item.time_end
})
}
}
console.log('forTest result length : ', result.length);
console.timeEnd('forTest');
} catch (err) {
console.error(err);
}
}
테스트 2
반복문을 두 개로 쪼개서 처음엔 특정 조건의 데이터만 추출하고,
다음엔 추출된 데이터에서 일정 필드, 데이터만 배열에 담았다.
async function forNfor() {
try {
const result = [];
const data = await getData();
console.time('forNfor');
console.log('forNfor data.length : ', data.length);
const tmp = [];
for (let item of data) {
if (item.dir === 1) {
tmp.push(item);
}
}
for (let item of tmp) {
result.push({
create_date: item.create_date,
time_start: item.time_start,
time_end: item.time_end
})
}
console.log('forNfor result length : ', result.length);
console.timeEnd('forNfor');
} catch (err) {
console.error(err);
}
}
테스트 3
파이프라인 방식으로 한방에...
filter에서 특정 조건의 데이터만 추출하고 map에서 특정 필드, 데이터만 추출해서 변수에 담는다.
async function pipelineTest() {
try {
const data = await getData();
console.time('pipelineTest');
console.log('pipelineTest data.length : ', data.length);
const result = data.filter(x => x.dir === 1).map( item => {
return {
create_date: item.create_date,
time_start: item.time_start,
time_end: item.time_end
}
})
console.log('pipelineTest result length : ', result.length);
console.timeEnd('pipelineTest');
} catch (err) {
console.error(err);
}
}
이걸 이어 붙인다고 해서 파이프라인 패턴??이라고 하는 것 같은데
정확한 단어를 알고 싶은데 구글링 해도 다 두루뭉실한 글들 뿐이라 좀 알고싶다...
결과
위 함수들을 모두 총 5번 정도 실행한 결과는 이렇다.
첫 번째
pipelineTest data.length : 500000
pipelineTest result length : 470460
pipelineTest: 188.445ms
forTest data.length : 500000
forTest result length : 470460
forTest: 120.63ms
forNfor data.length : 500000
forNfor result length : 470460
forNfor: 150.945ms
두 번째
forTest data.length : 500000
forTest result length : 470460
forTest: 106.795ms
forNfor data.length : 500000
forNfor result length : 470460
forNfor: 165.224ms
pipelineTest data.length : 500000
pipelineTest result length : 470460
pipelineTest: 127.374ms
세 번째
forTest data.length : 500000
forTest result length : 470460
forTest: 107.754ms
pipelineTest data.length : 500000
pipelineTest result length : 470460
pipelineTest: 159.283ms
forNfor data.length : 500000
forNfor result length : 470460
forNfor: 150.979ms
네 번째
forTest data.length : 500000
forTest result length : 470460
forTest: 120.564ms
pipelineTest data.length : 500000
pipelineTest result length : 470460
pipelineTest: 161.398ms
forNfor data.length : 500000
forNfor result length : 470460
forNfor: 174.012ms
다섯 번째
pipelineTest data.length : 500000
pipelineTest result length : 470460
pipelineTest: 164.288ms
forTest data.length : 500000
forTest result length : 470460
forTest: 91.212ms
forNfor data.length : 500000
forNfor result length : 470460
forNfor: 152.408ms
~ data.length는 총 데이터 개수 (50만 개)
~ result length는 조건문에 추출된 개수 (약 47만 개)
~ms는 걸린 시간
전체적으로 for문 하나로 처리하는 게 다른 2개 보단 더 빠르긴 했고, for문 쪼갠 것과 파이프라인은 서로 빨랐다 느렸다 했다.
차이가 있긴 하지만 다 0초대 차이라 어떤게 더 좋다, 뭐가 더 나쁘다를 판단할 정도는 아닌 것 같다는 생각이 든다.
그리고 5만 개로도 해봤다.
forTest data.length : 50000
forTest result length : 47434
forTest: 47.699ms
pipelineTest data.length : 50000
pipelineTest result length : 47434
pipelineTest: 42.945ms
forNfor data.length : 50000
forNfor result length : 47434
forNfor: 48.807ms
===
forTest data.length : 50000
forTest result length : 47434
forTest: 52.445ms
forNfor data.length : 50000
forNfor result length : 47434
forNfor: 53.992ms
pipelineTest data.length : 50000
pipelineTest result length : 47434
pipelineTest: 48.49ms
===
forNfor data.length : 50000
forNfor result length : 47434
forNfor: 59.962ms
forTest data.length : 50000
forTest result length : 47434
forTest: 45.359ms
pipelineTest data.length : 50000
pipelineTest result length : 47434
pipelineTest: 113.549ms
===
forTest data.length : 50000
forTest result length : 47434
forTest: 43.675ms
pipelineTest data.length : 50000
pipelineTest result length : 47434
pipelineTest: 46.917ms
forNfor data.length : 50000
forNfor result length : 47434
forNfor: 56.644ms
===
forTest data.length : 50000
forTest result length : 47434
forTest: 37.513ms
pipelineTest data.length : 50000
pipelineTest result length : 47434
pipelineTest: 45.834ms
forNfor data.length : 50000
forNfor result length : 47434
forNfor: 49.511ms
이때는 for문 한 개가 빠를 때도 있고, 파이프라인이 빠를 때도 있고, for문 쪼갠 게 빠를 때도 있고...
마치며.
나도 개인적으로 for문 하나로 하는 방식으로만 많이 했었고, 나중에야 소스가 더럽다 느껴져서 filter, reduce, map 등 함수를 활용해 처리하는 걸 선호하게 되었다. (for문으로만 해야 하는 건 어쩔 수 없고...)
특히 for문을 쪼개서 한다는 건 그냥 내 상식으로는 당연히 더 느릴 거라 생각했기에 쪼개서 사용해보겠다는 걸 생각도 안 해봤었다.
책에서도 쪼개는게 나중에 로직이 복잡해질 때 관리하는 차원에서 더 수월하기에 쪼개는 것도 방법이라고 했다.
나중에 이 쪼갠 로직이 병목 현상을 일으킨다면 다시 합치면 된다고...ㅋㅋ
그래도 쪼갠걸로 병목 현상이 발생하는 경우는 매우 드물다고 한다.
'JavaScript' 카테고리의 다른 글
[JS] 배열내 중복데이터 체크하기 - set() 함수 (0) | 2021.08.12 |
---|---|
[JavaScript] Uncaught TypeError: Assignment to constant variable. (0) | 2021.03.23 |
[JavaScript] Uncaught SyntaxError: Missing initializer in const declaration (0) | 2021.03.23 |
[JavaScript] 자식창에서 부모창 함수 호출 (0) | 2020.11.05 |
[JavaScript] 정규식 (0) | 2020.01.03 |