mirror of
https://github.com/apache/arrow.git
synced 2026-10-29 00:56:31 +08:00
Interface refactor and performance optimization
- refactor JNI interface to make the underlying resource id explicit - add concurrent map for resource management - hold reference to class and method field to avoid unnecessary overhead
This commit is contained in:
1 parent
3604c24d33
commit
1b6a7042aa
13 files changed
+412
-291
No files matched your search
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
*/
|
||||
|
||||
#ifndef JNI_ID_TO_MODULE_MAP_H
|
||||
#define JNI_ID_TO_MODULE_MAP_H
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
namespace arrow {
|
||||
|
||||
template <typename HOLDER>
|
||||
class concurrentMap {
|
||||
public:
|
||||
concurrentMap() : module_id_(kInitModuleId) {}
|
||||
|
||||
jlong Insert(HOLDER holder) {
|
||||
mtx_.lock();
|
||||
jlong result = module_id_++;
|
||||
map_.insert(std::pair<jlong, HOLDER>(result, holder));
|
||||
mtx_.unlock();
|
||||
return result;
|
||||
}
|
||||
|
||||
void Erase(jlong module_id) {
|
||||
mtx_.lock();
|
||||
map_.erase(module_id);
|
||||
mtx_.unlock();
|
||||
}
|
||||
|
||||
HOLDER Lookup(jlong module_id) {
|
||||
HOLDER result = nullptr;
|
||||
try {
|
||||
result = map_.at(module_id);
|
||||
} catch (const std::out_of_range &e) {
|
||||
}
|
||||
if (result != nullptr) {
|
||||
return result;
|
||||
}
|
||||
mtx_.lock();
|
||||
try {
|
||||
result = map_.at(module_id);
|
||||
} catch (const std::out_of_range &e) {
|
||||
}
|
||||
mtx_.unlock();
|
||||
return result;
|
||||
}
|
||||
|
||||
void Clear() {
|
||||
mtx_.lock();
|
||||
map_.clear();
|
||||
mtx_.unlock();
|
||||
}
|
||||
|
||||
private:
|
||||
static const int kInitModuleId = 4;
|
||||
|
||||
long module_id_;
|
||||
std::mutex mtx_;
|
||||
// map from module ids returned to Java and module pointers
|
||||
std::unordered_map<jlong, HOLDER> map_;
|
||||
};
|
||||
|
||||
} // namespace gandiva
|
||||
|
||||
#endif // JNI_ID_TO_MODULE_MAP_H
|
||||
@@ -28,140 +28,198 @@
|
||||
#include "org_apache_arrow_adapter_orc_OrcReaderJniWrapper.h"
|
||||
#include "org_apache_arrow_adapter_orc_OrcStripeReaderJniWrapper.h"
|
||||
|
||||
#include "concurrent_map.h"
|
||||
|
||||
using ORCFileReader = arrow::adapters::orc::ORCFileReader;
|
||||
using RecordBatchReader = arrow::RecordBatchReader;
|
||||
|
||||
void ThrowJavaIOException(JNIEnv* env, const std::exception& e) {
|
||||
jclass ioExceptionClass = env->FindClass("java/io/IOException");
|
||||
if (ioExceptionClass != nullptr) {
|
||||
if (env->ThrowNew(ioExceptionClass, e.what())) {
|
||||
// Failed to new IOException. This means another error has occurred in Java
|
||||
// We just propagate this error to caller by doing nothing.
|
||||
ARROW_LOG(ERROR) << "Error occurred when throwing IOException";
|
||||
}
|
||||
} else {
|
||||
ARROW_LOG(ERROR) << "Error occurred when getting IOException class";
|
||||
}
|
||||
static jclass io_exception_class;
|
||||
static jclass exception_class;
|
||||
|
||||
static jclass orc_field_node_class;
|
||||
static jmethodID orc_field_node_constructor;
|
||||
|
||||
static jclass orc_memory_class;
|
||||
static jmethodID orc_memory_constructor;
|
||||
|
||||
static jclass record_batch_class;
|
||||
static jmethodID record_batch_constructor;
|
||||
|
||||
static jint JNI_VERSION = JNI_VERSION_1_6;
|
||||
|
||||
static arrow::concurrentMap<std::shared_ptr<arrow::Buffer>> buffer_holder_;
|
||||
static arrow::concurrentMap<std::shared_ptr<RecordBatchReader>> orc_stripe_reader_holder_;
|
||||
static arrow::concurrentMap<std::shared_ptr<ORCFileReader>> orc_file_reader_holder_;
|
||||
|
||||
jclass CreateGlobalClassReference(JNIEnv* env, const char* class_name) {
|
||||
jclass local_class = env->FindClass(class_name);
|
||||
jclass global_class = (jclass)env->NewGlobalRef(local_class);
|
||||
env->DeleteLocalRef(local_class);
|
||||
return global_class;
|
||||
}
|
||||
|
||||
void ThrowJavaException(JNIEnv* env, const std::string& message) {
|
||||
jclass exception = env->FindClass("java/lang/Exception");
|
||||
if (exception != nullptr) {
|
||||
env->ThrowNew(exception, message.c_str());
|
||||
} else {
|
||||
throw std::runtime_error("Can't find java/lang/Exception class");
|
||||
}
|
||||
}
|
||||
|
||||
jfieldID GetFieldId(JNIEnv* env, jclass this_class, const std::string& sig) {
|
||||
jfieldID ret = env->GetFieldID(this_class, sig.c_str(), "J");
|
||||
jmethodID GetMethodID(JNIEnv* env, jclass this_class, const char* name, const char* sig) {
|
||||
jmethodID ret = env->GetMethodID(this_class, name, sig);
|
||||
if (ret == nullptr) {
|
||||
ThrowJavaException(env, "Unable to get java class field: " + sig);
|
||||
std::string error_message = "Unable to find method " + std::string(name) +
|
||||
" within signature" + std::string(sig);
|
||||
env->ThrowNew(exception_class, error_message.c_str());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::unique_ptr<ORCFileReader>* GetNativeReader(JNIEnv* env, jobject this_obj) {
|
||||
jlong reader = env->GetLongField(
|
||||
this_obj, GetFieldId(env, env->GetObjectClass(this_obj), "nativeReaderAddress"));
|
||||
return reinterpret_cast<std::unique_ptr<ORCFileReader>*>(reader);
|
||||
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
||||
JNIEnv* env;
|
||||
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION) != JNI_OK) {
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
io_exception_class = CreateGlobalClassReference(env, "java/io/IOException");
|
||||
exception_class = CreateGlobalClassReference(env, "java/lang/Exception");
|
||||
|
||||
orc_field_node_class =
|
||||
CreateGlobalClassReference(env, "/org/apache/arrow/adapter/orc/OrcFieldNode");
|
||||
orc_field_node_constructor = GetMethodID(env, orc_field_node_class, "<init>", "(II)V");
|
||||
|
||||
orc_memory_class = CreateGlobalClassReference(
|
||||
env, "/org/apache/arrow/adapter/orc/OrcMemoryJniWrapper");
|
||||
orc_memory_constructor = GetMethodID(env, orc_memory_class, "<init>", "(JJJJ)V");
|
||||
|
||||
record_batch_class =
|
||||
CreateGlobalClassReference(env, "/org/apache/arrow/adapter/orc/OrcRecordBatch");
|
||||
record_batch_constructor = GetMethodID(env, record_batch_class, "<init>",
|
||||
"(I[L/org/apache/arrow/adapter/orc/OrcFieldNode;"
|
||||
"[L/org/apache/arrow/adapter/orc/OrcMemoryJniWrapper;)V");
|
||||
|
||||
env->ExceptionDescribe();
|
||||
|
||||
return JNI_VERSION;
|
||||
}
|
||||
|
||||
std::shared_ptr<RecordBatchReader>* GetStripeReader(JNIEnv* env, jobject this_obj) {
|
||||
jlong reader = env->GetLongField(
|
||||
this_obj,
|
||||
GetFieldId(env, env->GetObjectClass(this_obj), "nativeStripeReaderAddress"));
|
||||
return reinterpret_cast<std::shared_ptr<RecordBatchReader>*>(reader);
|
||||
void JNI_OnUnload(JavaVM* vm, void* reserved) {
|
||||
JNIEnv* env;
|
||||
vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION);
|
||||
env->DeleteGlobalRef(io_exception_class);
|
||||
env->DeleteGlobalRef(exception_class);
|
||||
env->DeleteGlobalRef(orc_field_node_class);
|
||||
env->DeleteGlobalRef(orc_memory_class);
|
||||
env->DeleteGlobalRef(record_batch_class);
|
||||
|
||||
buffer_holder_.Clear();
|
||||
orc_stripe_reader_holder_.Clear();
|
||||
orc_file_reader_holder_.Clear();
|
||||
}
|
||||
|
||||
std::shared_ptr<ORCFileReader> GetNativeReader(jlong id) {
|
||||
return orc_file_reader_holder_.Lookup(id);
|
||||
}
|
||||
|
||||
std::shared_ptr<RecordBatchReader> GetStripeReader(jlong id) {
|
||||
return orc_stripe_reader_holder_.Lookup(id);
|
||||
}
|
||||
|
||||
int jstr_to_cstr(JNIEnv* env, jstring jstr, char* cstr, size_t cstr_len) {
|
||||
int32_t jlen, clen;
|
||||
|
||||
clen = env->GetStringUTFLength(jstr);
|
||||
if (clen > (int32_t)cstr_len) return -ENAMETOOLONG;
|
||||
jlen = env->GetStringLength(jstr);
|
||||
env->GetStringUTFRegion(jstr, 0, jlen, cstr);
|
||||
if (env->ExceptionCheck()) return -EIO;
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string JStringToCString(JNIEnv* env, jstring string) {
|
||||
int32_t jlen, clen;
|
||||
clen = env->GetStringUTFLength(string);
|
||||
jlen = env->GetStringLength(string);
|
||||
std::vector<char> buffer(clen);
|
||||
env->GetStringUTFRegion(string, 0, jlen, buffer.data());
|
||||
return std::string(buffer.data(), clen);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
JNIEXPORT jboolean JNICALL Java_org_apache_arrow_adapter_orc_OrcReaderJniWrapper_open(
|
||||
JNIEnv* env, jobject this_obj, jstring file_path) {
|
||||
JNIEXPORT jlong JNICALL Java_org_apache_arrow_adapter_orc_OrcReaderJniWrapper_open(
|
||||
JNIEnv* env, jclass this_cls, jstring file_path) {
|
||||
std::shared_ptr<arrow::io::ReadableFile> in_file;
|
||||
const char* str = env->GetStringUTFChars(file_path, nullptr);
|
||||
std::string path(str);
|
||||
env->ReleaseStringUTFChars(file_path, str);
|
||||
|
||||
std::string path = JStringToCString(env, file_path);
|
||||
|
||||
arrow::Status ret;
|
||||
if (path.find("hdfs://") == 0) {
|
||||
return false;
|
||||
env->ThrowNew(io_exception_class, "hdfs path not support yet.");
|
||||
} else {
|
||||
ret = arrow::io::ReadableFile::Open(path, &in_file);
|
||||
}
|
||||
|
||||
if (ret.ok()) {
|
||||
auto reader = new std::unique_ptr<ORCFileReader>();
|
||||
std::unique_ptr<ORCFileReader> reader;
|
||||
|
||||
ret = ORCFileReader::Open(
|
||||
std::static_pointer_cast<arrow::io::RandomAccessFile>(in_file),
|
||||
arrow::default_memory_pool(), reader);
|
||||
arrow::default_memory_pool(), &reader);
|
||||
|
||||
if (!ret.ok()) {
|
||||
delete reader;
|
||||
ThrowJavaIOException(env, std::invalid_argument("Failed open file" + path));
|
||||
env->ThrowNew(io_exception_class, std::string("Failed open file" + path).c_str());
|
||||
}
|
||||
|
||||
env->SetLongField(
|
||||
this_obj, GetFieldId(env, env->GetObjectClass(this_obj), "nativeReaderAddress"),
|
||||
reinterpret_cast<long>(reader));
|
||||
return orc_file_reader_holder_.Insert(
|
||||
std::shared_ptr<ORCFileReader>(reader.release()));
|
||||
}
|
||||
|
||||
return ret.ok();
|
||||
return static_cast<jlong>(ret.code()) * -1;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_apache_arrow_adapter_orc_OrcReaderJniWrapper_close(
|
||||
JNIEnv* env, jobject this_obj) {
|
||||
delete GetNativeReader(env, this_obj);
|
||||
JNIEnv* env, jclass this_cls, jlong id) {
|
||||
orc_file_reader_holder_.Erase(id);
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL Java_org_apache_arrow_adapter_orc_OrcReaderJniWrapper_seek(
|
||||
JNIEnv* env, jobject this_obj, jint row_number) {
|
||||
auto reader = GetNativeReader(env, this_obj);
|
||||
return (*reader)->Seek(row_number).ok();
|
||||
JNIEnv* env, jclass this_cls, jlong id, jint row_number) {
|
||||
auto reader = orc_file_reader_holder_.Lookup(id);
|
||||
return reader->Seek(row_number).ok();
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_org_apache_arrow_adapter_orc_OrcReaderJniWrapper_getNumberOfStripes(
|
||||
JNIEnv* env, jobject this_obj) {
|
||||
auto reader = GetNativeReader(env, this_obj);
|
||||
return (*reader)->NumberOfStripes();
|
||||
Java_org_apache_arrow_adapter_orc_OrcReaderJniWrapper_getNumberOfStripes(JNIEnv* env,
|
||||
jclass this_cls,
|
||||
jlong id) {
|
||||
auto reader = orc_file_reader_holder_.Lookup(id);
|
||||
return reader->NumberOfStripes();
|
||||
}
|
||||
|
||||
JNIEXPORT jobject JNICALL
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_org_apache_arrow_adapter_orc_OrcReaderJniWrapper_nextStripeReader(JNIEnv* env,
|
||||
jobject this_obj,
|
||||
jclass this_cls,
|
||||
jlong id,
|
||||
jlong batch_size) {
|
||||
auto reader = GetNativeReader(env, this_obj);
|
||||
auto stripe_reader = new std::shared_ptr<RecordBatchReader>();
|
||||
auto status = (*reader)->NextStripeReader(batch_size, stripe_reader);
|
||||
auto reader = GetNativeReader(id);
|
||||
std::shared_ptr<RecordBatchReader> stripe_reader;
|
||||
auto status = reader->NextStripeReader(batch_size, &stripe_reader);
|
||||
|
||||
jobject ret = nullptr;
|
||||
if (!status.ok()) {
|
||||
delete stripe_reader;
|
||||
return ret;
|
||||
return static_cast<jlong>(status.code()) * -1;
|
||||
;
|
||||
}
|
||||
|
||||
jclass cls = env->FindClass("/org/apache/arrow/adapter/orc/OrcStripeReaderJniWrapper");
|
||||
ret = env->AllocObject(cls);
|
||||
|
||||
env->SetLongField(ret, GetFieldId(env, cls, "nativeStripeReaderAddress"),
|
||||
reinterpret_cast<long>(stripe_reader));
|
||||
|
||||
return ret;
|
||||
return orc_stripe_reader_holder_.Insert(stripe_reader);
|
||||
}
|
||||
|
||||
JNIEXPORT jbyteArray JNICALL
|
||||
Java_org_apache_arrow_adapter_orc_OrcStripeReaderJniWrapper_getSchema(JNIEnv* env,
|
||||
jobject this_obj) {
|
||||
auto stripe_reader = GetStripeReader(env, this_obj);
|
||||
auto schema = (*stripe_reader)->schema();
|
||||
jclass this_cls,
|
||||
jlong id) {
|
||||
auto stripe_reader = GetStripeReader(id);
|
||||
auto schema = stripe_reader->schema();
|
||||
|
||||
std::shared_ptr<arrow::Buffer> out;
|
||||
auto status = arrow::ipc::SerializeSchema(*schema, arrow::default_memory_pool(), &out);
|
||||
auto status =
|
||||
arrow::ipc::SerializeSchema(*schema, nullptr, arrow::default_memory_pool(), &out);
|
||||
if (!status.ok()) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -173,29 +231,28 @@ Java_org_apache_arrow_adapter_orc_OrcStripeReaderJniWrapper_getSchema(JNIEnv* en
|
||||
|
||||
JNIEXPORT jobject JNICALL
|
||||
Java_org_apache_arrow_adapter_orc_OrcStripeReaderJniWrapper_next(JNIEnv* env,
|
||||
jobject this_obj) {
|
||||
auto stripe_reader = GetStripeReader(env, this_obj);
|
||||
jclass this_cls,
|
||||
jlong id) {
|
||||
auto stripe_reader = GetStripeReader(id);
|
||||
auto record_batch = new std::shared_ptr<arrow::RecordBatch>();
|
||||
auto status = (*stripe_reader)->ReadNext(record_batch);
|
||||
auto status = stripe_reader->ReadNext(record_batch);
|
||||
if (!status.ok()) {
|
||||
delete record_batch;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto schema = (*stripe_reader)->schema();
|
||||
auto schema = stripe_reader->schema();
|
||||
|
||||
// create OrcFieldNode[]
|
||||
jclass field_class = env->FindClass("/org/apache/arrow/adapter/orc/OrcFieldNode");
|
||||
jmethodID field_constructor = env->GetMethodID(field_class, "<init>", "(II)V");
|
||||
jobjectArray field_array =
|
||||
env->NewObjectArray(schema->num_fields(), field_class, nullptr);
|
||||
env->NewObjectArray(schema->num_fields(), orc_field_node_class, nullptr);
|
||||
|
||||
std::vector<std::shared_ptr<arrow::Buffer>> buffers;
|
||||
for (int i = 0; i < schema->num_fields(); ++i) {
|
||||
auto column = (*record_batch)->column(i);
|
||||
auto dataArray = column->data();
|
||||
jobject field = env->NewObject(field_class, field_constructor, column->length(),
|
||||
column->null_count());
|
||||
jobject field = env->NewObject(orc_field_node_class, orc_field_node_constructor,
|
||||
column->length(), column->null_count());
|
||||
env->SetObjectArrayElement(field_array, i, field);
|
||||
|
||||
for (auto& buffer : dataArray->buffers) {
|
||||
@@ -204,38 +261,32 @@ Java_org_apache_arrow_adapter_orc_OrcStripeReaderJniWrapper_next(JNIEnv* env,
|
||||
}
|
||||
|
||||
// create OrcMemoryJniWrapper[]
|
||||
jclass memory_class =
|
||||
env->FindClass("/org/apache/arrow/adapter/orc/OrcMemoryJniWrapper");
|
||||
jmethodID memory_constructor = env->GetMethodID(memory_class, "<init>", "(JJJJ)V");
|
||||
jobjectArray memory_array = env->NewObjectArray(buffers.size(), memory_class, nullptr);
|
||||
jobjectArray memory_array =
|
||||
env->NewObjectArray(buffers.size(), orc_memory_class, nullptr);
|
||||
|
||||
for (int j = 0; j < buffers.size(); ++j) {
|
||||
auto buffer = buffers[j];
|
||||
jobject memory = env->NewObject(memory_class, memory_constructor, &buffer,
|
||||
buffer->data(), buffer->size(), buffer->capacity());
|
||||
jobject memory = env->NewObject(orc_memory_class, orc_memory_constructor,
|
||||
buffer_holder_.Insert(buffer), buffer->data(),
|
||||
buffer->size(), buffer->capacity());
|
||||
env->SetObjectArrayElement(memory_array, j, memory);
|
||||
}
|
||||
|
||||
// create OrcRecordBatch
|
||||
jclass ret_cls = env->FindClass("/org/apache/arrow/adapter/orc/OrcRecordBatch");
|
||||
jmethodID ret_constructor =
|
||||
env->GetMethodID(ret_cls, "<init>",
|
||||
"(I[L/org/apache/arrow/adapter/orc/OrcFieldNode;"
|
||||
"[L/org/apache/arrow/adapter/orc/OrcMemoryJniWrapper;)V");
|
||||
jobject ret = env->NewObject(ret_cls, ret_constructor, (*record_batch)->num_rows(),
|
||||
field_array, memory_array);
|
||||
jobject ret = env->NewObject(record_batch_class, record_batch_constructor,
|
||||
(*record_batch)->num_rows(), field_array, memory_array);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_apache_arrow_adapter_orc_OrcStripeReaderJniWrapper_close(
|
||||
JNIEnv* env, jobject this_obj) {
|
||||
delete GetStripeReader(env, this_obj);
|
||||
JNIEnv* env, jclass this_cls, jlong id) {
|
||||
buffer_holder_.Erase(id);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_apache_arrow_adapter_orc_OrcMemoryJniWrapper_release(
|
||||
JNIEnv* env, jobject this_obj, jlong address) {
|
||||
delete reinterpret_cast<std::shared_ptr<arrow::Buffer>*>(address);
|
||||
JNIEnv* env, jobject this_obj, jlong id) {
|
||||
buffer_holder_.Erase(id);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
+1
-1
@@ -51,7 +51,7 @@ be provided as the value for argument arrow.cpp.build.dir. eg.
|
||||
|
||||
```
|
||||
cd java
|
||||
mvn install -P native-orc -pl adapter/orc -am -Dgandiva.cpp.build.dir=../../debug
|
||||
mvn install -P native-orc -pl adapter/orc -am -Darrow.cpp.build.dir=../../debug
|
||||
```
|
||||
|
||||
## Java Code Style Guide
|
||||
|
||||
@@ -17,21 +17,29 @@
|
||||
|
||||
package org.apache.arrow.adapter.orc;
|
||||
|
||||
public class OrcFieldNode {
|
||||
/**
|
||||
* Metadata about Vectors/Arrays that is passed via JNI interface
|
||||
*/
|
||||
class OrcFieldNode {
|
||||
|
||||
private final int length;
|
||||
private final int nullCount;
|
||||
|
||||
/**
|
||||
* Construct a new instance.
|
||||
* @param length the number of values written.
|
||||
* @param nullCount the number of null values.
|
||||
*/
|
||||
public OrcFieldNode(int length, int nullCount) {
|
||||
this.length = length;
|
||||
this.nullCount = nullCount;
|
||||
}
|
||||
|
||||
public int getLength() {
|
||||
int getLength() {
|
||||
return length;
|
||||
}
|
||||
|
||||
public int getNullCount() {
|
||||
int getNullCount() {
|
||||
return nullCount;
|
||||
}
|
||||
}
|
||||
@@ -25,11 +25,18 @@ import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.UUID;
|
||||
|
||||
public class OrcJniUtils {
|
||||
/**
|
||||
* Helper class for JNI related operations.
|
||||
*/
|
||||
class OrcJniUtils {
|
||||
private static final String LIBRARY_NAME = "arrow_orc_jni";
|
||||
private static boolean isLoaded = false;
|
||||
|
||||
public static void loadOrcAdapterLibraryFromJar()
|
||||
/**
|
||||
* Load arrow orc jni library from jar.
|
||||
* @throws IOException throws IOException in case target library can not be found.
|
||||
*/
|
||||
static void loadOrcAdapterLibraryFromJar()
|
||||
throws IOException {
|
||||
synchronized (OrcJniUtils.class) {
|
||||
if (!isLoaded) {
|
||||
|
||||
+18
-7
@@ -19,8 +19,11 @@ package org.apache.arrow.adapter.orc;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class OrcMemoryJniWrapper {
|
||||
private final long ownershipAddress;
|
||||
/**
|
||||
* Wrapper for orc memory allocated by native code.
|
||||
*/
|
||||
class OrcMemoryJniWrapper implements AutoCloseable {
|
||||
private final long id;
|
||||
|
||||
private final long memoryAddress;
|
||||
|
||||
@@ -36,8 +39,15 @@ public class OrcMemoryJniWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
OrcMemoryJniWrapper(long ownershipAddress, long memoryAddress, long size, long capacity) {
|
||||
this.ownershipAddress = ownershipAddress;
|
||||
/**
|
||||
* Construct a new instance.
|
||||
* @param id unique id of the underlying memory.
|
||||
* @param memoryAddress starting memory address of the the underlying memory.
|
||||
* @param size size of the valid data.
|
||||
* @param capacity allocated memory size.
|
||||
*/
|
||||
OrcMemoryJniWrapper(long id, long memoryAddress, long size, long capacity) {
|
||||
this.id = id;
|
||||
this.memoryAddress = memoryAddress;
|
||||
this.size = size;
|
||||
this.capacity = capacity;
|
||||
@@ -55,9 +65,10 @@ public class OrcMemoryJniWrapper {
|
||||
return memoryAddress;
|
||||
}
|
||||
|
||||
void release() {
|
||||
release(ownershipAddress);
|
||||
@Override
|
||||
public void close() {
|
||||
release(id);
|
||||
}
|
||||
|
||||
private native void release(long address);
|
||||
private native void release(long id);
|
||||
}
|
||||
@@ -22,32 +22,64 @@ import java.io.IOException;
|
||||
import org.apache.arrow.memory.BufferAllocator;
|
||||
import org.apache.arrow.vector.ipc.ArrowReader;
|
||||
|
||||
|
||||
/**
|
||||
* Orc Reader that allow accessing orc stripes in Orc file.
|
||||
* This orc reader basically acts like an ArrowReader iterator that
|
||||
* iterate over orc stripes. Each stripe will be accessed via an
|
||||
* ArrowReader.
|
||||
*/
|
||||
public class OrcReader implements AutoCloseable {
|
||||
|
||||
private static final int DEFAULT_BATCH_SIZE = 1024;
|
||||
|
||||
private OrcReaderJniWrapper orcReader;
|
||||
|
||||
private BufferAllocator allocator;
|
||||
|
||||
private final long id;
|
||||
|
||||
/**
|
||||
* Create an OrcReader that iterate over orc stripes.
|
||||
* @param filePath file path to target file, currently only support local file.
|
||||
* @param allocator allocator provided to ArrowReader.
|
||||
* @throws IOException throws exception in case of file not found
|
||||
*/
|
||||
public OrcReader(String filePath, BufferAllocator allocator) throws IOException {
|
||||
this.allocator = allocator;
|
||||
orcReader = new OrcReaderJniWrapper();
|
||||
orcReader.open(filePath);
|
||||
id = OrcReaderJniWrapper.open(filePath);
|
||||
}
|
||||
|
||||
public ArrowReader nextStripeReader() {
|
||||
OrcStripeReaderJniWrapper stripeReader = orcReader.nextStripeReader(DEFAULT_BATCH_SIZE);
|
||||
if (stripeReader == null) {
|
||||
/**
|
||||
* Seek to designated row. Invoke NextStripeReader() after seek
|
||||
* will return stripe reader starting from designated row.
|
||||
* @param rowNumber the rows number to seek
|
||||
* @return true if seek operation is succeeded
|
||||
*/
|
||||
public boolean seek(int rowNumber) {
|
||||
return OrcReaderJniWrapper.seek(id, rowNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a stripe level ArrowReader with specified batchSize in each record batch.
|
||||
*
|
||||
* @param batchSize the number of rows loaded on each iteration
|
||||
* @return ArrowReader that iterate over current stripes
|
||||
*/
|
||||
public ArrowReader nextStripeReader(long batchSize) {
|
||||
long stripeReaderId = OrcReaderJniWrapper.nextStripeReader(id, batchSize);
|
||||
if (stripeReaderId < 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new OrcStripeReader(stripeReader, allocator);
|
||||
return new OrcStripeReader(stripeReaderId, allocator);
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of stripes in the file.
|
||||
*
|
||||
* @return number of stripes
|
||||
*/
|
||||
public int getNumberOfStripes() {
|
||||
return OrcReaderJniWrapper.getNumberOfStripes(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
orcReader.close();
|
||||
OrcReaderJniWrapper.close(id);
|
||||
}
|
||||
}
|
||||
+37
-9
@@ -19,10 +19,10 @@ package org.apache.arrow.adapter.orc;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class OrcReaderJniWrapper {
|
||||
|
||||
private long nativeReaderAddress;
|
||||
|
||||
/**
|
||||
* JNI wrapper for Orc reader
|
||||
*/
|
||||
class OrcReaderJniWrapper {
|
||||
static {
|
||||
try {
|
||||
OrcJniUtils.loadOrcAdapterLibraryFromJar();
|
||||
@@ -31,13 +31,41 @@ public class OrcReaderJniWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
public native boolean open(String fileName);
|
||||
/**
|
||||
* Construct a orc file reader over the target file
|
||||
* @param fileName absolute file path of target file
|
||||
* @return id of the orc reader instance if file opened successfully,
|
||||
* otherwise return error code * -1.
|
||||
*/
|
||||
static native long open(String fileName);
|
||||
|
||||
public native void close();
|
||||
/**
|
||||
* Close the underlying reader and release related resources.
|
||||
* @param readerId id of the reader instance.
|
||||
*/
|
||||
static native void close(long readerId);
|
||||
|
||||
public native boolean seek(int rowNumber);
|
||||
/**
|
||||
* Seek to designated row. Invoke nextStripeReader() after seek
|
||||
* will return id of stripe reader starting from designated row.
|
||||
* @param readerId id of the reader instance
|
||||
* @param rowNumber the rows number to seek
|
||||
* @return true if seek operation is succeeded
|
||||
*/
|
||||
static native boolean seek(long readerId, int rowNumber);
|
||||
|
||||
public native int getNumberOfStripes();
|
||||
/**
|
||||
* The number of stripes in the file.
|
||||
* @param readerId id of the reader instance
|
||||
* @return number of stripes
|
||||
*/
|
||||
static native int getNumberOfStripes(long readerId);
|
||||
|
||||
public native OrcStripeReaderJniWrapper nextStripeReader(long batchSize);
|
||||
/**
|
||||
* Get a stripe level ArrowReader with specified batchSize in each record batch.
|
||||
* @param readerId id of the reader instance
|
||||
* @param batchSize the number of rows loaded on each iteration
|
||||
* @return id of the stripe reader instance.
|
||||
*/
|
||||
static native long nextStripeReader(long readerId, long batchSize);
|
||||
}
|
||||
@@ -20,16 +20,22 @@ package org.apache.arrow.adapter.orc;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class OrcRecordBatch {
|
||||
public final int length;
|
||||
class OrcRecordBatch {
|
||||
final int length;
|
||||
|
||||
/**
|
||||
* Nodes correspond to the pre-ordered flattened logical schema.
|
||||
*/
|
||||
public final List<OrcFieldNode> nodes;
|
||||
final List<OrcFieldNode> nodes;
|
||||
|
||||
public final List<OrcMemoryJniWrapper> buffers;
|
||||
final List<OrcMemoryJniWrapper> buffers;
|
||||
|
||||
/**
|
||||
* Construct a new instance
|
||||
* @param length number of records included in current batch
|
||||
* @param nodes meta data for each fields
|
||||
* @param buffers buffers for underlying data
|
||||
*/
|
||||
OrcRecordBatch(int length, OrcFieldNode[] nodes, OrcMemoryJniWrapper[] buffers) {
|
||||
this.length = length;
|
||||
this.nodes = Arrays.asList(nodes);
|
||||
|
||||
+7
-83
@@ -17,6 +17,8 @@
|
||||
|
||||
package org.apache.arrow.adapter.orc;
|
||||
|
||||
import io.netty.buffer.ArrowBuf;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.apache.arrow.memory.BufferAllocator;
|
||||
@@ -24,8 +26,10 @@ import org.apache.arrow.memory.OwnershipTransferResult;
|
||||
import org.apache.arrow.memory.ReferenceManager;
|
||||
import org.apache.arrow.util.Preconditions;
|
||||
|
||||
import io.netty.buffer.ArrowBuf;
|
||||
|
||||
/**
|
||||
* A simple reference manager implementation for memory allocated by native code.
|
||||
* The underlying memory will be released when reference count reach zero.
|
||||
*/
|
||||
public class OrcReferenceManager implements ReferenceManager {
|
||||
private final AtomicInteger bufRefCnt = new AtomicInteger(0);
|
||||
|
||||
@@ -35,36 +39,16 @@ public class OrcReferenceManager implements ReferenceManager {
|
||||
this.memory = memory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the reference count.
|
||||
*
|
||||
* @return reference count
|
||||
*/
|
||||
@Override
|
||||
public int getRefCount() {
|
||||
return bufRefCnt.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrement this reference manager's reference count by 1 for the associated underlying
|
||||
* memory. If the reference count drops to 0, it implies that ArrowBufs managed by this
|
||||
* reference manager no longer need access to the underlying memory
|
||||
*
|
||||
* @return true if ref count has dropped to 0, false otherwise
|
||||
*/
|
||||
@Override
|
||||
public boolean release() {
|
||||
return release(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrement this reference manager's reference count for the associated underlying
|
||||
* memory. If the reference count drops to 0, it implies that ArrowBufs managed by this
|
||||
* reference manager no longer need access to the underlying memory
|
||||
*
|
||||
* @param decrement the count to decrease the reference count by
|
||||
* @return the new reference count
|
||||
*/
|
||||
@Override
|
||||
public boolean release(int decrement) {
|
||||
Preconditions.checkState(decrement >= 1,
|
||||
@@ -76,7 +60,7 @@ public class OrcReferenceManager implements ReferenceManager {
|
||||
if (refCnt == 0) {
|
||||
// refcount of this reference manager has dropped to 0
|
||||
// release the underlying memory
|
||||
memory.release();
|
||||
memory.close();
|
||||
}
|
||||
}
|
||||
// the new ref count should be >= 0
|
||||
@@ -84,21 +68,11 @@ public class OrcReferenceManager implements ReferenceManager {
|
||||
return refCnt == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment this reference manager's reference count by 1 for the associated underlying
|
||||
* memory.
|
||||
*/
|
||||
@Override
|
||||
public void retain() {
|
||||
retain(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment this reference manager's reference count by a given amount for the
|
||||
* associated underlying memory.
|
||||
*
|
||||
* @param increment the count to increase the reference count by
|
||||
*/
|
||||
@Override
|
||||
public void retain(int increment) {
|
||||
Preconditions.checkArgument(increment > 0, "retain(%d) argument is not positive", increment);
|
||||
@@ -106,38 +80,12 @@ public class OrcReferenceManager implements ReferenceManager {
|
||||
Preconditions.checkArgument(originalReferenceCount > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ArrowBuf that is associated with an alternative allocator for the purposes of
|
||||
* memory ownership and accounting. This has no impact on the reference counting for the current
|
||||
* ArrowBuf except in the situation where the passed in Allocator is the same as the current buffer.
|
||||
* This operation has no impact on the reference count of this ArrowBuf. The newly created
|
||||
* ArrowBuf with either have a reference count of 1 (in the case that this is the first time this
|
||||
* memory is being associated with the target allocator or in other words allocation manager currently
|
||||
* doesn't hold a mapping for the target allocator) or the current value of the reference count for
|
||||
* the target allocator-reference manager combination + 1 in the case that the provided allocator
|
||||
* already had an association to this underlying memory.
|
||||
*
|
||||
* @param srcBuffer source ArrowBuf
|
||||
* @param targetAllocator The target allocator to create an association with.
|
||||
* @return A new ArrowBuf which shares the same underlying memory as this ArrowBuf.
|
||||
*/
|
||||
@Override
|
||||
public ArrowBuf retain(ArrowBuf srcBuffer, BufferAllocator targetAllocator) {
|
||||
retain();
|
||||
return srcBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Derive a new ArrowBuf from a given source ArrowBuf. The new derived
|
||||
* ArrowBuf will share the same reference count as rest of the ArrowBufs
|
||||
* associated with this reference manager.
|
||||
*
|
||||
* @param sourceBuffer source ArrowBuf
|
||||
* @param index index (relative to source ArrowBuf) new ArrowBuf should be derived from
|
||||
* @param length length (bytes) of data in underlying memory that derived buffer will
|
||||
* have access to in underlying memory
|
||||
* @return derived buffer
|
||||
*/
|
||||
@Override
|
||||
public ArrowBuf deriveBuffer(ArrowBuf sourceBuffer, int index, int length) {
|
||||
final long derivedBufferAddress = sourceBuffer.memoryAddress() + index;
|
||||
@@ -153,15 +101,6 @@ public class OrcReferenceManager implements ReferenceManager {
|
||||
return derivedBuf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transfer the memory accounting ownership of this ArrowBuf to another allocator.
|
||||
* This will generate a new ArrowBuf that carries an association with the underlying memory
|
||||
* for the given ArrowBuf
|
||||
*
|
||||
* @param sourceBuffer source ArrowBuf
|
||||
* @param targetAllocator The target allocator to create an association with
|
||||
* @return {@link OwnershipTransferResult} with info on transfer result and new buffer
|
||||
*/
|
||||
@Override
|
||||
public OwnershipTransferResult transferOwnership(ArrowBuf sourceBuffer, BufferAllocator targetAllocator) {
|
||||
retain();
|
||||
@@ -178,31 +117,16 @@ public class OrcReferenceManager implements ReferenceManager {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the buffer allocator associated with this reference manager.
|
||||
*
|
||||
* @return buffer allocator.
|
||||
*/
|
||||
@Override
|
||||
public BufferAllocator getAllocator() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Total size (in bytes) of memory underlying this reference manager.
|
||||
*
|
||||
* @return Size (in bytes) of the memory chunk.
|
||||
*/
|
||||
@Override
|
||||
public int getSize() {
|
||||
return (int)memory.getSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the total accounted size (in bytes).
|
||||
*
|
||||
* @return accounted size.
|
||||
*/
|
||||
@Override
|
||||
public int getAccountedSize() {
|
||||
return 0;
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
|
||||
package org.apache.arrow.adapter.orc;
|
||||
|
||||
import io.netty.buffer.ArrowBuf;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -33,28 +35,28 @@ import org.apache.arrow.vector.ipc.message.MessageResult;
|
||||
import org.apache.arrow.vector.ipc.message.MessageSerializer;
|
||||
import org.apache.arrow.vector.types.pojo.Schema;
|
||||
import org.apache.arrow.vector.util.ByteArrayReadableSeekableByteChannel;
|
||||
import sun.reflect.generics.reflectiveObjects.NotImplementedException;
|
||||
|
||||
import io.netty.buffer.ArrowBuf;
|
||||
|
||||
|
||||
/**
|
||||
* Orc stripe that load data into ArrowRecordBatch.
|
||||
*/
|
||||
public class OrcStripeReader extends ArrowReader {
|
||||
private OrcStripeReaderJniWrapper stripeReader;
|
||||
private final long id;
|
||||
private MessageChannelReader schemaReader;
|
||||
|
||||
protected OrcStripeReader(OrcStripeReaderJniWrapper stripeReader, BufferAllocator allocator) {
|
||||
/**
|
||||
* Construct a new instance.
|
||||
* @param id id of the stripe reader instance
|
||||
* @param allocator memory allocator for accounting.
|
||||
*/
|
||||
OrcStripeReader(long id, BufferAllocator allocator) {
|
||||
super(allocator);
|
||||
this.stripeReader = stripeReader;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the next ArrowRecordBatch to the vector schema root if available.
|
||||
*
|
||||
* @return true if a batch was read, false on EOS
|
||||
* @throws IOException on error
|
||||
*/
|
||||
@Override
|
||||
public boolean loadNextBatch() throws IOException {
|
||||
OrcRecordBatch recordBatch = stripeReader.next();
|
||||
OrcRecordBatch recordBatch = OrcStripeReaderJniWrapper.next(id);
|
||||
if (recordBatch == null) {
|
||||
return false;
|
||||
}
|
||||
@@ -78,36 +80,21 @@ public class OrcStripeReader extends ArrowReader {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of bytes read from the ReadChannel.
|
||||
*
|
||||
* @return number of bytes read
|
||||
*/
|
||||
@Override
|
||||
public long bytesRead() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the underlying read source.
|
||||
*
|
||||
* @throws IOException on error
|
||||
*/
|
||||
|
||||
@Override
|
||||
protected void closeReadSource() throws IOException {
|
||||
stripeReader.close();
|
||||
OrcStripeReaderJniWrapper.close(id);
|
||||
schemaReader.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the Schema from the source, will be invoked at the beginning the initialization.
|
||||
*
|
||||
* @return the read Schema
|
||||
* @throws IOException on error
|
||||
*/
|
||||
@Override
|
||||
protected Schema readSchema() throws IOException {
|
||||
byte[] schemaBytes = stripeReader.getSchema();
|
||||
byte[] schemaBytes = OrcStripeReaderJniWrapper.getSchema(id);
|
||||
schemaReader = new MessageChannelReader(
|
||||
new ReadChannel(
|
||||
new ByteArrayReadableSeekableByteChannel(schemaBytes)), allocator);
|
||||
@@ -125,33 +112,8 @@ public class OrcStripeReader extends ArrowReader {
|
||||
return MessageSerializer.deserializeSchema(result.getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a dictionary batch from the source, will be invoked after the schema has been read and
|
||||
* called N times, where N is the number of dictionaries indicated by the schema Fields.
|
||||
*
|
||||
* @return the read ArrowDictionaryBatch
|
||||
* @throws IOException on error
|
||||
*/
|
||||
@Override
|
||||
protected ArrowDictionaryBatch readDictionary() throws IOException {
|
||||
MessageResult result = schemaReader.readNext();
|
||||
|
||||
if (result == null) {
|
||||
throw new IOException("Unexpected end of input. Expected DictionaryBatch");
|
||||
}
|
||||
|
||||
if (result.getMessage().headerType() != MessageHeader.DictionaryBatch) {
|
||||
throw new IOException("Expected DictionaryBatch but header was " +
|
||||
result.getMessage().headerType());
|
||||
}
|
||||
|
||||
ArrowBuf bodyBuffer = result.getBodyBuffer();
|
||||
|
||||
// For zero-length batches, need an empty buffer to deserialize the batch
|
||||
if (bodyBuffer == null) {
|
||||
bodyBuffer = allocator.getEmpty();
|
||||
}
|
||||
|
||||
return MessageSerializer.deserializeDictionaryBatch(result.getMessage(), bodyBuffer);
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
+22
-6
@@ -19,7 +19,10 @@ package org.apache.arrow.adapter.orc;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class OrcStripeReaderJniWrapper {
|
||||
/**
|
||||
* JNI wrapper for orc stripe reader.
|
||||
*/
|
||||
class OrcStripeReaderJniWrapper {
|
||||
|
||||
static {
|
||||
try {
|
||||
@@ -29,11 +32,24 @@ public class OrcStripeReaderJniWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
private long nativeStripeReaderAddress;
|
||||
/**
|
||||
* Get the schema of current stripe.
|
||||
* @param readerId id of the stripe reader instance.
|
||||
* @return serialized schema.
|
||||
*/
|
||||
static native byte[] getSchema(long readerId);
|
||||
|
||||
public native byte[] getSchema();
|
||||
/**
|
||||
* Load next record batch.
|
||||
* @param readerId id of the stripe reader instance.
|
||||
* @return loaded record batch, return null when reached
|
||||
* the end of current stripe.
|
||||
*/
|
||||
static native OrcRecordBatch next(long readerId);
|
||||
|
||||
public native OrcRecordBatch next();
|
||||
|
||||
public native void close();
|
||||
/**
|
||||
* Release resources of underlying reader.
|
||||
* @param readerId id of the stripe reader instance.
|
||||
*/
|
||||
static native void close(long readerId);
|
||||
}
|
||||
@@ -70,7 +70,7 @@ public class AllocationManager {
|
||||
private volatile BufferLedger owningLedger;
|
||||
private volatile long amDestructionTime = 0;
|
||||
|
||||
public AllocationManager(BaseAllocator accountingAllocator, int size) {
|
||||
AllocationManager(BaseAllocator accountingAllocator, int size) {
|
||||
Preconditions.checkNotNull(accountingAllocator);
|
||||
accountingAllocator.assertOpen();
|
||||
|
||||
@@ -174,7 +174,7 @@ public class AllocationManager {
|
||||
// underlying memory chunk as it is no longer being referenced
|
||||
((BaseAllocator)oldLedger.getAllocator()).releaseBytes(size);
|
||||
// free the memory chunk associated with the allocation manager
|
||||
releaseMemory();
|
||||
memoryChunk.release();
|
||||
((BaseAllocator)oldLedger.getAllocator()).getListener().onRelease(size);
|
||||
amDestructionTime = System.nanoTime();
|
||||
owningLedger = null;
|
||||
@@ -196,10 +196,6 @@ public class AllocationManager {
|
||||
}
|
||||
}
|
||||
|
||||
public void releaseMemory() {
|
||||
memoryChunk.release();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the size of underlying chunk of memory managed by this Allocation Manager.
|
||||
* @return size of memory chunk
|
||||
|
||||
Reference in new issue
Block a user