C ++ valarray กับเวกเตอร์


159

ฉันชอบเวกเตอร์มาก พวกเขาดีและรวดเร็ว แต่ฉันรู้ว่าสิ่งนี้เรียกว่าวาลเลย์ ทำไมฉันถึงต้องใช้วาลเลย์แทนเวกเตอร์ ฉันรู้ว่า valarrays มีน้ำตาลประโยค แต่นอกเหนือจากนั้นเมื่อมีประโยชน์


2
เพิ่งไตร่ตรองเรื่องนี้เมื่อวันก่อนด้วย เท่าที่ฉันรู้มันเป็นเวคเตอร์คณิตศาสตร์เฉพาะทางจริงๆ
GManNickG

Valarray ไม่ได้ทำแม่แบบนิพจน์หรือไม่
Mooing Duck

นักฟิสิกส์ Ulrich Mutze มอบกรณีการใช้งานสำหรับvalarray ที่นี่และที่นี่
lifebalance

คำตอบ:


70

Valarrays (value arrays) มีวัตถุประสงค์เพื่อนำความเร็วของ Fortran มาสู่ C ++ คุณจะไม่ทำตัวชี้แนะเพื่อให้คอมไพเลอร์สามารถตั้งสมมติฐานเกี่ยวกับรหัสและปรับให้ดีขึ้นได้ (เหตุผลหลักที่ Fortran เร็วมากคือไม่มีประเภทตัวชี้ดังนั้นจึงไม่มีนามแฝงของตัวชี้)

วาลเลย์ยังมีคลาสที่อนุญาตให้คุณเชือดมันด้วยวิธีที่ง่ายพอสมควรถึงแม้ว่าส่วนหนึ่งของมาตรฐานอาจใช้งานได้อีกเล็กน้อย การปรับขนาดนั้นเป็นการทำลายและขาดตัววนซ้ำ

ดังนั้นหากเป็นตัวเลขที่คุณใช้งานและความสะดวกสบายไม่ใช่สิ่งสำคัญสำหรับการใช้งาน มิฉะนั้นเวกเตอร์จะสะดวกกว่ามาก


11
ไม่ได้ออกแบบมาเพื่อหลีกเลี่ยงพอยน์เตอร์ C ++ 11 กำหนดจุดเริ่มต้น () และจุดสิ้นสุด () ในวาลเลย์ที่ส่งคืนตัววนซ้ำให้กับพวกเขา
โมฮาเหม็ดเอล - นากิบ

3
@ user2023370: นั่นเป็นสาเหตุที่ผู้ใช้ Fortran จำนวนมากเลือกใช้ Fortran 77 :)
Michael

152

valarrayเป็นเด็กกำพร้าที่เกิดมาผิดที่ผิดเวลา มันเป็นความพยายามในการปรับให้เหมาะสมโดยเฉพาะอย่างยิ่งสำหรับเครื่องที่ใช้สำหรับคณิตศาสตร์หนักเมื่อมันถูกเขียน - โดยเฉพาะโปรเซสเซอร์แบบเวกเตอร์เช่น Crays

สำหรับตัวประมวลผลแบบเวกเตอร์สิ่งที่คุณต้องการทำโดยทั่วไปคือใช้การดำเนินการเดี่ยวกับทั้งอาร์เรย์จากนั้นใช้การดำเนินการถัดไปกับอาร์เรย์ทั้งหมดและต่อไปจนกว่าคุณจะทำทุกสิ่งที่คุณต้องการ

อย่างไรก็ตามถ้าคุณไม่ได้จัดการกับอาร์เรย์ที่มีขนาดค่อนข้างเล็กก็มักจะทำงานได้ไม่ดีกับการแคช ในเครื่องจักรที่ทันสมัยที่สุดสิ่งที่คุณต้องการโดยทั่วไป (เท่าที่จะทำได้) คือการโหลดบางส่วนของอาร์เรย์ทำการดำเนินการทั้งหมดที่คุณกำลังจะทำแล้วย้ายไปยังส่วนถัดไปของอาร์เรย์

