วัตถุประสงค์ของ NaN Boxing คืออะไร?


44

การอ่านศตวรรษที่ 21 Cฉันมาที่บทที่ 6 ในหัวข้อ"การทำเครื่องหมายตัวเลขที่เป็นตัวเลขที่ยอดเยี่ยมกับ NaNs"ซึ่งอธิบายการใช้บิตในแมนทิสซาเพื่อเก็บรูปแบบบิตโดยพลการเพื่อใช้เป็นเครื่องหมายหรือพอยน์เตอร์ WebKit ใช้เทคนิคนี้)

ฉันไม่แน่ใจจริงๆฉันเข้าใจประโยชน์ของเทคนิคนี้ว่าฉันเห็นว่าเป็นแฮ็ค (ขึ้นอยู่กับฮาร์ดแวร์ที่ไม่สนใจคุณค่าของ mantissa ใน NaN) แต่มาจากพื้นหลัง Java ที่ฉันไม่คุ้นเคย ความหยาบของ C.

นี่คือตัวอย่างของรหัสที่ตั้งและอ่านเครื่องหมายใน NaN

#include <stdio.h>
#include <math.h> //isnan

double ref;

double set_na(){
    if (!ref) {
        ref=0/0.;
        char *cr = (char *)(&ref);
        cr[2]='a';
    }
    return ref;
}

int is_na(double in){
    if (!ref) return 0;  //set_na was never called==>no NAs yet.

    char *cc = (char *)(&in);
    char *cr = (char *)(&ref);
    for (int i=0; i< sizeof(double); i++)
        if (cc[i] != cr[i]) return 0;
    return 1;
}

int main(){
    double x = set_na();
    double y = x;
    printf("Is x=set_na() NA? %i\n", is_na(x));
    printf("Is x=set_na() NAN? %i\n", isnan(x));
    printf("Is y=x NA? %i\n", is_na(y));
    printf("Is 0/0 NA? %i\n", is_na(0/0.));
    printf("Is 8 NA? %i\n", is_na(8));
}

มันพิมพ์:

Is x=set_na() NA? 1
Is x=set_na() NAN? 1
Is y=x NA? 1
Is 0/0 NA? 0
Is 8 NA? 0

และที่JSValue.h webkit อธิบายการเข้ารหัส แต่ไม่ใช่สาเหตุที่ใช้

จุดประสงค์ของเทคนิคนี้คืออะไร? ประโยชน์ของพื้นที่ / ประสิทธิภาพสูงพอที่จะสร้างสมดุลให้กับธรรมชาติของมันหรือไม่?


คุณสามารถให้ตัวอย่างง่ายๆได้หรือไม่
BЈовић

เพื่อให้ชัดเจน OP กำลังถามว่าสามารถใช้NaN เพื่อส่งสัญญาณได้อย่างไร
ratchet freak

1
@ ratchetfreak อะไรที่ทำให้คุณคิดอย่างนั้น?
Winston Ewert

@ ratchetfreak: คำถามไม่ได้เกี่ยวกับการส่งสัญญาณ NaN เนื่องจาก webkit JSValue.h อธิบายว่า แต่ขอขอบคุณที่ให้ฉันค้นพบสิ่งใหม่!
andijcr

1
@Hudson isnan () si ใช้ใน printf ที่สองใน main วัตถุประสงค์ของ is_an () คือการทดสอบว่ารูปแบบบิตของอินพุตคู่เท่ากับที่บันทึกไว้ภายในตัวแปรส่วนกลาง
andijcr

คำตอบ:


63

เมื่อคุณใช้ภาษาที่พิมพ์แบบไดนามิกคุณจะต้องมีประเภทเดียวที่สามารถเก็บวัตถุใด ๆ ของคุณ มีสามวิธีที่แตกต่างกันที่ฉันรู้เรื่องนี้:

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

ข้อเสียคือค่าขนาดเล็กเช่นตัวเลขถูกเก็บไว้เป็นค่าที่บรรจุอยู่ดังนั้นค่า 5 เล็กน้อยของคุณจะถูกเก็บเป็นบล็อกหน่วยความจำที่ไหนซักแห่ง ดังนั้นสิ่งนี้นำเราไปสู่แนวทางสหภาพซึ่งใช้โดยลัวะ แทนที่จะเป็น a PyObject*แต่ละค่าจะเป็นโครงสร้างที่ฟิลด์ใดฟิลด์หนึ่งเพื่อระบุชนิดและจากนั้นจะรวมยูเนี่ยนของประเภทที่รองรับที่แตกต่างกันทั้งหมด ด้วยวิธีนี้เราหลีกเลี่ยงการจัดสรรหน่วยความจำสำหรับค่าเล็ก ๆ แทนที่จะเก็บไว้ในสหภาพ

