C ++ valarray so với vector


159

Tôi thích vectơ rất nhiều. Chúng tiện lợi và nhanh chóng. Nhưng tôi biết thứ này được gọi là valarray tồn tại. Tại sao tôi sử dụng valarray thay vì vector? Tôi biết valarrays có một số đường cú pháp, nhưng ngoài ra, khi nào chúng hữu ích?


2
Chỉ cần suy nghĩ về điều này vào ngày khác, quá. Theo như tôi biết, nó thực sự giống như một vector toán học chuyên ngành.
GManNickG

Không valarray làm mẫu biểu thức?
Vịt Mooing

Nhà vật lý Ulrich Mutze cung cấp một trường hợp sử dụng cho valarray ở đâyđây
cuộc sống

Câu trả lời:


70

Các giá trị (mảng giá trị) nhằm đưa một số tốc độ của Fortran lên C ++. Bạn sẽ không tạo ra một giá trị con trỏ để trình biên dịch có thể đưa ra các giả định về mã và tối ưu hóa nó tốt hơn. (Lý do chính khiến Fortran nhanh như vậy là vì không có loại con trỏ nên không thể có bí danh con trỏ.)

Valarrays cũng có các lớp cho phép bạn cắt chúng ra một cách hợp lý dễ dàng mặc dù phần đó của tiêu chuẩn có thể sử dụng nhiều công việc hơn một chút. Thay đổi kích thước chúng là phá hoại và chúng thiếu các trình vòng lặp.

Vì vậy, nếu đó là những con số bạn đang làm việc và sự tiện lợi không phải là tất cả những giá trị sử dụng quan trọng. Nếu không, vectơ chỉ thuận tiện hơn rất nhiều.


11
Chúng không được thiết kế để tránh con trỏ. C ++ 11 định nghĩa bắt đầu () và kết thúc () trong valarray trả lại các vòng lặp cho chúng
Mohamed El-Nakib

3
@ user2023370: đó là lý do tại sao rất nhiều người dùng Fortran thích Fortran 77. :)
Michael

152

valarraylà một đứa trẻ mồ côi được sinh ra không đúng lúc, không đúng lúc. Đó là một nỗ lực tối ưu hóa, khá cụ thể cho các máy được sử dụng cho toán học hạng nặng khi nó được viết - cụ thể là các bộ xử lý vector như Crays.

Đối với bộ xử lý vectơ, những gì bạn thường muốn làm là áp dụng một thao tác duy nhất cho toàn bộ một mảng, sau đó áp dụng thao tác tiếp theo cho toàn bộ mảng, và cứ thế cho đến khi bạn hoàn thành mọi việc bạn cần làm.

Tuy nhiên, trừ khi bạn xử lý các mảng khá nhỏ, tuy nhiên, điều đó có xu hướng hoạt động kém với bộ nhớ đệm. Trên hầu hết các máy hiện đại, những gì bạn thường thích (trong phạm vi có thể) sẽ là tải một phần của mảng, thực hiện tất cả các thao tác trên nó, sau đó chuyển sang phần tiếp theo của mảng.

valarraycũng được cho là để loại bỏ bất kỳ khả năng răng cưa nào, điều này (ít nhất là về mặt lý thuyết) cho phép trình biên dịch cải thiện tốc độ vì việc lưu trữ các giá trị trong các thanh ghi sẽ tự do hơn. Tuy nhiên, trong thực tế, tôi không chắc chắn rằng bất kỳ triển khai thực tế nào cũng tận dụng lợi thế này ở bất kỳ mức độ đáng kể nào. Tôi nghi ngờ đây là một vấn đề rắc rối giữa gà và trứng - không có hỗ trợ trình biên dịch, nó không trở nên phổ biến và miễn là nó không phổ biến, sẽ không có ai gặp rắc rối khi làm việc với trình biên dịch của họ để hỗ trợ nó.