valarrayก็ควรที่จะกำจัดความเป็นไปได้ใด ๆ ของนามแฝงซึ่ง (อย่างน้อยในทางทฤษฎี) ให้คอมไพเลอร์ปรับปรุงความเร็วเนื่องจากมีอิสระมากขึ้นในการจัดเก็บค่าในการลงทะเบียน อย่างไรก็ตามในความเป็นจริงฉันไม่แน่ใจว่าการใช้งานจริงใด ๆ จะได้ประโยชน์จากสิ่งนี้ในระดับที่สำคัญ ฉันสงสัยว่ามันเป็นปัญหาแบบไก่และไข่ - หากไม่มีการสนับสนุนคอมไพเลอร์ก็ไม่ได้รับความนิยมและตราบใดที่มันไม่เป็นที่นิยมก็ไม่มีใครที่จะไปยุ่งกับปัญหาในการทำงานกับคอมไพเลอร์เพื่อสนับสนุน

นอกจากนี้ยังมีอาเรย์ของคลาสเสริมที่ทำให้สับสนเพื่อใช้กับวาลเลย์ คุณจะได้รับslice, slice_array, gsliceและgslice_arrayจะเล่นกับชิ้นส่วนของvalarrayและทำให้มันทำหน้าที่เหมือนอาร์เรย์หลายมิติ นอกจากนี้คุณยังmask_arrayสามารถ "ปิดบัง" การดำเนินการ (เช่นเพิ่มรายการใน x ถึง y แต่เฉพาะในตำแหน่งที่ z ไม่เป็นศูนย์) ในการใช้มากกว่าเล็กน้อยvalarrayคุณต้องเรียนรู้มากมายเกี่ยวกับคลาสเสริมเหล่านี้ซึ่งบางอันค่อนข้างซับซ้อนและไม่มีเอกสารใดที่ดูเหมือน (อย่างน้อยสำหรับฉัน)

ที่บรรทัดล่าง: ในขณะที่มันมีช่วงเวลาของความฉลาดและสามารถทำบางสิ่งบางอย่างสวยอย่างประณีตมีบางเหตุผลที่ดีว่ามันเป็น (และเกือบจะแน่นอนจะยังคงอยู่) ปิดบัง

แก้ไข (แปดปีต่อมาในปี 2560): สิ่งที่กล่าวมาก่อนหน้านี้ล้าสมัยไปแล้วอย่างน้อยก็ระดับหนึ่ง ตัวอย่างหนึ่ง Intel ได้ติดตั้ง valarray รุ่นปรับปรุงสำหรับคอมไพเลอร์ ใช้การรวมประสิทธิภาพการทำงานพื้นฐานของ Intel (Intel IPP) เพื่อปรับปรุงประสิทธิภาพ แม้ว่าการปรับปรุงประสิทธิภาพการทำงานที่แน่นอนไม่ต้องสงสัยแตกต่างกันไปการทดสอบอย่างรวดเร็วกับการแสดงรหัสที่ง่ายรอบ 2: 1 การปรับปรุงในความเร็วเมื่อเทียบกับรหัสเหมือนกันรวบรวมกับการดำเนินงาน "มาตรฐาน" valarrayของ

ดังนั้นในขณะที่ฉันไม่มั่นใจว่าโปรแกรมเมอร์ C ++ จะเริ่มใช้valarrayในจำนวนมาก แต่ก็มีบางสถานการณ์ที่สามารถปรับปรุงความเร็วได้


1
มันไม่ได้รับอนุญาตเป็นการเฉพาะในการจัดเก็บชนิดของวัตถุโดยพลการในวาลเลย์
user541686

6
@ Mehrdad: ใช่ - มีรายการข้อ จำกัด (ค่อนข้างยาว) ที่ [Numeric.Requirements] สำหรับตัวอย่างสองสามคลาสที่เป็นนามธรรมและข้อยกเว้นทั้งหมดถูกห้าม นอกจากนี้ยังต้องมีความเท่าเทียมกันระหว่าง (ตัวอย่าง) การคัดลอกการก่อสร้างและลำดับของการก่อสร้างเริ่มต้นตามด้วยการมอบหมาย
Jerry Coffin

@JerryCoffin sheesh ที่น่ากลัว เราสัญญาว่าเราจะไม่ใช้มัน
Hani Goc

4
ฉันจะไม่ตัดสินใจด้วยความกลัว ฉันจะตัดสินใจโดยพิจารณาจากว่าคุณต้องจัดเก็บองค์ประกอบที่ใช้คุณสมบัติที่ห้าม
Jerry Coffin

