vector :: iterator 또는 at ()을 사용하여 STL 벡터를 반복하는 것이 더 빠릅니다.
성능면에서 무엇이 더 빨리 작동할까요? 차이가 있습니까? 플랫폼에 따라 다릅니 까?
//1. Using vector<string>::iterator:
vector<string> vs = GetVector();
for(vector<string>::iterator it = vs.begin(); it != vs.end(); ++it)
{
*it = "Am I faster?";
}
//2. Using size_t index:
for(size_t i = 0; i < vs.size(); ++i)
{
//One option:
vs.at(i) = "Am I faster?";
//Another option:
vs[i] = "Am I faster?";
}
테스트를 작성하고 알아보십시오.
편집 : 내 잘못입니다. 최적화 된 버전의 타이밍을 맞추고 있다고 생각했지만 그렇지 않았습니다. 내 컴퓨터에서 g ++ -O2로 컴파일 된 경우 반복기 버전이 operator [] 버전보다 약간 느리지 만 그다지 크지는 않습니다.
#include <vector>
#include <iostream>
#include <ctime>
using namespace std;
int main() {
const int BIG = 20000000;
vector <int> v;
for ( int i = 0; i < BIG; i++ ) {
v.push_back( i );
}
int now = time(0);
cout << "start" << endl;
int n = 0;
for(vector<int>::iterator it = v.begin(); it != v.end(); ++it) {
n += *it;
}
cout << time(0) - now << endl;
now = time(0);
for(size_t i = 0; i < v.size(); ++i) {
n += v[i];
}
cout << time(0) - now << endl;
return n != 0;
}
반복자를 사용하면 포인터가 증가 (증가)되고 역 참조가 포인터 역 참조가됩니다.
인덱스를 사용하면 증가 속도도 똑같이 빨라야하지만 요소 검색에는 추가 (데이터 포인터 + 인덱스)와 해당 포인터 역 참조가 포함되지만 그 차이는 미미합니다.
at()
또한 인덱스가 범위 내에 있는지 확인하므로 속도가 느려질 수 있습니다.
500M 반복, 벡터 크기 10, gcc 4.3.3 (-O3), linux 2.6.29.1 x86_64 :
at()
: 9158ms
operator[]
: 4269ms
iterator
: 3914ms에 대한 벤치 마크 결과
YMMV이지만 색인을 사용하면 코드를 더 읽기 쉽고 이해하기 쉽게 만들 수 있습니다.
인덱싱이 필요하지 않으면 사용하지 마십시오. 반복기 개념은 최선을 다합니다. 반복자는 최적화하기가 매우 쉬우 며 직접 액세스에는 추가 지식이 필요합니다.
인덱싱은 직접 액세스를 의미합니다. 대괄호와 at
메서드가이를 수행합니다. at
과 달리 []
범위를 벗어난 인덱싱을 확인하므로 속도가 느려집니다.
신조는 : 필요하지 않은 것을 요구하지 마십시오. 그러면 컴파일러는 사용하지 않는 것에 대해 비용을 청구하지 않습니다.
효율성을 고려하고 있으므로 다음 변형이 잠재적으로 더 효율적이라는 것을 인식해야합니다.
//1. Using vector<string>::iterator:
vector<string> vs = GetVector();
for(vector<string>::iterator it = vs.begin(), end = vs.end(); it != end; ++it)
{
//...
}
//2. Using size_t index:
vector<string> vs = GetVector();
for(size_t i = 0, size = vs.size(); i != size; ++i)
{
//...
}
end / size 함수는 루프를 통해 매번 호출되지 않고 한 번만 호출되기 때문입니다. 어쨌든 컴파일러가 이러한 함수를 인라인 할 가능성이 있지만 이렇게하면 확실합니다.
여기있는 모든 사람들이 말했듯이 벤치 마크를 수행하십시오.
즉, at ()도 범위 검사를 수행하기 때문에 반복기가 더 빠르다고 주장합니다. 즉, 인덱스가 범위를 벗어난 경우 out_of_range 예외가 발생합니다. 그 검사 자체는 아마도 약간의 오버 헤드를 유발할 것입니다.
첫 번째 변형이 더 빠르다고 생각합니다.
그러나 구현에 따라 다릅니다. 자신의 코드를 프로파일 링해야합니다.
왜 자신의 코드를 프로파일 링합니까?
이러한 요인은 모두 결과에 차이가 있기 때문입니다.
- 어떤 OS
- 어떤 컴파일러
- 사용 된 STL 구현
- 최적화가 켜져 있었습니까?
- ... (기타 요인)
인덱스 액세스는 장면 뒤에서 반복기를 생성하기 때문에 첫 번째는 디버그 모드에서 더 빠르지 만 모든 것이 인라인되어야하는 릴리스 모드에서는 차이가 무시할 수 있거나 null이어야합니다.
이 테스트 코드를 사용하여 결과를 비교할 수 있습니다! Dio it!
#include <vector>
#include <iostream>
#include <ctime>
using namespace std;;
struct AAA{
int n;
string str;
};
int main() {
const int BIG = 5000000;
vector <AAA> v;
for ( int i = 0; i < BIG; i++ ) {
AAA a = {i, "aaa"};
v.push_back( a );
}
clock_t now;
cout << "start" << endl;
int n = 0;
now = clock();
for(vector<AAA>::iterator it = v.begin(); it != v.end(); ++it) {
n += it->n;
}
cout << clock() - now << endl;
n = 0;
now = clock();
for(size_t i = 0; i < v.size(); ++i) {
n += v[i].n;
}
cout << clock() - now << endl;
getchar();
return n != 0;
}
실제로 수행하는 작업에 따라 다르지만 반복기를 계속 다시 선언해야하는 경우 반복기가 MARGINALLY SLOWER가됩니다. 내 테스트에서 가능한 가장 빠른 반복은 벡터 배열에 간단한 *를 선언하고이를 통해 반복하는 것입니다.
예를 들면 :
벡터 반복 및 패스 당 두 개의 함수를 가져옵니다.
vector<MyTpe> avector(128);
vector<MyTpe>::iterator B=avector.begin();
vector<MyTpe>::iterator E=avector.end()-1;
for(int i=0; i<1024; ++i){
B=avector.begin();
while(B!=E)
{
float t=B->GetVal(Val1,12,Val2); float h=B->GetVal(Val1,12,Val2);
++B;
}}
벡터 90 회 클릭 (0.090000 초)
하지만 포인터로했다면 ...
for(int i=0; i<1024; ++i){
MyTpe *P=&(avector[0]);
for(int i=0; i<avector.size(); ++i)
{
float t=P->GetVal(Val1,12,Val2); float h=P->GetVal(Val1,12,Val2);
}}
Vector는 18 번 클릭 (0.018000 초)
대략 다음과 같습니다.
MyTpe Array[128];
for(int i=0; i<1024; ++i)
{
for(int p=0; p<128; ++p){
float t=Array[p].GetVal(Val1, 12, Val2); float h=Array[p].GetVal(Val2,12,Val2);
}}
어레이 클릭 15 회 (0.015000 초)가 걸렸습니다.
avector.size () 호출을 제거하면 시간이 동일 해집니다.
마지막으로 []로 호출
for(int i=0; i<1024; ++i){
for(int i=0; i<avector.size(); ++i){
float t=avector[i].GetVal(Val1,12,Val2); float h=avector[i].GetVal(Val1,12,Val2);
}}
벡터 33 회 클릭 (0.033000 초)
clock ()으로 시간 제한
때에 따라 다르지.
대답은 기존 답변보다 훨씬 더 미묘합니다.
at
항상 반복자 또는 operator[]
.
그러나 operator[]
반복자 대 반복기의 경우 다음에 따라 다릅니다.
정확히 어떻게 당신이 사용하고 있습니다
operator[]
.특정 CPU에 인덱스 레지스터 가 있는지 여부 (
ESI/EDI
x86에서).에 전달 된 동일한 인덱스를 다른 코드에서도 사용 하는 정도
operator[]
입니다.
(예 : 잠금 단계에서 여러 배열을 통해 인덱싱하고 있습니까?)
그 이유는 다음과 같습니다.
당신이 뭔가를한다면
std::vector<unsigned char> a, b; for (size_t i = 0; i < n; ++i) { a[13 * i] = b[37 * i]; }
그러면이 코드는 루프의 각 반복에서 곱셈 연산 을 수행하기 때문에 반복기 버전보다 훨씬 느릴 것입니다 !
마찬가지로 다음과 같은 작업을 수행하면
struct T { unsigned char a[37]; }; std::vector<T> a; for (size_t i = 0; i < n; ++i) { a[i] = foo(i); }
그런 다음이 아마 것 또한 있기 때문에, 반복자 버전보다 속도가 느려질 수
sizeof(T)
있습니다 2의 거듭 제곱하지 , 따라서 당신은 (다시)에 의해 증식하는37
때마다 루프!CPU에 인덱스 레지스터가있는 경우 인덱스 레지스터 를 사용하여 루프에서 사용할 다른 레지스터 를 해제하면 코드가 반복기보다는 인덱스를 사용하여 더 잘 수행 될 수 있습니다 . 이것은 보는 것만으로도 알 수있는 것이 아닙니다 . 코드를 프로파일 링하거나 분해해야합니다.
여러 배열이 동일한 인덱스를 공유 할 수있는 경우 코드는 여러 반복자를 증가시키는 대신 하나의 인덱스 만 증가 시키면 메모리에 대한 쓰기가 줄어들고 일반적으로 성능이 향상됩니다. 그러나 단일 배열에 대해서만 반복하는 경우 기존 기본 포인터에 오프셋을 추가 할 필요가 없기 때문에 반복기가 매우 빠를 수 있습니다.
일반적으로, 프로파일 링이 전환하는 것이 유리하다는 병목 현상에 직면하지 않는 한, 반복기가 범용 이고 이미 가장 빠른 접근 방식 일 가능성이 높기 때문에 인덱스보다 반복기 를 선호 하고 포인터에 인덱스를 선호 해야 합니다 . 데이터를 무작위로 지정할 필요가 없으므로 필요한 경우 컨테이너를 교체 할 수 있습니다. 인덱스는 데이터에 직접 액세스 할 필요가 없기 때문에 다음으로 선호되는 도구입니다. 인덱스는 무효화되는 빈도가 적고 문제없이 a 를 a로 대체 할 수 있습니다. 포인터는 최후의 수단이되어야하며, 이터레이터가 릴리스 모드에서 아직 포티 너로 퇴화되지 않은 경우에만 유용합니다.deque
vector
VisualStudio 2005 또는 2008을 사용하는 경우 벡터에서 최상의 성능을 얻으려면 _SECURE_SCL = 0을 정의해야합니다.
기본적으로 _SECURE_SCL이 켜져 있으므로 포함을 반복하는 속도가 훨씬 느려집니다. 즉, 디버그 빌드에서 그대로두면 오류를 훨씬 쉽게 추적 할 수 있습니다. 주의 할 점은 매크로가 반복기 및 컨테이너의 크기를 변경하므로 stl 컨테이너를 공유하는 모든 컴파일 단위에서 일관성을 유지해야합니다.
유일한 답은 플랫폼에서 테스트하는 것입니다. 일반적으로 STL에서 표준화 된 유일한 것은 컬렉션이 제공하는 반복기 유형과 알고리즘의 복잡성입니다.
나는 그 두 버전 사이에 차이가 없다고 말하고 싶습니다. 제가 생각할 수있는 유일한 차이점은 배열의 길이를 계산해야 할 때 코드가 전체 컬렉션을 반복해야한다는 것입니다. 길이가 벡터 내부의 변수에 저장되어 있는지 확실하지 않으면 오버 헤드가 중요하지 않습니다)
"at"를 사용하여 요소에 액세스하는 것은 []로 직접 액세스하는 것보다 약간 더 오래 걸립니다. 이는 벡터의 경계에 있는지 확인하고 경계를 벗어난 경우 예외를 던지기 때문입니다 ([]는 일반적으로 포인터 산술 사용-더 빠를 것입니다)
이제 OpenGL 코드를 최적화하려고 할 때이 스레드를 발견했으며 스레드가 오래되었지만 결과를 공유하고 싶었습니다.
배경 : 6에서 12까지의 크기를 가진 4 개의 벡터가 있습니다. 쓰기는 코드 시작 부분에 한 번만 발생하고 0.1 밀리 초마다 벡터의 각 요소에 대해 읽기가 발생합니다.
다음은 먼저 사용 된 코드의 제거 된 버전입니다.
for(vector<T>::iterator it = someVector.begin(); it < someVector.end(); it++)
{
T a = *it;
// Various other operations
}
이 방법을 사용한 프레임 속도는 약 7fps입니다.
그러나 코드를 다음과 같이 변경하면 프레임 속도가 15fps로 거의 두 배가되었습니다.
for(size_t index = 0; index < someVector.size(); ++index)
{
T a = someVector[index];
// Various other operations
}
그 차이는 무시해도 좋을 것입니다. std :: vector는 해당 요소가 메모리에 연속적으로 배치되도록 보장합니다. 따라서 대부분의 stl 구현은 일반 포인터로 std :: vector에 반복기를 구현합니다. 이를 염두에두고 두 버전 간의 유일한 차이점은 첫 번째 버전은 포인터를 증가시키고 두 번째 버전에서는 인덱스를 증가시켜 포인터에 추가한다는 것입니다. 그래서 내 추측은 두 번째가 아마도 매우 빠른 기계 명령 일 것입니다.
컴파일러가 생성하는 기계어 코드를 확인하십시오.
그러나 일반적으로 정말 중요한 경우 프로필을 작성하는 것이 좋습니다. 이런 종류의 질문에 대해 너무 일찍 생각하는 것은 일반적으로 너무 많은 것을 돌려주지 않습니다. 일반적으로 코드의 핫스팟은 첫눈에 의심하지 않는 다른 곳에 있습니다.
다음은 기본 mingw 컴파일러를 사용하여 Code :: Blocks v12.11에서 컴파일 한 코드입니다. 이렇게하면 거대한 벡터가 생성되고 반복기, at () 및 인덱스를 사용하여 각 요소에 액세스합니다. 각 함수는 마지막 요소를 함수별로 호출하고 마지막 요소를 임시 메모리에 저장하여 한 번 반복됩니다.
타이밍은 GetTickCount를 사용하여 수행됩니다.
#include <iostream>
#include <windows.h>
#include <vector>
using namespace std;
int main()
{
cout << "~~ Vector access speed test ~~" << endl << endl;
cout << "~ Initialization ~" << endl;
long long t;
int a;
vector <int> test (0);
for (int i = 0; i < 100000000; i++)
{
test.push_back(i);
}
cout << "~ Initialization complete ~" << endl << endl;
cout << " iterator test: ";
t = GetTickCount();
for (vector<int>::iterator it = test.begin(); it < test.end(); it++)
{
a = *it;
}
cout << GetTickCount() - t << endl;
cout << "Optimised iterator: ";
t=GetTickCount();
vector<int>::iterator endofv = test.end();
for (vector<int>::iterator it = test.begin(); it < endofv; it++)
{
a = *it;
}
cout << GetTickCount() - t << endl;
cout << " At: ";
t=GetTickCount();
for (int i = 0; i < test.size(); i++)
{
a = test.at(i);
}
cout << GetTickCount() - t << endl;
cout << " Optimised at: ";
t = GetTickCount();
int endof = test.size();
for (int i = 0; i < endof; i++)
{
a = test.at(i);
}
cout << GetTickCount() - t << endl;
cout << " Index: ";
t=GetTickCount();
for (int i = 0; i < test.size(); i++)
{
a = test[i];
}
cout << GetTickCount() - t << endl;
cout << " Optimised Index: ";
t = GetTickCount();
int endofvec = test.size();
for (int i = 0; i < endofvec; i++)
{
a = test[i];
}
cout << GetTickCount() - t << endl;
cin.ignore();
}
이를 바탕으로 개인적으로 "최적화 된"버전이 "최적화되지 않은"반복자보다 빠르다는 사실을 알게되었습니다. 이터레이터는 직접 인덱스보다 느린 vector.at ()보다 느립니다.
직접 코드를 컴파일하고 실행하는 것이 좋습니다.
편집 :이 코드는 C / C ++에 대한 경험이 적을 때 다시 작성되었습니다. 추가 테스트 사례는 접미사 대신 접두사 증가 연산자를 사용하는 것입니다. 실행 시간이 더 좋아질 것입니다.
Only slightly tangential to the original question, but the fastest loop would be
for( size_t i=size() ; i-- ; ) { ... }
which would of course count down. This does give a substantial saving if you have a large number of iterations in your loop, but it contains only a small number of very fast operations.
So with the [] operator access, this might be faster than many of the examples already posted.
ReferenceURL : https://stackoverflow.com/questions/776624/whats-faster-iterating-an-stl-vector-with-vectoriterator-or-with-at
'development' 카테고리의 다른 글
이러한 폴더의 공유 이름에 달러 기호가 추가 된 이유는 무엇입니까? (0) | 2020.12.29 |
---|---|
XSLT : 동안 속성 값을 변경하는 방법 (0) | 2020.12.29 |
NSURLConnection의 userAgent 변경 (0) | 2020.12.29 |
자바 게터 및 세터 (0) | 2020.12.29 |
XML 속성에 줄 바꿈을 저장하는 방법은 무엇입니까? (0) | 2020.12.29 |