سؤال

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

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

كانت فكرتي هي تقليل الحمل الناتج عن نسخ البيانات من وإلى وحدة معالجة الرسومات عن طريق ترك البيانات في ذاكرة وحدة معالجة الرسومات وتمرير مرجع إليها حتى تتمكن الوظائف الأخرى من استخدامها.

بعد تجربة بعض الوقت، فكرت بنفسي، لا ينبغي أن يكون هذا ممكنًا، لأنه يتم حذف المؤشرات بعد انتهاء التطبيق (في هذه الحالة، عندما تنتهي الدالة C).هل هذا صحيح؟أم أنني سيئ جدًا في لغة C لأرى الحل؟

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

شكرا لمساهمتك :)

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

المحلول

ولقد استخدمت النهج التالي:

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

والهيكل سيكون في كومة، لذلك لن يتم المصادقة عليها من بين مختلف JNI يدعو.

وتحرير: أنا لا أعتقد أنه يمكنك استخدام فترة طويلة PTR = (long)&address; منذ العنوان هو متغير ثابت. تستخدم هذه هي الطريقة اقترح Gunslinger47، أي إنشاء مثيل جديد من فئة أو بنية (باستخدام جديدة أو malloc) وتمرير مؤشر لها.

نصائح أخرى

في C++، يمكنك استخدام أي آلية تريد تخصيصها/تحرير الذاكرة:المكدس، malloc/مجاني، جديد/حذف أو أي تطبيق مخصص آخر.الشرط الوحيد هو أنه إذا قمت بتخصيص كتلة من الذاكرة بآلية واحدة، فيجب عليك تحريرها بنفس الآلية، لذلك لا يمكنك الاتصال free على متغير مكدس ولا يمكنك الاتصال به delete على mallocذاكرة إد.

لدى JNI آلياتها الخاصة لتخصيص/تحرير ذاكرة JVM:

  • كائن جديد/حذف LocalRef
  • نيوجلوبالريف/حذفجلوبالريف
  • NewWeakGlobalRef/DeleteWeakGlobalRef

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

لا تعرف JNI كيفية تخصيص ذاكرتك، لذا لا يمكنها تحريرها عند خروج وظيفتك.من الواضح أنه سيتم تدمير متغيرات المكدس لأنك لا تزال تكتب C++، لكن ذاكرة GPU الخاصة بك ستظل صالحة.

المشكلة الوحيدة إذن هي كيفية الوصول إلى الذاكرة عند الاستدعاءات اللاحقة، ومن ثم يمكنك استخدام اقتراح Gunslinger47:

JNIEXPORT jlong JNICALL Java_MyJavaClass_Function1() {
    MyClass* pObject = new MyClass(...);
    return (long)pObject;
}

JNIEXPORT void JNICALL Java_MyJavaClass_Function2(jlong lp) {
    MyClass* pObject = (MyClass*)lp;
    ...
}
سوف

وجافا لا تعرف ماذا تفعل مع مؤشر، ولكن يجب أن تكون قادرة على تخزين مؤشر من قيمة الإرجاع وظيفة الأم وثم تسليمه إلى وظيفة الأم آخر له للتعامل معها. C المؤشرات ليست أكثر من القيم الرقمية في الصميم.

وcontibutor آخر لا بد أن أقول لكم ما إذا كان أشار إلى الرسومات سوف يتم مسح الذاكرة بين الدعاء JNI وإذا كان سيكون هناك أي-احترف العمل.

وأنا أعلم تم بالفعل أجاب على هذا السؤال رسميا، ولكن أود أن أضم الحل: وبدلا من محاولة تمرير مؤشر، وضع المؤشر في مجموعة جافا (في الفهرس 0) وتمرير ذلك إلى JNI. كود JNI يمكن الحصول على وتعيين عنصر صفيف باستخدام GetIntArrayRegion / SetIntArrayRegion.

في قانون بلدي، أنا في حاجة إلى طبقة الأم لإدارة واصف ملف (مأخذ مفتوح). فئة Java يحمل مجموعة int[1] ويمرره إلى وظيفة الأم. وظيفة الأم تستطيع أن تفعل أي شيء معها (الحصول على / مجموعة) واعادة وضع النتيجة في مجموعة.

إذا كنت تخصيص الذاكرة حيوي (على كومة) من داخل وظيفة الأم، لا يتم حذفها. وبعبارة أخرى، كنت قادرا على الاحتفاظ الدولة بين المكالمات مختلفة إلى وظائف الأم، وذلك باستخدام مؤشرات، فار ثابت، الخ.

وأنها تفكر في طريقة مختلفة: ماذا يمكن أن لا تبقي بأمان في استدعاء دالة، ودعا من برنامج آخر C ++؟ الأشياء نفسها تنطبق هنا. عندما خرجت وظيفة، يتم إتلاف أي شيء على كومة لتلك الدعوة وظيفة. ولكن أي شيء على كومة يتم الاحتفاظ إلا إذا قمت بحذفه بشكل واضح.

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

وعلى الرغم من أن الإجابة مقبولة من @ دينيس tulskiy لا معنى له، لقد تابعت personnally اقتراحات من <لأ href = "https://rkennke.wordpress.com/2007/07/30/efficient-jni-programming- رابعا: التفاف الناطقين البيانات الأجسام / "يختلط =" نوفولو noreferrer "> هنا .

وهكذا بدلا من استخدام نوع الزائفة مؤشر مثل jlong (أو jint إذا كنت تريد حفظ بعض المساحة على 32bits قوس)، استخدم بدلا من ذلك ByteBuffer. على سبيل المثال:

MyNativeStruct* data; // Initialized elsewhere.
jobject bb = (*env)->NewDirectByteBuffer(env, (void*) data, sizeof(MyNativeStruct));

والتي يمكنك لاحقا إعادة استخدام مع:

jobject bb; // Initialized elsewhere.
MyNativeStruct* data = (MyNativeStruct*) (*env)->GetDirectBufferAddress(env, bb);

لحالات بسيطة جدا، وهذا الحل سهل جدا للاستخدام. افترض أن لديك:

struct {
  int exampleInt;
  short exampleShort;
} MyNativeStruct;

في الجانب جافا، يمكنك ببساطة عليك القيام به:

public int getExampleInt() {
  return bb.getInt(0);
}

public short getExampleShort() {
  return bb.getShort(4);
}

والذي يوفر لك من كتابة <م> الكثير من التعليمات البرمجية النمطي! ولكن ينبغي للمرء أن تولي اهتماما لترتيب بايت كما هو موضح هنا .

وسعها أن تفعل هذا بالضبط كيف Unsafe.allocateMemory يفعل.

إنشاء الكائن الخاص بك، ثم اكتبه ل(uintptr_t) وهو عدد صحيح غير موقعة 32/64 بت.

return (uintptr_t) malloc(50);

void * f = (uintptr_t) jlong;

وهذه هي الطريقة الصحيحة الوحيدة للقيام بذلك.

وهنا هو التعقل فحص Unsafe.allocateMemory يفعل.

inline jlong addr_to_java(void* p) {
  assert(p == (void*)(uintptr_t)p, "must not be odd high bits");
  return (uintptr_t)p;
}

UNSAFE_ENTRY(jlong, Unsafe_AllocateMemory(JNIEnv *env, jobject unsafe, jlong size))
  UnsafeWrapper("Unsafe_AllocateMemory");
  size_t sz = (size_t)size;
  if (sz != (julong)size || size < 0) {
    THROW_0(vmSymbols::java_lang_IllegalArgumentException());
  }
  if (sz == 0) {
    return 0;
  }
  sz = round_to(sz, HeapWordSize);
  void* x = os::malloc(sz, mtInternal);
  if (x == NULL) {
    THROW_0(vmSymbols::java_lang_OutOfMemoryError());
  }
  //Copy::fill_to_words((HeapWord*)x, sz / HeapWordSize);
  return addr_to_java(x);
UNSAFE_END
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top