C ++ valarray vs. 벡터


159

나는 벡터를 많이 좋아한다. 그들은 깔끔하고 빠릅니다. 그러나 나는 valarray라는 것이 존재한다는 것을 알고 있습니다. 왜 벡터 대신에 valarray를 사용해야합니까? 나는 valarrays에 약간의 구문 설탕이 있다는 것을 알고 있지만 그 외에는 언제 유용합니까?


2
다른 날에도이 문제를 숙고하고있었습니다. 내가 아는 한, 그것은 실제로 전문 수학 벡터입니다.
GManNickG

valarray는 표현식 템플릿을 사용하지 않습니까?
Mooing Duck

물리학 울리히 Mutze는 대한 사용 사례를 제공 valarray 여기여기에
lifebalance

답변:


70

Valarray (값 배열)는 Fortran의 속도 중 일부를 C ++로 가져 오도록 고안되었습니다. 컴파일러는 포인터에 대한 valarray를 만들지 않으므로 컴파일러가 코드에 대한 가정을하고 더 잘 최적화 할 수 있습니다. (Fortran이 너무 빠른 주된 이유는 포인터 유형이 없으므로 포인터 별칭이 없기 때문입니다.)

Valarrays에는 표준의 일부가 약간 더 많은 작업을 사용할 수 있지만 합리적으로 쉬운 방법으로 클래스를 슬라이스 할 수있는 클래스가 있습니다. 그것들의 크기를 조정하는 것은 파괴적이며 반복자가 없습니다.

따라서 숫자로 작업하고 편리함이 중요한 용도로 사용되는 것은 아닙니다. 그렇지 않으면 벡터가 훨씬 더 편리합니다.


11
포인터를 피하도록 설계되지 않았습니다. C ++ (11 개)를 정의가 된 valarray에 ()과 끝 ()를 시작하는 그들에게 반환 반복자
모하메드 엘 - Nakib

3
@ user2023370 : 이것이 많은 Fortran 사용자가 Fortran 77을 선호하는 이유입니다. :)
Michael

152

valarray잘못된 시간에 잘못된 장소에서 태어난 고아의 일종입니다. 최적화, 특히 Crays와 같은 벡터 프로세서가 쓰여질 때 강력한 수학에 사용 된 기계에 대한 최적화 시도입니다.

벡터 프로세서의 경우 일반적으로 전체 작업에 단일 작업을 적용한 후 다음 작업을 전체 배열에 적용하는 등 필요한 작업을 모두 수행 할 때까지 계속했습니다.

그러나 아주 작은 배열을 다루지 않는 한 캐싱에는 제대로 작동하지 않는 경향이 있습니다. 대부분의 최신 컴퓨터에서 일반적으로 선호하는 것은 배열의 일부를로드하고 모든 작업을 수행 한 다음 배열의 다음 부분으로 이동하는 것입니다.

valarray또한 앨리어싱 (aliasing) 가능성을 제거해야합니다. 적어도 이론적으로는 레지스터에 값을 더 자유롭게 저장할 수 있기 때문에 컴파일러가 속도를 향상시킬 수 있습니다. 그러나 실제로는 실제 구현이이 점을 어느 정도 활용할 수 있는지 확신 할 수 없습니다. 나는 그것이 일종의 닭과 계란 종류의 문제라고 생각합니다. 컴파일러 지원이 없으면 인기를 얻지 못했지만 인기가 없다면 컴파일러를 지원하는 데 어려움을 겪지는 않을 것입니다.

또한 valarray와 함께 사용할 어리석은 (문자 그대로) 보조 클래스가 있습니다. 당신은 얻을 slice, slice_array, gslice그리고 gslice_array(A)의 조각과 재생 valarray, 그리고 그것은 다차원 배열처럼 행동합니다. 또한 mask_array연산을 "마스크"해야합니다 (예 : x에 y의 항목을 추가하지만 z가 0이 아닌 위치에만). 을 사소하게 사용하려면 valarray이 보조 클래스에 대해 많은 것을 배워야합니다. 그 중 일부는 꽤 복잡하고 그 중 어느 것도 매우 잘 문서화되지 않은 것 같습니다.

결론 : 그것은 광채의 순간을 가지고 꽤 깔끔하게 어떤 일을 할 수 있지만, 그것이 모호한 (그리고 거의 확실하게 남아있을) 매우 좋은 이유가 있습니다.

