जावास्क्रिप्ट में तेजी से उलटा वर्गमूल लागू करें?


11

क्वेक III से फास्ट इनवर्स स्क्वायर रूट एक फ्लोटिंग-पॉइंट ट्रिक का उपयोग करता है। जैसा कि मैं समझता हूं, फ्लोटिंग-पॉइंट प्रतिनिधित्व में कुछ अलग कार्यान्वयन हो सकते हैं।

तो क्या जावास्क्रिप्ट में फास्ट इनवर्स स्क्वायर रूट को लागू करना संभव है?

क्या यह उसी परिणाम को लौटाएगा?

float Q_rsqrt(float number) {

  long i;
  float x2, y;
  const float threehalfs = 1.5F;

  x2 = number * 0.5F;
  y = number;
  i = * ( long * ) &y;
  i = 0x5f3759df - ( i >> 1 );
  y = * ( float * ) &i;
  y = y * ( threehalfs - ( x2 * y * y ) );

  return y;

}

मुझे बताएं कि क्या यह सवाल स्टैकऑवरफ्लो पर बेहतर होगा। यह यहाँ अधिक उपयुक्त लग रहा था क्योंकि इसमें खेल देव की जड़ें हैं और ज्यादातर खेल देव अनुप्रयोग हैं।
एटाव ३२

4
जावास्क्रिप्ट में संकेत हैं?
पब्बी

2
अपने पूरे कार्यक्रम को गति देने वाले "विशेष" फ़ंक्शन का उपयोग करने की इसकी लालसा के दौरान, संभावना है कि आप बग का परिचय दें या बस चीजों को गति न दें (उदाहरण के लिए नीचे केविन रीड का जवाब देखें)। c2.com/cgi/wiki?PrematureOptimization
डैनियल कार्लसन

मुझे खेद है, लेकिन जावास्क्रिप्ट के साथ निम्न-स्तरीय एफपी ऑप्टिमाइज़ेशन का उपयोग करना ऐसा लगता है जैसे पतले रहने के लिए फ्राइज़ के साथ 4 वसा बर्गर और एक आहार कोला का आदेश देना । ऐसा मत करो, यह व्यर्थ और हास्यास्पद है।
बात नहीं

गेम के प्रोग्रामिंग में तेजी से उलटा sqrt एक बहुत ही सामान्य ऑपरेशन है, और सभी गेम कंसोल हार्डवेयर में इसे लागू करते हैं। ES6 को निश्चित रूप से भाषा में Math.fastinvsqrt (x) जोड़ने पर विचार करना चाहिए।
जॉन हेनकेल

जवाबों:


15

चाल एक पूर्णांक के रूप में फ़्लोटिंग-पॉइंट संख्या के बिट्स को फिर से स्थापित करने पर निर्भर करती है , जो कि टाइप किए गए एरे के सुविधा का उपयोग करके जावास्क्रिप्ट में संभव है , उस पर कई संख्यात्मक विचारों के साथ एक कच्ची बाइट बफर बनाने के लिए।

यहां आपके द्वारा दिए गए कोड का शाब्दिक रूपांतरण है; ध्यान दें कि यह बिल्कुल वैसा ही नहीं है, क्योंकि जावास्क्रिप्ट में सभी अंकगणितीय ऑपरेशन 64-बिट फ्लोटिंग पॉइंट हैं, न कि 32-बिट, इसलिए इनपुट आवश्यक रूप से परिवर्तित हो जाएगा। इसके अलावा, मूल कोड की तरह, यह प्लेटफ़ॉर्म-आश्रित है कि यह बकवास परिणाम देगा यदि प्रोसेसर आर्किटेक्चर एक अलग बाइट ऑर्डर का उपयोग करता है; यदि आप इस तरह की चीजें करते हैं, तो मैं सुझाव देता हूं कि आपके आवेदन से पहले यह निर्धारित करने के लिए एक परीक्षण मामले को निष्पादित करें कि पूर्णांक और फ़्लोट्स आपके द्वारा अपेक्षित बाइट का प्रतिनिधित्व करते हैं।

