[javascript] 자바 스크립트 세트 대 어레이 성능

세트가 Javascript에 비교적 새롭기 때문일 수 있지만 StackO 또는 다른 곳에서 Javascript에서 둘의 성능 차이에 대해 설명하는 기사를 찾을 수 없었습니다. 그렇다면 성능 측면에서 둘 사이의 차이점은 무엇입니까? 특히 제거, 추가 및 반복과 관련하여.



답변

좋아, 배열과 집합 모두에서 요소를 추가, 반복 및 제거하는 것을 테스트했습니다. 나는 10,000 개의 요소를 사용하는 “작은”테스트와 100,000 개의 요소를 사용하는 “큰”테스트를 실행했습니다. 결과는 다음과 같습니다.

컬렉션에 요소 추가

추가되는 요소의 수에 관계없이 .push배열 방법은 .addset 방법 보다 약 4 배 빠른 것 같습니다 .

컬렉션의 요소 반복 및 수정

테스트의이 부분에서는 for루프를 사용하여 배열 for of을 반복 하고 루프를 사용하여 세트를 반복했습니다. 다시 말하지만 어레이를 반복하는 것이 더 빨랐습니다. 이번에는 “작은”테스트에서는 두 배, “큰”테스트에서는 거의 4 배 더 오래 걸리므로 기하 급수적으로 보일 것입니다.

컬렉션에서 요소 제거

이제 이것이 흥미로워지는 곳입니다. 나는 for루프 의 조합을 .splice사용 for of하고 배열에서 일부 요소 .delete를 제거하고 세트에서 일부 요소를 사용 하고 제거했습니다. “작은”테스트의 경우 세트에서 항목을 제거하는 것이 약 3 배 더 빨랐지만 (2.6ms 대 7.1ms) “대형”테스트의 경우에는 항목을 배열에서 제거하는 데 1955.1ms가 소요되었지만 세트에서 제거하는 데 83.6ms가 걸렸습니다. 23 배 더 빨랐습니다.

결론

10k 요소에서 두 테스트 모두 비슷한 시간 (배열 : 16.6ms, 세트 : 20.7ms)을 실행했지만 100k 요소를 처리 할 때 세트가 확실한 승자였습니다 (배열 : 1974.8ms, 세트 : 83.6ms). 조작. 그렇지 않으면 어레이가 더 빨랐습니다. 그 이유를 정확히 말할 수 없었습니다.

배열이 생성되고 채워진 다음 일부 요소가 제거되는 집합으로 변환 된 다음 집합이 배열로 다시 변환되는 하이브리드 시나리오를 가지고 놀았습니다. 이렇게하면 배열에서 요소를 제거하는 것보다 훨씬 더 나은 성능을 제공 할 수 있지만 세트로 /에서 전송하는 데 필요한 추가 처리 시간이 세트 대신 배열을 채우는 이점보다 큽니다. 결국 세트 만 처리하는 것이 더 빠릅니다. 그럼에도 불구하고, 중복이없는 일부 빅 데이터에 대한 데이터 수집으로 어레이를 사용하기로 선택하면 하나에서 많은 요소를 제거해야하는 경우 성능 측면에서 유리할 수 있다는 것은 흥미로운 아이디어입니다. 배열을 집합으로 변환하고 제거 작업을 수행 한 다음 집합을 다시 배열로 변환합니다.

어레이 코드 :

var timer = function(name) {
  var start = new Date();
  return {
    stop: function() {
      var end = new Date();
      var time = end.getTime() - start.getTime();
      console.log('Timer:', name, 'finished in', time, 'ms');
    }
  }
};

var getRandom = function(min, max) {
  return Math.random() * (max - min) + min;
};

var lastNames = ['SMITH', 'JOHNSON', 'WILLIAMS', 'JONES', 'BROWN', 'DAVIS', 'MILLER', 'WILSON', 'MOORE', 'TAYLOR', 'ANDERSON', 'THOMAS'];

