Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Port to Android Toolchain #4

Merged
merged 3 commits into from
Apr 18, 2016
Merged

Port to Android Toolchain #4

merged 3 commits into from
Apr 18, 2016

Conversation

phlip9
Copy link
Contributor

@phlip9 phlip9 commented Apr 14, 2016

  • Add explicit support for Android ARM architectures.
  • Sizeof.computePointerSize() now delegates to native implementation as
    System.getProperty("sun.arch.data.model") does not exist on Android.
    Likely a more portable solution in the long run considering that we
    already rely on JNI.
  • LibInitializer.java now searches through a list of potential OpenCL
    shared library candidates. Some Android platforms that support OpenCL
    store their library under different names or in non-standard locations.
    Would this be better placed somewhere else?
  • Android shared library outputs to nativeLibraries/${ANDROID_ABI}/
    to comply with canonical Android lib location (also the only place the
    Android runtime checks when searching for a shared library through
    System.loadLibrary(...)).
  • Document building for Android. Currently describes building using a
    global installation of android-cmake. Possible to cross-compile without
    this toolchain file, but 10x more painful and 10x more edge-cases.
  • Should be merged in conjunction with the commit of the same name
    under JOCLCommon. Port to Android Toolchain JOCLCommon#3

TODO: More general cross-compiling support?

TODO: Better, more explicit support for other OS's / ARCH's?

TODO: Library would be more accessible if uploaded to maven repo. But we
also have native libraries which makes everything painful.

TODO: Refactor out native library loading to another library? There's
lots of redundancy and slight changes between this LibUtils, jcuda's
LibUtils, our LibUtils (BIDMat), etc...

+ Add explicit support for Android ARM architectures.

+ Sizeof.computePointerSize() now delegates to native implementation as
System.getProperty("sun.arch.data.model") does not exist on Android.
Likely a more portable solution in the long run considering that we
already rely on JNI.

+ LibInitializer.java now searches through a list of potential OpenCL
shared library candidates. Some Android platforms that support OpenCL
store their library under different names or in non-standard locations.
Would this be better placed somewhere else?

+ Android shared library outputs to `nativeLibraries/${ANDROID_ABI}/`
to comply with canonical Android lib location (also the only place the
Android runtime checks when searching for a shared library through
System.loadLibrary(...)).

+ Document building for Android. Currently describes building using a
global installation of android-cmake. Possible to cross-compile without
this toolchain file, but 10x more painful and 10x more edge-cases.

+ Should be merged in conjunction with the commit of the same name
under JOCLCommon.

TODO: More general cross-compiling support?
TODO: Better, more explicit support for other OS's / ARCH's?
TODO: Library would be more accessible if uploaded to maven repo. But we
also have native libraries which makes everything painful.
TODO: Refactor out native library loading to another library? There's
lots of redundancy and slight changes between this LibUtils, jcuda's
LibUtils, our LibUtils (BIDMat), etc...
+ Needed for Android build, as Android doesn't support java 8
+ See: http://stackoverflow.com/a/22981151
@gpu
Copy link
Owner

gpu commented Apr 15, 2016

Thanks for this contribution!

Actually, I created an Android version of JOCL ~4 years ago, but I had a non-disclosure-agreement with some company (which now does no longer seem to give a ... care about this topic - and the NDA was limited to 3 years, so ... the risk of being sued should be rather small). I remember that there had been some other issues. From the tip of my head, there was something related to the inheritance hierarchy of the Buffer class, but I'll have to revise this - it might be that this is no longer an issue.

I'll have to read through the changes in more detail. I'll try to do this during the weekend, and give it a try early next week (I should even be able to test it on some android device - the above mentioned company provided me one, and they never wanted it back...).