Ngoài ra còn có một loạt các lớp phụ trợ sử dụng với valarray. Bạn nhận được slice, slice_array, gslicegslice_arraychơi với những mảnh một valarray, và làm cho nó hoạt động giống như một mảng đa chiều. Bạn cũng nhận đượcmask_array thể "che dấu" một thao tác (ví dụ: thêm các mục trong x vào y, nhưng chỉ tại các vị trí mà z khác không). Để sử dụng nhiều hơn tầm thườngvalarray , bạn phải tìm hiểu rất nhiều về các lớp phụ trợ này, một số trong đó khá phức tạp và dường như không có tài liệu nào (ít nhất là đối với tôi).

Điểm mấu chốt: trong khi nó có những khoảnh khắc rực rỡ, và có thể làm một số điều khá gọn gàng, thì cũng có một số lý do rất tốt mà nó (và gần như chắc chắn sẽ vẫn còn) tối nghĩa.

Chỉnh sửa (tám năm sau, năm 2017): Một số trước đó đã trở nên lỗi thời ở ít nhất một mức độ. Lấy một ví dụ, Intel đã triển khai một phiên bản valarray được tối ưu hóa cho trình biên dịch của họ. Nó sử dụng Nguyên tắc hiệu suất tích hợp Intel (Intel IPP) để cải thiện hiệu suất. Mặc dù cải thiện hiệu suất chính xác chắc chắn thay đổi, một thử nghiệm nhanh với mã đơn giản cho thấy tốc độ cải thiện 2: 1, so với mã giống hệt được biên dịch với triển khai "tiêu chuẩn"valarray .

Vì vậy, trong khi tôi không hoàn toàn tin rằng các lập trình viên C ++ sẽ bắt đầu sử dụng valarrayvới số lượng lớn, thì có ít nhất một số trường hợp có thể cải thiện tốc độ.


1
Có đặc biệt không được phép lưu trữ các loại đối tượng tùy ý bên trong valarray?
dùng541686

6
@Mehrdad: Có - có một danh sách hạn chế (khá dài) tại [Numeric.Requirements]. Đối với chỉ một vài ví dụ, tất cả các lớp trừu tượng và ngoại lệ đều bị cấm. Nó cũng đòi hỏi sự tương đương giữa (ví dụ) bản sao xây dựng và một chuỗi các cấu trúc mặc định theo sau là sự phân công.
Jerry Coffin

@JerryCoffin sheesh thật đáng sợ. chúng tôi hứa rằng chúng tôi sẽ không sử dụng nó.
Hani Goc

4
Tôi sẽ không quyết định điều đó dựa trên sự sợ hãi. Tôi quyết định nó dựa trên việc bạn có cần lưu trữ các yếu tố sử dụng các tính năng mà nó cấm hay không.
Jerry Coffin

3
@annoying_squid: Nếu bạn có thông tin chính xác hơn và (bạn tin) để thêm, xin vui lòng thêm câu trả lời hiển thị thông tin đó. Vì hiện tại, bình luận của bạn dường như không thêm bất kỳ thông tin hữu ích nào.
Jerry Coffin

39

Trong quá trình chuẩn hóa C ++ 98, valarray được thiết kế để cho phép một số tính toán toán học nhanh. Tuy nhiên, khoảng thời gian đó, Todd Veldhuizen đã phát minh ra các mẫu biểu thức và tạo ra blitz ++ , và các kỹ thuật meta-meta mẫu tương tự đã được phát minh, khiến cho các giá trị khá lỗi thời trước khi tiêu chuẩn thậm chí được phát hành. IIRC, người đề xuất ban đầu của valarray đã bỏ rơi nó giữa chừng trong tiêu chuẩn hóa, điều này (nếu đúng) cũng không giúp được gì.

ISTR rằng lý do chính khiến nó không bị xóa khỏi tiêu chuẩn là vì không ai dành thời gian để đánh giá vấn đề một cách kỹ lưỡng và viết một đề xuất để loại bỏ nó.

Xin lưu ý, tuy nhiên, tất cả điều này chỉ là những tin đồn được nhớ một cách mơ hồ. Lấy cái này với một hạt muối và hy vọng ai đó sửa chữa hoặc xác nhận điều này.


mẫu biểu thức cũng có thể được ghi có vào Vandevoorde, phải không?
Nikos Athanasiou

@Nikos: Không phải tôi biết. Tôi có thể sai mặc dù. Bạn có lợi gì cho việc đọc đó?
sbi