3
@annoying_squid: หากคุณมีข้อมูลที่เฉพาะเจาะจงมากขึ้นและ (คุณเชื่อว่า) ข้อมูลที่ถูกต้องที่จะเพิ่มโปรดอย่าลังเลที่จะเพิ่มคำตอบที่แสดง ความคิดเห็นของคุณดูเหมือนจะไม่เพิ่มข้อมูลที่เป็นประโยชน์
Jerry Coffin

39

ในระหว่างการสร้างมาตรฐานของ C ++ 98 valarray ได้รับการออกแบบเพื่อให้สามารถคำนวณทางคณิตศาสตร์ได้อย่างรวดเร็ว อย่างไรก็ตามในช่วงเวลาดังกล่าว Todd Veldhuizen ได้ประดิษฐ์เทมเพลตการแสดงออกและสร้างblitz ++และเทคนิคการทำเทมเพลตเมตาดาต้าที่คล้ายกันนั้นได้ถูกประดิษฐ์ขึ้นซึ่งทำให้วาเลย์ล้าสมัยไปแล้ว IIRC ผู้เสนอดั้งเดิมของวาลเรย์ทิ้งมันลงไปครึ่งหนึ่งในมาตรฐานซึ่ง (ถ้าเป็นจริง) ก็ไม่ได้ช่วยเช่นกัน

ISTR ที่เหตุผลหลักที่ไม่ได้ลบออกจากมาตรฐานคือไม่มีใครใช้เวลาในการประเมินปัญหาอย่างละเอียดและเขียนข้อเสนอเพื่อลบออก

อย่างไรก็ตามโปรดจำไว้ว่าทั้งหมดนี้เป็นข่าวลือที่จำได้ไม่ชัด ใช้เวลานี้กับเม็ดเกลือและหวังว่าจะมีคนแก้ไขหรือยืนยันสิ่งนี้


เทมเพลตการแสดงออกสามารถให้เครดิตกับ Vandevoorde ได้เช่นกันใช่ไหม?
Nikos Athanasiou

@ Nikos: ไม่ว่าฉันรู้ ฉันอาจจะผิด แต่ คุณชอบอ่านอะไรมาก
sbi

1
ก็กล่าวถึงในหนังสือ "C ++ แม่ - คู่มือที่สมบูรณ์" ผมคิดว่ามันได้รับการยอมรับโดยทั่วไปว่าพวกเขาทั้งสองคิดค้นพวกเขาเป็นอิสระ
Nikos Athanasiou

27

ฉันรู้ว่า valarrays มีน้ำตาลประโยค

ฉันต้องบอกว่าฉันไม่คิดว่ามันstd::valarraysจะมีน้ำตาลมากนัก ไวยากรณ์แตกต่างกัน แต่ฉันจะไม่เรียกความแตกต่างว่า "น้ำตาล" API นั้นแปลก ส่วนที่อยู่std::valarrayในภาษาการเขียนโปรแกรม C ++กล่าวถึง API ที่ผิดปกตินี้และความจริงที่ว่าเนื่องจากstd::valarrays คาดว่าจะได้รับการปรับให้เหมาะสมอย่างสูงสุดข้อความแสดงข้อผิดพลาดใด ๆ ที่คุณได้รับขณะใช้งานนั้นอาจไม่ง่าย

จากความอยากรู้เกี่ยวกับปีที่ผ่านมาผมรับมือกับstd::valarray std::vectorฉันไม่มีรหัสหรือผลลัพธ์ที่แม่นยำอีกต่อไป (แม้ว่ามันจะไม่ยากในการเขียนของคุณเอง) การใช้ GCC ฉันได้รับประโยชน์ด้านประสิทธิภาพเพียงเล็กน้อยเมื่อใช้std::valarrayกับคณิตศาสตร์ง่าย ๆ แต่ไม่ใช่สำหรับการปรับใช้ของฉันในการคำนวณค่าเบี่ยงเบนมาตรฐาน (และแน่นอนว่าการเบี่ยงเบนมาตรฐานนั้นไม่ซับซ้อนเท่าที่คณิตศาสตร์ดำเนินไป) ฉันสงสัยว่าการดำเนินงานในแต่ละรายการในขนาดใหญ่std::vectorการเล่นที่ดีขึ้นกับแคชกว่าการดำเนินการบนstd::valarrays ( หมายเหตุตามคำแนะนำจาก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มันเป็นเวลากว่าห้าปีแล้ว

สำหรับ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));