NaNทุกอย่างที่ร้านค้าวิธีการเป็นคู่และ reuses ส่วนที่ไม่ได้ใช้NaNสำหรับการจัดเก็บพิเศษ ข้อได้เปรียบเหนือเมธอด union คือการที่เราบันทึกฟิลด์ประเภท หากเป็น double ที่ถูกต้องมันจะเป็น double มิฉะนั้น mantissa จะเป็นตัวชี้ไปยังวัตถุจริง

จำไว้ว่านี่คือทุกวัตถุจาวาสคริปต์ ทุกตัวแปรทุกค่าในวัตถุทุกนิพจน์ ถ้าเราสามารถลดทั้งหมดจาก 96 บิตเป็น 64 บิตที่น่าประทับใจมาก

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


2
CPython จริง ๆ แล้วแคชจำนวนน้อย ดูhg.python.org/cpython/file/e6cc582cafce/Objects/longobject.c
Phillip Cloud

1
@cpcloud จริง แต่รายละเอียดนั้นดูเหมือนจะไม่เกี่ยวข้อง
Winston Ewert

1
@Winston ให้คุณพูดถูก ฉันคิดในสิ่งเดียวกันหลังจากอ่านสิ่งที่ฉันเขียน
Phillip Cloud

2
การใช้บิตประเภทดั้งเดิมเพื่อหลีกเลี่ยง "มวย" ค่าทั้งหมดเป็นเทคนิคที่ให้เกียรติเวลา สมอลล์ทอล์คใช้มันในปี 1970 ขโมยหนึ่งบิตจากจำนวนเต็ม 16 บิตที่จะส่งสัญญาณทั้งชี้วัตถุหรือ SmallInteger15
Jonathan Eunice

2
@ JonathanEunice จริงเหรอ? นั่นเป็นเรื่องที่ทำให้ฉันประหลาดใจเพราะมีช่วงไม่มากนักใน 16 บิตที่ฉันยินดียอมแพ้
Winston Ewert

7

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

เทคนิคนี้มีข้อผิดพลาดบางอย่างแน่นอน (ดูที่นี่http://ppkwok.blogspot.co.uk/2012/11/java-cafe-1-never-write-nan-nan_24.html ) แต่ในภาษาเช่น Java ( หรือคล้ายกันมาก C #) มีฟังก์ชั่นไลบรารีมาตรฐานที่ต้องการFloat.isNaNทำให้การจัดการกับ NaN ง่ายขึ้น แน่นอนใน Java คุณสามารถใช้หรือFloatและDoubleคลาสและใน C # ประเภทค่า nullable float?และdouble?ทำให้คุณมีความเป็นไปได้ของการใช้nullแทน NaN สำหรับตัวเลขจุดลอยตัวที่ไม่ถูกต้อง แต่เทคนิคเหล่านั้นมีอิทธิพลเชิงลบอย่างมีนัยสำคัญต่อประสิทธิภาพและหน่วยความจำ การใช้งานโปรแกรมของคุณ

ใน C การใช้ NaN นั้นไม่ใช่แบบพกพา 100% ซึ่งเป็นเรื่องจริง แต่คุณสามารถใช้ได้ทุกที่ที่มีมาตรฐาน IEEE 754 floating point AFAIK นี่เป็นฮาร์ดแวร์หลักเกือบทุกตัวในปัจจุบัน (หรืออย่างน้อยที่สุดสภาพแวดล้อมรันไทม์ของคอมไพเลอร์ส่วนใหญ่ก็สนับสนุน) ตัวอย่างเช่นโพสต์ SOนี้มีข้อมูลบางอย่างเพื่อค้นหารายละเอียดเพิ่มเติมเกี่ยวกับการใช้ NaN ใน C


auto-Boxing ใน java ยุ่งและควรหลีกเลี่ยงเพียงแค่ใช้มันเพื่อให้ค่า null นั้นไร้สาระและมีแนวโน้มที่จะเกิดข้อผิดพลาด
ratchet freak

ฉันแก้ไขคำถามเพื่อเชื่อมโยงไปยังที่ webkit ใช้ NaN-Boxing ดูเหมือนว่า webkit มีการใช้ NaN ในวงกว้างมากกว่าการส่งสัญญาณ 'NaN'
andijcr

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