var genLastName = function() {
  var index = Math.round(getRandom(0, lastNames.length - 1));
  return lastNames[index];
};

var sex = ["Male", "Female"];

var genSex = function() {
  var index = Math.round(getRandom(0, sex.length - 1));
  return sex[index];
};

var Person = function() {
  this.name = genLastName();
  this.age = Math.round(getRandom(0, 100))
  this.sex = "Male"
};

var genPersons = function() {
  for (var i = 0; i < 100000; i++)
    personArray.push(new Person());
};

var changeSex = function() {
  for (var i = 0; i < personArray.length; i++) {
    personArray[i].sex = genSex();
  }
};

var deleteMale = function() {
  for (var i = 0; i < personArray.length; i++) {
    if (personArray[i].sex === "Male") {
      personArray.splice(i, 1)
      i--
    }
  }
};

var t = timer("Array");

var personArray = [];

genPersons();

changeSex();

deleteMale();

t.stop();

console.log("Done! There are " + personArray.length + " persons.")

코드 설정 :

var timer = function(name) {
    var start = new Date();
    return {
        stop: function() {
            var end  = new Date();
            var time = end.getTime() - start.getTime();
            console.log('Timer:', name, 'finished in', time, 'ms');
        }
    }
};

var getRandom = function (min, max) {
  return Math.random() * (max - min) + min;
};

var lastNames = ['SMITH','JOHNSON','WILLIAMS','JONES','BROWN','DAVIS','MILLER','WILSON','MOORE','TAYLOR','ANDERSON','THOMAS'];

var genLastName = function() {
    var index = Math.round(getRandom(0, lastNames.length - 1));
    return lastNames[index];
};

var sex = ["Male", "Female"];

var genSex = function() {
    var index = Math.round(getRandom(0, sex.length - 1));
    return sex[index];
};

var Person = function() {
	this.name = genLastName();
	this.age = Math.round(getRandom(0,100))
	this.sex = "Male"
};

var genPersons = function() {
for (var i = 0; i < 100000; i++)
	personSet.add(new Person());
};

var changeSex = function() {
	for (var key of personSet) {
		key.sex = genSex();
	}
};

var deleteMale = function() {
	for (var key of personSet) {
		if (key.sex === "Male") {
			personSet.delete(key)
		}
	}
};

var t = timer("Set");

var personSet = new Set();

genPersons();

changeSex();

deleteMale();

t.stop();

console.log("Done! There are " + personSet.size + " persons.")


답변

관찰 :

  • 집합 작업은 실행 스트림 내에서 스냅 샷으로 이해할 수 있습니다.
  • 우리는 확실한 대체물이 아닙니다.
  • Set 클래스 의 요소 에는 액세스 가능한 인덱스가 없습니다.
  • Set 클래스Array 클래스 보완으로, 기본 추가, 삭제, 검사 및 반복 작업을 적용 할 컬렉션을 저장해야하는 시나리오에서 유용합니다.

성능 테스트를 공유합니다. 콘솔을 열고 아래 코드를 복사하여 붙여 넣으십시오.

어레이 만들기 (125000)

var n = 125000;
var arr = Array.apply( null, Array( n ) ).map( ( x, i ) => i );
console.info( arr.length ); // 125000

1. 색인 찾기

Set의 has 메소드를 Array indexOf와 비교했습니다.

배열 / 같이 IndexOf (0.281ms) | 세트 / (0.053ms)을

// Helpers
var checkArr = ( arr, item ) => arr.indexOf( item ) !== -1;
var checkSet = ( set, item ) => set.has( item );

// Vars
var set, result;

console.time( 'timeTest' );
result = checkArr( arr, 123123 );
console.timeEnd( 'timeTest' );

set = new Set( arr );

console.time( 'timeTest' );
checkSet( set, 123123 );
console.timeEnd( 'timeTest' );

2. 새 요소 추가