1
nó được đề cập trong cuốn sách "Các mẫu C ++ - Hướng dẫn đầy đủ", tôi nghĩ rằng nói chung chấp nhận rằng cả hai đều phát minh ra chúng một cách độc lập .
Nikos Athanasiou

27

Tôi biết valarrays có một số đường cú pháp

Tôi phải nói rằng tôi không nghĩ std::valarrayscó nhiều cú pháp đường. Cú pháp là khác nhau, nhưng tôi sẽ không gọi sự khác biệt là "đường." API thật kỳ lạ. Phần trên std::valarrays trong Ngôn ngữ lập trình C ++ có đề cập đến API bất thường này và thực tế là, kể từ đóstd::valarray chúng được dự kiến ​​sẽ được tối ưu hóa cao, nên bất kỳ thông báo lỗi nào bạn nhận được khi sử dụng chúng có thể sẽ không trực quan.

Ra khỏi tò mò, khoảng một năm trước, tôi đọ sức std::valarraychống lại std::vector. Tôi không còn có mã hoặc kết quả chính xác (mặc dù không khó để tự viết). Sử dụng GCC tôi đã nhận được một chút lợi ích hiệu suất khi sử dụng std::valarraycho toán đơn giản, nhưng không phải cho việc triển khai của tôi để tính độ lệch chuẩn (và tất nhiên, độ lệch chuẩn không quá phức tạp, theo như toán học). Tôi nghi ngờ rằng các thao tác trên mỗi mục trong một std::vectortrò chơi lớn sẽ tốt hơn với bộ nhớ cache hơn các thao tác trên std::valarrays. ( LƯU Ý , theo lời khuyên từ musiphil , tôi đã quản lý để có được hiệu suất gần như giống hệt từ vectorvalarray).

Cuối cùng, tôi quyết định sử dụng std::vectortrong khi chú ý đến những thứ như phân bổ bộ nhớ và tạo đối tượng tạm thời.


Cả hai std::vectorstd::valarraylưu trữ dữ liệu trong một khối liền kề. Tuy nhiên, họ truy cập dữ liệu đó bằng các mẫu khác nhau và quan trọng hơn là API std::valarraykhuyến khích các mẫu truy cập khác với API chostd::vector .

Đối với ví dụ độ lệch chuẩn, tại một bước cụ thể tôi cần tìm giá trị trung bình của bộ sưu tập và sự khác biệt giữa giá trị của từng phần tử và giá trị trung bình.

Đối với std::valarray, tôi đã làm một cái gì đó như:

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;

Tôi có thể đã thông minh hơn với std::slicehoặcstd::gslice . Đã hơn năm năm rồi.

Đối với std::vector, tôi đã làm một cái gì đó dọc theo dòng:

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

Hôm nay tôi chắc chắn sẽ viết khác đi. Nếu không có gì khác, tôi sẽ tận dụng C ++ 11 lambdas.

Rõ ràng là hai đoạn mã này làm những việc khác nhau. Đối với một, std::vectorví dụ không tạo ra một bộ sưu tập trung gian như std::valarrayví dụ này. Tuy nhiên, tôi nghĩ thật công bằng khi so sánh chúng vì sự khác biệt gắn liền với sự khác biệt giữa std::vectorstd::valarray.

Khi tôi viết câu trả lời này, tôi đã nghi ngờ rằng việc trừ giá trị của các phần tử từ hai std::valarrays (dòng cuối cùng trong std::valarrayví dụ) sẽ ít thân thiện với bộ đệm hơn so với dòng tương ứng trongstd::vector ví dụ (cũng xảy ra là dòng cuối cùng).

Hóa ra, tuy nhiên,

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;

Làm điều tương tự như std::vectorví dụ, và có hiệu suất gần như giống hệt nhau. Cuối cùng, câu hỏi là bạn thích API nào.


Tôi không thể nghĩ ra bất kỳ lý do nào khiến a std::vectorchơi tốt hơn với bộ nhớ cache hơn a std::valarray; cả hai đều phân bổ một khối bộ nhớ liền kề cho các phần tử của chúng.
musiphil

1
@musiphil Phản hồi của tôi quá dài cho một bình luận, vì vậy tôi đã cập nhật câu trả lời.
Max Lybbert