편집 (8 년 후, 2017 년) : 위의 일부 중 적어도 일부는 더 이상 사용되지 않습니다. 일례로, 인텔은 컴파일러에 최적화 된 버전의 valarray를 구현했습니다. 인텔 IPP (Integrated Performance Primitives)를 사용하여 성능을 향상시킵니다. 정확한 성능 향상은 의심 할 여지없이 다양하지만 간단한 코드를 사용한 빠른 테스트는 "표준"구현으로 컴파일 된 동일한 코드와 비교하여 속도가 약 2 : 1 개선되었음을 보여줍니다 valarray.

따라서 C ++ 프로그래머가 valarray많은 수 를 사용할 것이라고 확신하지는 않지만 속도 향상을 제공 할 수있는 상황은 최소한 있습니다.


1
valarray 내부에 임의의 객체 유형을 저장하는 것이 구체적으로 허용되지 않습니까?
user541686

6
@Mehrdad : 예. [Numeric.Requirements]에는 제한 사항 목록이 있습니다. 몇 가지 예의 경우 모든 추상 클래스와 예외는 금지됩니다. 또한 (예를 들어) 사본 구성과 일련의 기본 구성 및 할당이 동일해야합니다.
Jerry Coffin

@JerryCoffin esh이 무섭습니다. 우리는 그것을 사용하지 않을 것을 약속합니다.
Hani Goc

4
나는 두려움에 근거하여 그것을 결정하지 않을 것입니다. 금지하는 기능을 사용하는 요소를 저장해야하는지 여부에 따라 결정합니다.
Jerry Coffin

3
@annoying_squid : 더 구체적이고 정확한 정보를 추가해야한다면, 그 정보를 보여주는 답을 자유롭게 추가하십시오. 현재로서는 귀하의 의견이 유용한 정보를 추가하지 않는 것 같습니다.
Jerry Coffin

39

C ++ 98을 표준화하는 동안 valarray는 일종의 빠른 수학적 계산을 허용하도록 설계되었습니다. 그러나 그 당시 Todd Veldhuizen은 표현 템플릿을 발명하고 blitz ++를 만들었 으며 유사한 템플릿 메타 기술이 개발되어 표준이 출시되기 전에는 valarray를 거의 쓸모 없게 만들었습니다. valarray의 최초 제안자 인 IIRC는 표준화에 반을 버렸으며, 사실 인 경우에도 도움이되지 않았습니다.

ISTR이 표준에서 제거되지 않은 주된 이유는 아무도 문제를 철저히 평가하고 제거 제안을 작성하지 않았기 때문입니다.

그러나이 모든 것이 모호하게 기억 된 소문임을 명심하십시오. 소금 한 알로 이것을 가지고 누군가가 이것을 수정하거나 확인하기를 바랍니다.


표현식 템플릿도 Vandevoorde에 똑같이 적용됩니다.
Nikos Athanasiou 2016 년

@Nikos : 내가 아는 것은 아닙니다. 그래도 틀릴 수 있습니다. 그 독서에 유리한 점은 무엇입니까?
sbi

1
그것은 "C ++ 템플릿-완전한 가이드"책에서 언급되었는데, 그것들은 그것들이 독립적으로 그것들을 발명했다는 것이 일반적으로 받아 들여 진다고 생각합니다 .
Nikos Athanasiou

27

valarrays에 약간의 구문 설탕이 있다는 것을 알고 있습니다

나는 std::valarrays구문 설탕을 많이 가지고 있다고 생각하지 않는다고 말해야합니다 . 구문은 다르지만 그 차이를 "설탕"이라고 부르지는 않습니다. API가 이상합니다. C ++ 프로그래밍 언어std::valarray 의 s 섹션 에서는이 비정상적인 API에 대해 언급하고 있으며,이 API 는 고도로 최적화 될 것으로 예상 되므로 사용 중에 발생하는 모든 오류 메시지는 직관적이지 않을 수 있습니다.std::valarray

약 1 년 전에 호기심에서 나는 std::valarray대항했다 std::vector. 더 이상 코드 나 정확한 결과를 얻지 못합니다 (자체를 작성하는 것이 어렵지는 않지만). GCC 내가 사용 했던 사용할 때 약간의 성능 이점을 얻을 std::valarray내 구현 표준 편차를 계산하는 간단한 수학을 위해,하지만 (물론,과, 표준 편차가되지 복잡한 즉, 지금까지의 수학 간다). 큰 항목의 각 항목에 대한 std::vector작업이 std::valarrays의 작업보다 캐시에서 더 잘 작동한다고 생각합니다 . ( 참고 , 조언을 다음 musiphil , 나는 거의 동일한에서 성능을 얻기 위해 관리했습니다 vectorvalarray).