Set 및 Array 개체의 추가 및 푸시 메서드를 각각 비교합니다.

어레이 / 푸시 (1.612ms) | 설정 / 추가 (0.006ms)

console.time( 'timeTest' );
arr.push( n + 1 );
console.timeEnd( 'timeTest' );

set = new Set( arr );

console.time( 'timeTest' );
set.add( n + 1 );
console.timeEnd( 'timeTest' );

console.info( arr.length ); // 125001
console.info( set.size ); // 125001

3. 요소 삭제

요소를 삭제할 때 배열과 집합이 동일한 조건에서 시작되지 않는다는 점을 명심해야합니다. Array에는 네이티브 메서드가 없으므로 외부 함수가 필요합니다.

배열 / deleteFromArr (0.356ms) | 설정 / 제거 (0.019ms)

var deleteFromArr = ( arr, item ) => {
    var i = arr.indexOf( item );
    i !== -1 && arr.splice( i, 1 );
};

console.time( 'timeTest' );
deleteFromArr( arr, 123123 );
console.timeEnd( 'timeTest' );

set = new Set( arr );

console.time( 'timeTest' );
set.delete( 123123 );
console.timeEnd( 'timeTest' );

여기 에서 전체 기사 읽기


답변

내 관찰은 큰 배열에 대해 두 가지 함정을 염두에두고 Set이 항상 더 낫다는 것입니다.

a) 배열에서 세트를 생성하는 작업 for은 미리 캐시 된 길이 의 루프 에서 수행되어야합니다 .

느림 (예 : 18ms) new Set(largeArray)

빠름 (예 : 6ms)
const SET = new Set();
const L = largeArray.length;
for(var i = 0; i<L; i++) { SET.add(largeArray[i]) }

b) 반복은 for of루프 보다 빠르기 때문에 같은 방식으로 수행 할 수 있습니다 .

참조 https://jsfiddle.net/0j2gkae7/5/를

에 실제 비교
difference(), intersection(), union()uniq()40.000 요소 (+ 자신의 iteratee 동반자 등)


답변

벤치마킹 된 반복 스크린 샷질문의 반복 부분에 대해 최근에이 테스트를 실행 한 결과 Set가 10,000 개 항목의 배열보다 훨씬 우수한 성능을 보였습니다 (동일한 시간 내에 작업이 약 10 배 발생할 수 있음). 그리고 브라우저에 따라 비슷한 테스트를 위해 Object.hasOwnProperty를이기거나 잃었습니다.

Set과 Object는 모두 O (1)로 상각되는 것처럼 보이는 “has”메서드를 가지고 있지만 브라우저의 구현에 따라 단일 작업이 더 오래 걸리거나 더 빨라질 수 있습니다. 대부분의 브라우저는 Set.has ()보다 빠르게 Object에 키를 구현하는 것 같습니다. 키에 대한 추가 검사를 포함하는 Object.hasOwnProperty조차도 Chrome v86에서 적어도 Set.has ()보다 약 5 % 빠릅니다.

https://jsperf.com/set-has-vs-object-hasownproperty-vs-array-includes/1

업데이트 : 2020 년 11 월 11 일 : https://jsbench.me/irkhdxnoqa/2

다른 브라우저 / 환경에서 자체 테스트를 실행하려는 경우.


마찬가지로 배열과 세트에 항목을 추가하고 제거하는 벤치 마크를 추가합니다.


답변

console.time("set")
var s = new Set()
for(var i = 0; i < 10000; i++)
  s.add(Math.random())
s.forEach(function(e){
  s.delete(e)
})
console.timeEnd("set")
console.time("array")
var s = new Array()
for(var i = 0; i < 10000; i++)
  s.push(Math.random())
s.forEach(function(e,i){
  s.splice(i)
})
console.timeEnd("array")

10K 항목에 대한 세 가지 작업은 다음과 같은 이점을 제공합니다.

set: 7.787ms
array: 2.388ms


답변