วันนี้ฉันจะเขียนที่แตกต่างกันอย่างแน่นอน หากไม่มีอะไรอื่นฉันจะใช้ประโยชน์จาก lambdas C ++ 11

เป็นที่ชัดเจนว่าโค้ดสองชุดนี้ทำสิ่งที่แตกต่างกัน ตัวอย่างหนึ่งstd::vectorตัวอย่างจะไม่สร้างคอลเลกชันระดับกลางตามstd::valarrayตัวอย่าง แต่ผมคิดว่ามันยุติธรรมที่จะเปรียบเทียบพวกเขาเพราะความแตกต่างที่มีการเชื่อมโยงกับความแตกต่างระหว่างและstd::vectorstd::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;

ทำสิ่งเดียวกันกับstd::vectorตัวอย่างและมีประสิทธิภาพเกือบเหมือนกัน ในที่สุดคำถามคือสิ่งที่คุณต้องการ API


ฉันไม่สามารถคิดเหตุผลใด ๆ ที่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ขอบคุณสำหรับคำแนะนำในการทำความสะอาด ฉันจะต้องดูว่ามันช่วยเพิ่มประสิทธิภาพได้หรือไม่ สำหรับmeanความเป็นอยู่int: นั่นเป็นความผิดพลาด ตอนแรกฉันเขียนตัวอย่างโดยใช้ints แล้วก็รู้ว่าสิ่งmeanนั้นจะห่างไกลจากค่าเฉลี่ยจริงเพราะการตัดทอน แต่ฉันพลาดการเปลี่ยนแปลงที่จำเป็นเล็กน้อยในการแก้ไขรอบแรก
Max Lybbert

@musiphil คุณพูดถูก การเปลี่ยนแปลงนั้นทำให้โค้ดตัวอย่างมีประสิทธิภาพเกือบเหมือนกัน
Max Lybbert

23

valarray ควรปล่อยให้ความดีของการประมวลผลเวกเตอร์ FORTRAN ถูกับ C ++ อย่างใดการสนับสนุนคอมไพเลอร์ที่จำเป็นไม่เคยเกิดขึ้นจริง

หนังสือ Josuttis มีคำอธิบายที่น่าสนใจ (ดูหมิ่น) ในวาลเลย์ ( ที่นี่และที่นี่ )

อย่างไรก็ตามตอนนี้ Intel ดูเหมือนจะมีการทบทวน valarray ในคอมไพเลอร์รุ่นล่าสุดของพวกเขา (เช่นดูสไลด์ 9 ) นี่คือการพัฒนาที่น่าสนใจเนื่องจากชุดคำสั่ง SIMD SSE แบบ 4 ทางของพวกเขากำลังจะเข้าร่วมโดย 8-way AVX และคำสั่ง Larrabee แบบ 16 ทางและเพื่อประโยชน์ในการพกพามันน่าจะดีกว่าในการเขียนโค้ดด้วยนามธรรม วาลเลย์กว่า (พูด) อินทริน


16

ฉันพบการใช้งานที่ดีอย่างหนึ่งสำหรับวาลเลย์ มันใช้ valarray เหมือนกับอาร์เรย์ 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);

ป้อนคำอธิบายรูปภาพที่นี่

เราสามารถนำไปใช้ด้านบนด้วย 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
ฉันมีความคิดแบบเดียวกันกับที่คุณทำเมื่อฉันค้นพบเกี่ยวกับวาลเลย์วันนี้ในที่ทำงาน ฉันคิดว่าจากนี้ไปสำหรับปัญหาการประมวลผลทางคณิตศาสตร์ใน c ++ ฉันจะใช้ valarray เนื่องจากโค้ดดูง่ายกว่าที่จะอยู่ภายใต้มุมมองทางคณิตศาสตร์
Zachary Kraus

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 ซึ่งคุณมีอาร์เรย์ที่มีล้านรายการบางครั้งมีหลายสิบล้านรายการและคุณวนซ้ำในเวลาเดียวกัน บางทีวันนี้มาตรฐาน :: vector มีประสิทธิภาพเทียบเคียง แต่ประมาณ 15 ปีที่แล้ว valarray เป็นข้อบังคับเกือบหากคุณต้องการเขียนแก้ตัวเลขที่มีประสิทธิภาพ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.