But quickly going over the changes and the points that you mentioned:

  • Support for Android ARM architectures: Should be fine
  • Sizeof.computePointerSize(): The "sun.arch.data.model" string is, according to the name, likely just a "legacy" property and too specific. But according to https://docs.oracle.com/javase/tutorial/essential/environment/sysprop.html , System.getProperty("os.arch"); should be a portable approach, and I'd prefer this to a JNI based solution (if it works on Android as well)
  • LibInitializer.java : These hard-coded paths look a bit odd, but from a quick websearch, "trying out different names" seems to be the way to go here. (This might be related to the last TODO, see below)
  • Android shared library outputs to nativeLibraries/${ANDROID_ABI}/ : I'm not entirely sure (yet) what this is about (and have I'll have to revise how I handled the native library in my first tests). Usually, the intention in JOCL is to pack the native library into the JAR, unpack it at runtime, and load it with System.load. The System.loadLibrary path is mainly for local tests and as a fallback. However, there are some open questions, also regarding Maven and the deployment of the natives (also see the TODOs below)
  • Document building for Android: During my first tests, I used hand-crafted .mk files to be fed into the NDK - it indeed was painful. And I think android-cmake did not exist back then - I'll have a closer look at this.

Regarding the TODOs:

Generalizations regarding the target platforms (and their build processes):

These are always welcome. However, they may increase the complexity of the makefiles considerably. (You might have noticed that the current issues in the issue tracker are related to "embarassingly basic" problems of building on other platforms - I hope that a robust, portable and maintainable solution can be found there soon...)


