문제

나는 벡터를 많이 좋아합니다. 그들은 멋지고 빠릅니다. 그러나 나는 Valarray라는이 일이 존재한다는 것을 알고 있습니다. 왜 벡터 대신 valarray를 사용합니까? 나는 Valarrays가 구문 설탕을 가지고 있다는 것을 알고 있지만 그 외에는 언제 유용합니까?

도움이 되었습니까?

해결책

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

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

따라서 숫자라면 작업 중이며 편의성이 중요한 사용 Valarrays는 아닙니다. 그렇지 않으면 벡터가 훨씬 더 편리합니다.

다른 팁

valarray 잘못된 시간에 잘못된 장소에서 태어난 고아입니다. 이 책은 가장 구체적으로 작성된 수학에 사용 된 기계, 특히 Crays와 같은 벡터 프로세서에 사용 된 기계에 대해 공정하게 최적화를 시도합니다.

벡터 프로세서의 경우 일반적으로 원하는 것은 전체 배열에 단일 작업을 적용한 다음 다음 작업을 전체 배열에 적용하는 것입니다.

그러나 상당히 작은 배열을 다루지 않는 한 캐싱과 함께 제대로 작동하지 않는 경향이 있습니다. 대부분의 최신 기계에서, 당신이 일반적으로 선호하는 것은 (가능한 한) 배열의 일부를로드하고, 당신이 가고있는 모든 작업을 수행 한 다음 배열의 다음 부분으로 넘어가는 것입니다.

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

Valarray와 함께 사용할 수있는 보조 수업이 당황한 (문자 그대로) 배열이 있습니다. 당신은 얻습니다 slice, slice_array, gslice 그리고 gslice_array 조각과 놀기 위해 valarray, 다차원 배열처럼 작동하게합니다. 당신도 얻습니다 mask_array 조작을 "마스크"하려면 (예 : x에 x에 항목을 추가하지만 z가 0이 아닌 위치에만). 사소한 사용 이상의 사용 valarray, 당신은이 보조 수업에 대해 많은 것을 배워야합니다. 그 중 일부는 꽤 복잡하고 그 중 어느 것도 (적어도 나에게는) 잘 문서화 된 것 같습니다.

결론 : 광채의 순간이 있고 깔끔하게 어떤 일을 할 수 있지만, 그것이 거의 모호한 이유도 있습니다.

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

따라서 C ++ 프로그래머가 사용하기 시작할 것이라고 완전히 확신하지는 않습니다. valarray 많은 숫자로 속도 개선을 제공 할 수있는 상황이 가장 적습니다.

C ++ 98의 표준화 동안 Valarray는 일종의 빠른 수학 계산을 허용하도록 설계되었습니다. 그러나 그 당시 Todd Veldhuizen은 표현식 템플릿을 발명하고 만들었습니다. Blitz ++, 유사한 템플릿-메타 기술이 발명되어 표준이 출시되기 전에 Valarrays를 거의 쓸모 없게 만들었습니다. Valarray의 원래 제안자 인 IIRC는 표준화에 반쯤 버렸다.

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

그러나이 모든 것이 모호하게 기억 된 소식이라는 것을 명심하십시오. 이것을 소금 한 알로 가져 가서 누군가가 이것을 교정하거나 확인하기를 바랍니다.

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

나는 생각하지 않는다고 말해야한다 std::valarrays 구문 설탕의 방식이 많이 있습니다. 구문은 다르지만 차이를 "설탕"이라고 부르지 않을 것입니다. API는 이상합니다. 섹션 std::valarrays in C ++ 프로그래밍 언어 이 특이한 API와 그 이후로 std::valarrayS는 최적화 될 것으로 예상되며, 사용 중에 얻을 수있는 오류 메시지는 직관적이지 않을 수 있습니다.

호기심으로 약 1 년 전에 나는 std::valarray 에 맞서 std::vector. 더 이상 코드 나 정확한 결과가 없습니다 (자신의 글을 쓰기가 어렵지는 않지만). GCC 사용 i 했다 사용할 때 약간의 성능 이점을 얻으십시오 std::valarray 간단한 수학의 경우, 내 구현이 표준 편차를 계산하는 것은 아닙니다 (물론 표준 편차는 수학이 진행되는 한 그렇게 복잡하지 않습니다). 나는 각 항목에서 큰 std::vector 작업보다 캐시로 더 잘 플레이하십시오 std::valarray에스. (노트, 다음 Musiphil, 나는 거의 동일한 성능을 얻었습니다 vector 그리고 valarray).

결국, 나는 사용하기로 결정했다 std::vector 메모리 할당 및 임시 객체 생성과 같은 것들에주의를 기울이는 동안.


둘 다 std::vector 그리고 std::valarray 데이터를 인접한 블록에 저장하십시오. 그러나 다른 패턴을 사용하여 해당 데이터에 액세스하고 더 중요한 것은 API에 액세스합니다. std::valarray API와는 다른 액세스 패턴을 장려합니다 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::valarrays (마지막 줄의 마지막 줄 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;

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

Valarray는 일부 Fortran 벡터 프로세싱의 선이 C ++에 문지르도록해야했습니다. 어떻게 든 필요한 컴파일러 지원은 실제로 일어나지 않았습니다.

Josuttis Books에는 Valarray에 대한 흥미로운 (다소 비열한) 논평이 포함되어 있습니다.여기 그리고 여기).

그러나 인텔은 이제 최근 컴파일러 릴리스에서 Valarray를 다시 방문하고있는 것 같습니다 (예 : 슬라이드 9); 4 방향 SIMD SSE 명령어 세트가 8- 웨이 AVX와 16 방향 Larrabee 명령어가 결합 될 예정이며 이식성의 이익에 따라 추상화와 같은 추상화로 코딩하는 것이 훨씬 나을 것입니다. (예정인) 내재적 인 Valarray.

나는 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);

enter image description here

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()

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

Valarray 어레이 클래스는 특정 형태의 별명이없는 것으로 정의되므로 이러한 클래스에서의 작업을 최적화 할 수 있습니다.

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

STD :: Valarray는 계산 유체 역학 또는 계산 구조 역학과 같은 무거운 숫자 작업을위한 것이며, 여기에는 수백만 개의 배열이 있고 때로는 수천만 개의 항목이 있으며 수백만 개의 타임 스펙트가있는 루프로 반복합니다. 어쩌면 오늘날 STD :: Vector는 비슷한 성능을 가지고 있지만 약 15 년 전에 효율적인 숫자 솔버를 작성하려면 Valarray가 거의 필수였습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top