결국 std::vector메모리 할당 및 임시 객체 생성과 같은 것에주의를 기울이면서 사용하기로 결정했습니다 .


모두 std::vectorstd::valarray연속 된 블록에 데이터를 저장합니다. 그러나 이들은 다른 패턴을 사용하여 해당 데이터에 액세스하며,보다 중요한 API는에 대한 API와 std::valarray다른 액세스 패턴 을 권장합니다 std::vector.

표준 편차 예제의 경우 특정 단계에서 컬렉션의 평균과 각 요소의 값과 평균의 차이를 찾아야했습니다.

에 대해 std::valarray다음과 같은 작업을 수행했습니다.

std::valarray<double> original_values = ... // obviously I put something here
double mean = original_values.sum() / original_values.size();
std::valarray<double> temp(mean, original_values.size());
std::valarray<double> differences_from_mean = original_values - temp;

std::slice또는 로 더 영리했을 수도 있습니다 std::gslice. 5 년이 지났습니다.

에 대해 std::vector, 나는 다음 라인을 따라 무언가를했다.

std::vector<double> original_values = ... // obviously, I put something here
double mean = std::accumulate(original_values.begin(), original_values.end(), 0.0) / original_values.size();

std::vector<double> differences_from_mean;
differences_from_mean.reserve(original_values.size());
std::transform(original_values.begin(), original_values.end(), std::back_inserter(differences_from_mean), std::bind1st(std::minus<double>(), mean));

오늘 나는 확실히 다르게 쓸 것입니다. 다른 것이 없다면 C ++ 11 람다를 활용할 것입니다.

이 두 코드 스 니펫은 서로 다른 기능을 수행합니다. 예를 std::vector들어, std::valarray예제는 예제 와 같이 중간 컬렉션을 만들지 않습니다. 그러나, 나는 차이가 사이의 차이로 연결되어 있기 때문에 적정 그들을 비교 생각 std::vector하고 std::valarray.

이 답변을 쓸 때 두 개의 요소 std::valarray( std::valarray예 : 마지막 줄)에서 요소의 값을 빼는 것이 예제의 해당 줄 (마지막 줄이기도 함)보다 캐시 친화적이지 않을 것이라고 생각했습니다 std::vector.

그러나 그것은

std::valarray<double> original_values = ... // obviously I put something here
double mean = original_values.sum() / original_values.size();
std::valarray<double> differences_from_mean = original_values - mean;

는 AS 같은 일을합니까 std::vector예를 들어, 거의 동일한 성능을 가지고있다. 결국, 당신이 선호하는 API가 문제입니다.


나는 a std::vector보다 캐시에서 더 잘 플레이 하는 이유를 생각할 수 없다 std::valarray. 둘 다 요소에 하나의 연속 된 메모리 블록을 할당합니다.
musiphil

1
@musiphil 댓글에 대한 답변이 너무 깁니다. 그래서 답변을 업데이트했습니다.
Max Lybbert

1
valarray위 의 예제에서는 temp valarray객체 를 생성 할 필요가 없었지만 방금 수행했을 수 있으며 std::valarray<double> differences_from_mean = original_values - mean;캐시 동작은 vector예제 와 비슷해야합니다 . (그런데 mean실제로 int는 그렇지 않다면 double필요할 수도 있습니다 static_cast<double>(mean).)
musiphil

정리 제안을 주셔서 감사합니다 valarray. 성능이 향상되는지 확인해야합니다. 인에 관해서 meanint: 그것은 실수였습니다. 원래 ints 를 사용하여 예제를 작성한 mean다음 잘림으로 인해 실제 평균과는 거리가 멀다는 것을 깨달았습니다 . 그러나 첫 편집 라운드에서 몇 가지 필요한 변경 사항을 놓쳤습니다.
Max Lybbert

@musiphil 당신이 맞아요; 이러한 변경으로 인해 샘플 코드는 거의 동일한 성능을 얻었습니다.
Max Lybbert

23

valarray는 일부 FORTRAN 벡터 처리 장점이 C ++에서 문지르도록해야했습니다. 어떻게 든 필요한 컴파일러 지원이 실제로 발생하지 않았습니다.