The native library deployment (and loading) is indeed an issue. There have been some related discussions in the JOCL forum. The migration of JOCL from "a downloadable ZIP" to "a Maven artifact" was a hassle. (For me, because I'm not a Maven expert, and ... native libraries are always difficult in Maven).

I'll try to summarize some points here (and consider to open one or more dedicated issues in the issue tracker for these) :

  • The current process is to build the native libraries (basically all of them), place them into the native libraries folder, and let them be packed into the JAR by Maven. At runtime, the matching native library is unpacked, and loaded with System.load (not System.loadLibrary). This has the drawback that all native libraries must be available before creating the Maven artifact. The advantage is that one can just download and drop the JAR into the classpath and it should run everywhere.
  • An alternative to the "One-JAR-With-All-Natives" approach would be to have a plain, pure Java JAR, and one JAR for each native library. These Native-JARs could then be declared as (profile-dependent) dependencies for the main JAR, and downloaded from Maven Central as they are required. The possible advantage that I see here would be that one could add the native parts individually. (Although I like the "one-JAR" solution, I'm not sure how well this would play with Android). There are other projects (JOGL, LWJGL...) that may provide some "inspiration" here.
  • (There are things like the https://github.com/maven-nar/nar-maven-plugin that should simplify the build process - but in the end, each native library has to be compiled on a different platform - so I don't see such a great benefit here)
  • The LibUtils class has evolved over time, and generalized step by step, to allow more flexibility in the library loading. Recently, I added the option to load dependencies before loading the actual JNI library (mainly for JOCLBLAS and JOCLBlast). However, it is still lacking some features that are already on my TODO list. Mainly, a proper versioning scheme. It should more easily be possible to handle different versions of the natives. Some "version number string" (with semantic versioning pattern) should likely be integrated here.
  • For the case of OpenCL, there is a special caveat: The JNI library does not link directly against the OpenCL implementation library. It links against the OpenCL.DLL, which contains the ICD (here is a recent related forum thread). In the end, this is the reason for the LibInitializer contortions, which would not be necessary for "simple" JNI bindings.
  • Of course, I already considered to extract the LibUtils into a dedicated project. (I also hate the redundancy between JOCL and JCuda, and when more features are integrated (version numbers, dependecy handling...), this becomes an even more pressing issue). But there already are approaches for this - for example, https://github.com/scijava/native-lib-loader . I have to evaluate this and compare its functionality to the LibUtils, to see whether this library could replace the LibUtils, or whether the LibUtils should indeed become a dedicated project.

(BTW: I didn't even know BIDMat, thanks for this pointer)

@phlip9
Copy link
Contributor Author

phlip9 commented Apr 15, 2016

Sizeof.computePointerSize(): The "sun.arch.data.model" string is, according to the name, likely just a "legacy" property and too specific. But according to https://docs.oracle.com/javase/tutorial/essential/environment/sysprop.html , System.getProperty("os.arch"); should be a portable approach, and I'd prefer this to a JNI based solution (if it works on Android as well).

I'm not sure this is always valid. While the system might be 64 bit, the library and/or JVM could be compiled as 32 bit. I'm not sure how exactly the "os.arch" responds to say a 32 bit vs 64 bit JVM or the precise interplay between JVM and native pointers; but (from my understanding, please correct me if I'm wrong), I would assume the native code interacting with the OpenCL API is the source of truth in this case.

LibInitializer.java : These hard-coded paths look a bit odd, but from a quick websearch, "trying out different names" seems to be the way to go here. (This might be related to the last TODO, see below)

I agree. It's definitely not pretty, but Google has not standardized OpenCL on Android (they're pushing for their own framework called Renderscript). As a result, every vendor seems to put their library in nonstandard locations...

Android shared library outputs to nativeLibraries/${ANDROID_ABI}/ : I'm not entirely sure (yet) what this is about (and have I'll have to revise how I handled the native library in my first tests). Usually, the intention in JOCL is to pack the native library into the JAR, unpack it at runtime, and load it with System.load. The System.loadLibrary path is mainly for local tests and as a fallback. However, there are some open questions, also regarding Maven and the deployment of the natives (also see the TODOs below)

Unfortunately, I don't think Android supports unpacking the library and then loading it (might have something to do with permissions?) Maybe I'm wrong, but I haven't had any luck with the extract and then load process. Android's loadLibrary method is nonstandard in that a call to loadLibrary("libfoo.so") searches not for lib/libfoo.so but rather lib/armveabi-v7a/libfoo.so and lib/arm/libfoo.so if, for example, the device is running an armv7a compatible processor. I would like a more standard and maintainable library loading method, however.

On a different note, I'm wondering whether we should classify Android as its own OS or put it under Linux in LibUtils. I'm not sure what the exact differences are, if any, between binaries compiled with the arm-linux-androideabi toolchain and the arm-linux-gnueabi toolchain.

@gpu
Copy link
Owner

gpu commented Apr 18, 2016

I'm not sure this is always valid. While the system might be 64 bit, the library and/or JVM could be compiled as 32 bit. I'm not sure how exactly the "os.arch" responds to say a 32 bit vs 64 bit JVM or the precise interplay between JVM and native pointers; but (from my understanding, please correct me if I'm wrong), I would assume the native code interacting with the OpenCL API is the source of truth in this case.

On the one hand, you are right. According to a quick websearch, os.arch returns the JRE architecture, and not the OS architecture. On the other hand, this is kind of a chicken-egg problem: The native library that will be loaded will also depend on the os.arch. The alternative would thus be to store the bitness directly in the LibUtils.ArchType enums and obtain them from ther. But frankly, the ArchType involves some guesswork as well, so I think that obtaining this information natively (and generically) is at least reasonable - maybe I'll review this if the LibUtils are refactored.

Unfortunately, I don't think Android supports unpacking the library and then loading it ...

This may be true. I had another look at my old JOCL-Android project, and noticed the libs/armeabi and libs/armeabi-v7a directories there, containing the JOCL binaries. However, this project was set up years ago. It still used the Eclipse ADT plugin and custom .mk makefiles - and admittedly, I didn't investigate all the details that are hidden behind the convenient "Install" button that pushes the APK to the device...


Regarding the pull request: I had a closer look at this, and it seems that it is safe and compatible to the existing toolchains. I reviewed the changes, tested it locally, and everything worked fine. So I merged it locally, with minor adjustments, and will push it soon.

One thing: In gpu/JOCLCommon#3 you added the -fPIC flag for Unix platforms. I think this is strongly related to #3 . Might it be that this change also resolves the issue?


Apart from that, I'm currently updating (or rather reviving) my Android infrastructure: I downloaded Android Studio and try to familiarize with it, and see how the NDK interplays with it. I have some trouble with my android device (API version 7 ... ) : It's connected to the USB for 1 hour now, and still says "Charging: 0%" - hope it's not a defect battery... However, I hope that once everything is set up, I can actually try out your changes.

@gpu
Copy link
Owner

gpu commented Apr 18, 2016

Forgot that one:

I'm wondering whether we should classify Android as its own OS or put it under Linux in LibUtils

Although it does not make a difference in the LibUtils, the difference in the LibInitializer is crucial: For Android, it needs the predefined implementation library paths. (This might also be reviewed during a refactoring of the LibUtils, but at the moment, the ANDROID type seems to be required)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants