استخدم JNI بدلاً من JNA لاستدعاء التعليمات البرمجية الأصلية؟

StackOverflow https://stackoverflow.com/questions/1556421

سؤال

يبدو أن JNA أسهل قليلاً في استخدام الكود الأصلي مقارنة بـ JNI. في أي حالات ستستخدم JNI على JNA؟

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

المحلول

  1. لا تدعم JNA تعيين فئات C ++ ، لذلك إذا كنت تستخدم مكتبة C ++ ، فستحتاج إلى ملف JNI
  2. إذا كنت بحاجة إلى الكثير من نسخ الذاكرة. على سبيل المثال ، يمكنك استدعاء طريقة واحدة تعيد لك مخزنًا كبيرًا للبايت ، يمكنك تغيير شيء ما فيه ، ثم تحتاج إلى استدعاء طريقة أخرى تستخدم هذا المخزن المؤقت للبايت. سيتطلب منك ذلك نسخ هذا المخزن المؤقت من C إلى Java ، ثم نسخه من Java إلى C. في هذه الحالة ، سيفوز JNI في الأداء لأنه يمكنك الاحتفاظ بهذا العازلة وتعديلها في C ، دون نسخ.

هذه هي المشاكل التي واجهتها. ربما هناك المزيد. ولكن في الأداء العام لا يختلف عن JNA و JNI ، لذلك أينما يمكنك استخدام JNA ، استخدمه.

تعديل

يبدو أن هذه الإجابة تحظى بشعبية كبيرة. إذن هذه بعض الإضافات:

  1. إذا كنت بحاجة إلى تعيين C ++ أو COM ، فهناك مكتبة من تأليف Oliver Chafic ، منشئ Jnaerator ، يسمى بريدج. إنها لا تزال مكتبة شابة ، لكنها تحتوي على العديد من الميزات المثيرة للاهتمام:
    • Dynamic C / C ++ / COM Interop: Call C ++ طرق ، قم بإنشاء كائنات C ++ (وفئات C ++ الفرعية من Java!)
    • تعيينات من النوع المباشر مع استخدام جيد للوحدين (بما في ذلك الكثير من النموذج الجميل للمؤشرات)
    • دعم jnaerator الكامل
    • يعمل على Windows و Linux و MacOS X و Solaris و Android
  2. أما بالنسبة لنسخ الذاكرة ، أعتقد أن JNA تدعم البايتات المباشرة ، بحيث يمكن تجنب نسخ الذاكرة.

لذلك ، ما زلت أعتقد أنه حيثما كان ذلك ممكنًا ، من الأفضل استخدام JNA أو Bridj ، والعودة إلى JNI إذا كان الأداء أمرًا بالغ الأهمية ، لأنه إذا كنت بحاجة إلى استدعاء الوظائف الأصلية بشكل متكرر ، فإن أداء الأداء ملحوظ.

نصائح أخرى

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

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

  1. أنت تكتب رمزًا قبل بضع سنوات قبل أن يكون هناك JNA أو تستهدف 1.4 JRE.
  2. الرمز الذي تعمل معه ليس في DLL SO.
  3. أنت تعمل على رمز لا يتوافق مع LGPL.

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

بالمناسبة ، في أحد مشاريعنا ، حافظنا على طباعة صغيرة جدًا من جني. استخدمنا المخازن المؤقتة للبروتوكول لتمثيل كائنات المجال الخاصة بنا ، وبالتالي كان لدينا وظيفة أصلية واحدة فقط لسد Java و C (ثم بالطبع ستسمي الوظيفة C مجموعة من الوظائف الأخرى).

إنها ليست إجابة مباشرة وليس لدي خبرة مع JNA ، لكن عندما أنظر إلى مشاريع باستخدام JNA وشاهد أسماء مثل Svnkit ، Idea Intellij ، NetBeans IDE ، إلخ ، أنا أميل إلى الاعتقاد بأنها مكتبة لائقة إلى حد ما.

في الواقع ، أعتقد بالتأكيد أنني كنت سأستخدم JNA بدلاً من JNI عندما اضطررت إلى ذلك لأنها تبدو أبسط من JNI (التي لديها عملية تطوير مملة). سيء للغاية ، لم يتم إطلاق JNA في هذا الوقت.

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

native "C++" void printHello() {
    const char* helloWorld = "Hello, World!";
    `System.out.println(#$(helloWorld));`
}

ثم تقوم جانيت بترجمة Java المدمجة إلى مكالمات JNI المناسبة.

لقد قمت بالفعل ببعض المعايير البسيطة مع JNI و JNA.

كما أشار آخرون بالفعل ، JNA هي للراحة. لا تحتاج إلى تجميع أو كتابة التعليمات البرمجية الأصلية عند استخدام JNA. يعد Loader الأصلي لـ JNA أيضًا أحد أفضل/أسهل استخدامه على الإطلاق. للأسف ، لا يمكنك استخدامه لجني على ما يبدو. (لهذا السبب كتبت بديل للنظام. loadlibrary () التي تستخدم اتفاقية مسار JNA ويدعم التحميل السلس من classpath (أي الجرار).).

ومع ذلك ، يمكن أن يكون أداء JNA أسوأ بكثير من أداء JNI. لقد أجريت اختبارًا بسيطًا جدًا يسمى وظيفة زيادة عدد صحيح أصلي "Return Arg + 1 ؛". أظهرت المعايير التي تم إجراؤها مع JMH أن نداءات JNI إلى هذه الوظيفة أسرع 15 مرة من JNA.

مثال "أكثر تعقيدًا" حيث تلخص الوظيفة الأصلية مجموعة عدد صحيح من 4 قيم لا يزال يظهر أن أداء JNI أسرع 3 مرات من JNA. ربما كانت الميزة المخفضة بسبب كيفية الوصول إلى المصفوفات في JNI: لقد أنشأت مثال بعض الأشياء وأطلقها مرة أخرى خلال كل عملية تلخيص.

يمكن العثور على نتائج الكود والاختبار في جيثب.

لقد بحثت في JNI و JNA لمقارنة الأداء لأننا بحاجة إلى تحديد أحدهما للاتصال بـ DLL في المشروع وكان لدينا قيد في الوقت الفعلي. وقد أظهرت النتائج أن JNI لديها أداء أكبر من JNA (حوالي 40 مرة). ربما تكون هناك خدعة لأداء أفضل في JNA ، لكنها بطيئة للغاية بالنسبة لمثال بسيط.

ما لم أفتقد شيئًا ما ، أليس الفرق الرئيسي بين JNA vs JNI أنه مع JNA ، لا يمكنك استدعاء رمز Java من الرمز الأصلي (C)؟

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