Josuttis 책에는 valarray ( here and here ) 에 대한 흥미로운 (일부 비열한) 해설이 포함되어 있습니다 .

그러나 인텔은 최근 컴파일러 릴리스에서 valarray를 다시 방문하는 것으로 보입니다 (예 : 슬라이드 9 참조) ). 이것은 4-way SIMD SSE 명령어 세트가 8-way AVX 및 16-way Larrabee 명령어에 의해 결합 될 예정이고 이식성을 위해 다음과 같은 추상화로 코딩하는 것이 훨씬 나을 것입니다. 본질적인 것보다 valarray.


16

valarray에 대한 좋은 사용법을 찾았습니다. numpy 배열과 마찬가지로 valarray를 사용합니다.

auto x = linspace(0, 2 * 3.14, 100);
plot(x, sin(x) + sin(3.f * x) / 3.f + sin(5.f * x) / 5.f);

여기에 이미지 설명을 입력하십시오

valarray로 위를 구현할 수 있습니다.

valarray<float> linspace(float start, float stop, int size)
{
    valarray<float> v(size);
    for(int i=0; i<size; i++) v[i] = start + i * (stop-start)/size;
    return v;
}

std::valarray<float> arange(float start, float step, float stop)
{
    int size = (stop - start) / step;
    valarray<float> v(size);
    for(int i=0; i<size; i++) v[i] = start + step * i;
    return v;
}

string psstm(string command)
{//return system call output as string
    string s;
    char tmp[1000];
    FILE* f = popen(command.c_str(), "r");
    while(fgets(tmp, sizeof(tmp), f)) s += tmp;
    pclose(f);
    return s;
}

string plot(const valarray<float>& x, const valarray<float>& y)
{
    int sz = x.size();
    assert(sz == y.size());
    int bytes = sz * sizeof(float) * 2;
    const char* name = "plot1";
    int shm_fd = shm_open(name, O_CREAT | O_RDWR, 0666);
    ftruncate(shm_fd, bytes);
    float* ptr = (float*)mmap(0, bytes, PROT_WRITE, MAP_SHARED, shm_fd, 0);
    for(int i=0; i<sz; i++) {
        *ptr++ = x[i];
        *ptr++ = y[i];
    }

    string command = "python plot.py ";
    string s = psstm(command + to_string(sz));
    shm_unlink(name);
    return s;
}

또한 파이썬 스크립트가 필요합니다.

import sys, posix_ipc, os, struct
import matplotlib.pyplot as plt

sz = int(sys.argv[1])
f = posix_ipc.SharedMemory("plot1")
x = [0] * sz
y = [0] * sz
for i in range(sz):
    x[i], y[i] = struct.unpack('ff', os.read(f.fd, 8))
os.close(f.fd)
plt.plot(x, y)
plt.show()

2
오늘 직장에서 valarray에 대해 알게되었을 때와 똑같은 생각을했습니다. 나는 지금부터 C ++의 수학 처리 문제에 대해 코드가 수학 관점에서 이해하기가 훨씬 간단 해지기 때문에 valarray를 사용할 것이라고 생각합니다.
Zachary Kraus 2018 년

8

C ++ 11 표준은 다음과 같이 말합니다.

valarray 배열 클래스는 특정 형태의 앨리어싱이없는 것으로 정의되므로 이러한 클래스의 작업을 최적화 할 수 있습니다.

C ++ 11 26.6.1-2를 참조하십시오.


표준이 어떤 양식을 정의한다고 가정하므로 인용 할 수 있습니까? 또한 이것들은 코딩 트릭을 사용하여 구현됩니까, 아니면 언어의 다른 곳에서 앨리어싱 규칙에 대한 컴파일러 기반 예외입니까?
underscore_d

2

std::valarray당신 과 함께 표준 수학 표기법을 사용할 수 있습니다 v1 = a*v2 + v3. 고유 한 연산자를 정의하지 않으면 벡터를 사용할 수 없습니다.


0

std :: valarray는 Computational Fluid Dynamics 또는 Computational Structure Dynamics와 같은 무거운 숫자 작업을위한 것으로 수백만, 때로는 수천만 개의 항목이있는 배열이 있고 수백만 번의 시간 간격으로 반복적으로 반복됩니다. 아마 오늘 std :: vector는 비슷한 성능을 가지고 있지만, 약 15 년 전에는 효율적인 수치 솔버를 작성하려면 valarray가 거의 필수입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.