const bytes = new ArrayBuffer(Float32Array.BYTES_PER_ELEMENT);
const floatView = new Float32Array(bytes);
const intView = new Uint32Array(bytes);
const threehalfs = 1.5;

function Q_rsqrt(number) {
  const x2 = number * 0.5;
  floatView[0] = number;
  intView[0] = 0x5f3759df - ( intView[0] >> 1 );
  let y = floatView[0];
  y = y * ( threehalfs - ( x2 * y * y ) );

  return y;
}

मैंने एक ग्राफ को नेत्रगोलक द्वारा पुष्टि की है कि यह उचित संख्यात्मक परिणाम देता है। हालांकि, यह स्पष्ट नहीं है कि इससे प्रदर्शन में सुधार होगा, क्योंकि हम अधिक उच्च-स्तरीय जावास्क्रिप्ट संचालन कर रहे हैं। मैंने उन ब्राउज़रों पर बेंचमार्क चलाया है जो मेरे पास हैं और पाया कि अप्रैल 2018 तक मैकओएस पर क्रोम (फ़ायरफ़ॉक्स, और सफारी) Q_rsqrt(number)द्वारा लिए गए समय का 50% से 80% समय लगता है 1/sqrt(number))। यहाँ मेरा पूरा परीक्षण सेटअप है:

const {sqrt, min, max} = Math;

const bytes = new ArrayBuffer(Float32Array.BYTES_PER_ELEMENT);
const floatView = new Float32Array(bytes);
const intView = new Uint32Array(bytes);
const threehalfs = 1.5;

function Q_rsqrt(number) {
  const x2 = number * 0.5;
  floatView[0] = number;
  intView[0] = 0x5f3759df - ( intView[0] >> 1 );
  let y = floatView[0];
  y = y * ( threehalfs - ( x2 * y * y ) );

  return y;
}

// benchmark
const junk = new Float32Array(1);
function time(f) {
  const t0 = Date.now();
  f();
  const t1 = Date.now();
  return t1 - t0;
}
const timenat = time(() => { 
  for (let i = 0; i < 5000000; i++) junk[0] = 1/sqrt(i)
});
const timeq = time(() => {
  for (let i = 0; i < 5000000; i++) junk[0] = Q_rsqrt(i);
});
document.getElementById("info").textContent =
  "Native square root: " + timenat + " ms\n" +
  "Q_rsqrt: " + timeq + " ms\n" +
  "Ratio Q/N: " + timeq/timenat;

// plot results
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
function plot(f) {
  ctx.beginPath();
  const mid = canvas.height / 2;
  for (let i = 0; i < canvas.width; i++) {
    const x_f = i / canvas.width * 10;
    const y_f = f(x_f);
    const y_px = min(canvas.height - 1, max(0, mid - y_f * mid / 5));
    ctx[i == 0 ? "moveTo" : "lineTo"](i, y_px);
  }
  ctx.stroke();
  ctx.closePath();
}
ctx.strokeStyle = "black";
plot(x => 1/sqrt(x));
ctx.strokeStyle = "yellow";
plot(x => Q_rsqrt(x));
<pre id="info"></pre>
<canvas width="300" height="300" id="canvas"
        style="border: 1px solid black;"></canvas>


In classic JavaScript, it is not possible to... reinterpreting the bits of a floating-point number as an integerवास्तव में? यह वर्षों पहले था, इसलिए मुझे ठीक से याद नहीं है कि मैं कौन से ऑपरेशन का उपयोग कर रहा था, लेकिन मैंने एक बार जावास्क्रिप्ट में डेटा पार्सर लिखा था जो बाइट्स के एक स्ट्रिंग को एन-बिट (एन हेडर में परिभाषित किया गया था) पूर्णांक में बदल देगा। यह बहुत समान है।
झटकेदार
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.