1
Đối với valarrayví dụ của bạn ở trên, bạn không phải xây dựng một temp valarrayđối tượng, nhưng bạn có thể vừa thực hiện std::valarray<double> differences_from_mean = original_values - mean;, và sau đó hành vi bộ đệm sẽ tương tự như đối tượng vector. (Nhân tiện, nếu meanthực sự int, không double, bạn có thể cần static_cast<double>(mean).)
musiphil 27/03/13

Cảm ơn đề nghị để làm sạch valarray. Tôi sẽ cần phải xem nếu điều đó cải thiện hiệu suất. Như meanint: đó là một sai lầm. Ban đầu tôi đã viết ví dụ lên bằng ints, và sau đó nhận ra rằng meansau đó sẽ rất xa so với ý nghĩa thực sự vì cắt ngắn. Nhưng tôi đã bỏ lỡ một vài thay đổi cần thiết trong vòng chỉnh sửa đầu tiên của mình.
Max Lybbert

@musiphil Bạn nói đúng; sự thay đổi đó đã mang lại mã mẫu cho hiệu năng gần như giống hệt nhau.
Max Lybbert

23

valarray được cho là để cho một số lòng tốt xử lý vector FORTRAN xóa tan trên C ++. Bằng cách nào đó sự hỗ trợ trình biên dịch cần thiết không bao giờ thực sự xảy ra.

Các sách Josuttis chứa một số bình luận thú vị (hơi chê bai) về valarray ( ở đâyở đây ).

Tuy nhiên, Intel dường như đang xem xét lại valarray trong các bản phát hành trình biên dịch gần đây của họ (ví dụ: xem slide 9 ); đây là một sự phát triển thú vị khi bộ hướng dẫn SIMD SSE 4 chiều của họ sắp được kết hợp bởi các hướng dẫn AVX 8 chiều và 16 hướng Larrabee và vì lợi ích của tính di động, việc mã hóa bằng cách trừu tượng hóa sẽ tốt hơn nhiều valarray hơn (nói) nội tại.


16

Tôi tìm thấy một cách sử dụng tốt cho valarray. Đó là sử dụng valarray giống như mảng numpy.

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

nhập mô tả hình ảnh ở đây

Chúng ta có thể thực hiện ở trên với 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;
}

Ngoài ra, chúng ta cần kịch bản python.

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
Tôi thực sự có cùng suy nghĩ giống như bạn đã làm khi tôi phát hiện ra valarray ngày hôm nay tại nơi làm việc. Tôi nghĩ từ giờ trở đi đối với các vấn đề xử lý toán học trong c ++, tôi sẽ sử dụng valarray vì mã trông đơn giản hơn khi đứng dưới góc độ toán học.
Zachary Kraus

8

Tiêu chuẩn C ++ 11 cho biết:

Các lớp mảng valarray được định nghĩa là không có các dạng răng cưa nhất định, do đó cho phép các hoạt động trên các lớp này được tối ưu hóa.

Xem C ++ 11 26.6.1-2.


Vì tôi giả sử Tiêu chuẩn xác định hình thức nào, bạn có thể trích dẫn chúng không? Ngoài ra, chúng có được thực hiện bằng các thủ thuật mã hóa hay chúng là các ngoại lệ dựa trên trình biên dịch đối với các quy tắc răng cưa ở nơi khác trong ngôn ngữ?
underscore_d

2

Với std::valarraybạn có thể sử dụng ký hiệu toán học tiêu chuẩn như v1 = a*v2 + v3ra khỏi hộp. Điều này là không thể đối với các vectơ trừ khi bạn xác định toán tử của riêng mình.


0

std :: valarray dành cho các tác vụ số nặng, chẳng hạn như Động lực học tính toán hoặc Động lực cấu trúc tính toán, trong đó bạn có các mảng với hàng triệu, đôi khi hàng chục triệu mục và bạn lặp lại chúng trong một vòng lặp với hàng triệu dấu thời gian. Có thể ngày nay std :: vector có hiệu suất tương đương, nhưng khoảng 15 năm trước, valarray gần như là bắt buộc nếu bạn muốn viết một bộ giải số hiệu quả.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.