سؤال

أحتاج إلى الحساب Math.exp() من Java بشكل متكرر، هل من الممكن الحصول على إصدار أصلي ليعمل بشكل أسرع من javaMath.exp()??

لقد حاولت فقط استخدام jni + C، ولكنه أبطأ من مجرد عادي java.

هل كانت مفيدة؟

المحلول

إجراء 1+ لكتابة تطبيق exp() الخاص بك.وهذا هو، إذا كان هذا حقًا عنق الزجاجة في طلبك.إذا كان بإمكانك التعامل مع القليل من عدم الدقة، فهناك عدد من خوارزميات تقدير الأس الفعالة للغاية، وبعضها يعود تاريخه إلى قرون مضت.كما أفهمها، فإن تطبيق Java's exp() بطيء إلى حد ما، حتى بالنسبة للخوارزميات التي يجب أن تعرض نتائج "دقيقة".

أوه، ولا تخف من كتابة تطبيق exp() بلغة Java الخالصة.لدى JNI الكثير من النفقات العامة، وJVM قادر على تحسين الكود الثانوي في وقت التشغيل في بعض الأحيان بما يتجاوز ما يستطيع C/C++ تحقيقه.

نصائح أخرى

لقد تم طلب ذلك بالفعل عدة مرات (انظر على سبيل المثال. هنا).هنا تقريب لـ Math.exp()، منسوخ من نشر هذا بلوق:

public static double exp(double val) {
    final long tmp = (long) (1512775 * val + (1072693248 - 60801));
    return Double.longBitsToDouble(tmp << 32);
}

إنه في الأساس نفس جدول البحث الذي يحتوي على 2048 إدخالاً واستيفاء خطيًا بين الإدخالات، ولكن كل هذا باستخدام حيل الفاصلة العائمة لـ IEEE.إنه أسرع بخمس مرات من Math.exp() على جهازي، لكن هذا يمكن أن يختلف بشكل كبير إذا قمت بالتجميع باستخدام -server.

استخدم جافا.

يمكنك أيضًا تخزين نتائج التجربة مؤقتًا ومن ثم يمكنك البحث عن الإجابة بشكل أسرع من حسابها مرة أخرى.

قد ترغب في إنهاء أي حلقة تنادي Math.exp() في C كذلك.وإلا فإن الحمل الزائد للتنظيم بين Java وC سوف يطغى على أي ميزة في الأداء.

قد تتمكن من تشغيله بشكل أسرع إذا قمت بذلك على دفعات.يؤدي إجراء استدعاء JNI إلى زيادة الحمل، لذا لا تريد القيام بذلك لكل exp() تحتاج إلى حسابه.سأحاول تمرير مجموعة من 100 قيمة والحصول على النتائج لمعرفة ما إذا كان ذلك يساعد في الأداء.

السؤال الحقيقي هو، هل أصبح هذا بمثابة عنق الزجاجة بالنسبة لك؟هل قمت بإعداد ملف تعريف لتطبيقك ووجدت أن هذا هو السبب الرئيسي للتباطؤ؟

إذا لم يكن الأمر كذلك، فإنني أوصي باستخدام إصدار جافا.حاول ألا تقوم بالتحسين المسبق لأن هذا سيؤدي فقط إلى تباطؤ عملية التطوير.قد تقضي وقتًا طويلًا في حل مشكلة قد لا تمثل مشكلة.

ومع ذلك، أعتقد أن اختبارك أعطاك إجابتك.إذا كان jni + C أبطأ، فاستخدم إصدار Java.

الرياضيات العامة3 يأتي مع نسخة محسنة: FastMath.exp(double x).لقد أدى ذلك إلى تسريع الكود الخاص بي بشكل ملحوظ.

فابيان أجريت بعض الاختبارات واكتشفت أنها كانت أسرع بمرتين تقريبًا Math.exp():

 0.75s for Math.exp     sum=1.7182816693332244E7
 0.40s for FastMath.exp sum=1.7182816693332244E7

هنا جافادوك:

بحساب exp(x)، يتم تقريب نتيجة الدالة تقريبًا.سيتم تقريبه بشكل صحيح إلى القيمة النظرية لـ 99.9% من قيم الإدخال، وإلا فسيكون هناك خطأ 1 UPL.

طريقة:

    Lookup intVal = exp(int(x))
    Lookup fracVal = exp(int(x-int(x) / 1024.0) * 1024.0 );
    Compute z as the exponential of the remaining bits by a polynomial minus one
    exp(x) = intVal * fracVal * (1 + z)

دقة:يتم إجراء الحساب بدقة تبلغ 63 بت، لذا يجب تقريب النتيجة بشكل صحيح لـ 99.9% من قيم الإدخال، مع وجود خطأ ULP واحد على خلاف ذلك.

نظرًا لأن كود Java سيتم تجميعه إلى كود أصلي باستخدام برنامج التحويل البرمجي في الوقت المناسب (JIT)، فلا يوجد حقًا سبب لاستخدام JNI لاستدعاء الكود الأصلي.

وأيضًا، لا يجب عليك تخزين نتائج الطريقة التي تكون فيها معلمات الإدخال عبارة عن أرقام حقيقية ذات فاصلة عائمة.المكاسب التي تم الحصول عليها في الوقت المناسب سوف تضيع كثيرًا في مقدار المساحة المستخدمة.

مشكلة استخدام JNI هي النفقات العامة التي ينطوي عليها إجراء الاتصال بـ JNI.تم تحسين جهاز Java الظاهري إلى حد كبير هذه الأيام، ويتم تحسين الاستدعاءات إلى Math.exp() المضمنة تلقائيًا للاتصال مباشرة بالدالة C exp()، وقد يتم تحسينها أيضًا إلى تجميع النقطة العائمة x87 المستقيم تعليمات.

هناك ببساطة حمل مرتبط باستخدام JNI، راجع أيضًا:http://java.sun.com/docs/books/performance/1st_edition/html/JPNativeCode.fm.html

لذلك، كما اقترح آخرون، حاول تجميع العمليات التي قد تتضمن استخدام JNI.

اكتب ما يناسبك، ومصممًا خصيصًا لاحتياجاتك.

على سبيل المثال، إذا كانت جميع الأسس الخاصة بك من قوة اثنين، فيمكنك استخدام تحويل البتات.إذا كنت تعمل بنطاق محدود أو مجموعة من القيم، فيمكنك استخدام جداول البحث.إذا لم تكن بحاجة إلى دقة بالغة، فإنك تستخدم خوارزمية غير دقيقة، ولكنها أسرع.

هناك تكلفة مرتبطة بالاتصال عبر حدود JNI.

إذا تمكنت من نقل الحلقة التي تستدعي exp() إلى الكود الأصلي أيضًا، بحيث يكون هناك استدعاء أصلي واحد فقط، فقد تحصل على نتائج أفضل، لكنني أشك في أنها ستكون أسرع بكثير من حل Java الخالص.

لا أعرف تفاصيل تطبيقك، ولكن إذا كان لديك مجموعة محدودة إلى حد ما من الوسائط المحتملة للمكالمة، فيمكنك استخدام جدول بحث محسوب مسبقًا لجعل كود Java الخاص بك أسرع.

توجد خوارزميات أسرع للتجربة اعتمادًا على ما تحاول تحقيقه.هل مساحة المشكلة تقتصر على نطاق معين، هل تحتاج فقط إلى دقة معينة أو دقة أو دقة معينة، وما إلى ذلك.

إذا قمت بتعريف مشكلتك بشكل جيد للغاية، فقد تجد أنه يمكنك استخدام جدول مع الاستيفاء، على سبيل المثال، والذي سوف يدمر أي خوارزمية أخرى تقريبًا.

ما هي القيود التي يمكنك تطبيقها على الخبرة للحصول على مقايضة الأداء؟

-آدم

أقوم بتشغيل خوارزمية مناسبة والخطأ الأدنى لنتيجة التركيب أكبر من دقة Math.exp ().

تكون الدوال المتعالية دائمًا أبطأ بكثير من الجمع أو الضرب وعنق الزجاجة المعروف.إذا كنت تعلم أن قيمك تقع في نطاق ضيق، فيمكنك ببساطة إنشاء جدول بحث (مصفوفتان مصنفتان؛مدخل واحد ومخرج واحد).استخدم Arrays.binarySearch للعثور على الفهرس الصحيح واستيفاء القيمة باستخدام العناصر الموجودة في [index] و[index+1].

هناك طريقة أخرى وهي تقسيم الرقم.لنأخذ على سبيل المثال3.81 وتقسيمها إلى 3+0.81.الآن اضرب e = 2.718 ثلاث مرات لتحصل على 20.08.

الآن إلى 0.81.جميع القيم بين 0 و 1 تتقارب بسرعة مع السلسلة الأسية المعروفة

1+x+x^2/2+x^3/6+x^4/24....إلخ.

خذ المصطلحات التي تحتاجها للحصول على الدقة؛لسوء الحظ يكون أبطأ إذا اقتربت x من 1.لنفترض أنك ذهبت إلى x^4، ثم حصلت على 2.2445 بدلاً من 2.2448 الصحيح

ثم اضرب النتيجة 2.781^3 = 20.08 مع 2.781^0.81 = 2.2445 ولديك النتيجة 45.07 مع خطأ من جزء واحد من ألفي (صحيح:45.15).

قد لا يكون ذا صلة بعد الآن، ولكن فقط لعلمك، في أحدث الإصدارات من OpenJDK (انظر هنا)، يجب أن يكون Math.exp جوهريًا (إذا كنت لا تعرف ما هو، تحقق هنا).

وهذا سيجعل الأداء لا يهزم في معظم البنيات، لأنه يعني أن Hotspot VM سيحل محل استدعاء Math.exp من خلال تطبيق خاص بالمعالج لـ exp في وقت التشغيل.لا يمكنك أبدًا التغلب على هذه المكالمات، حيث إنها مُحسَّنة للهندسة المعمارية...

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top