Какой контейнер лучше всего подходит для хранения данных {произвольного задания} на C++?

Я пишу приложение, которое должно использовать поля jobject несколько раз с большой скоростью. Поскольку получение полей из jobjects происходит довольно медленно, я хочу сделать это один раз, а затем читать и записывать поля какого-нибудь объекта C++ или элементы многотипного массива (если таковой может быть создан) или что-то еще, что обеспечивает быстрый доступ к данным (далее - целевой контейнер ).

Мой код имеет две функции: первая записывает поля jobject в целевой контейнер, вторая считывает из него записанные данные.

Целевой контейнер должен быть способен хранить переменные разных типов, и его размер не должен определяться во время компиляции (поэтому struct не подходит). Вопрос в следующем: какой контейнер лучше всего удовлетворяет этим условиям, если его качество определяется скоростью чтения (+преобразования типов в правильные) из него?

Прямо сейчас я использую vector<any>, как показано ниже:

#include <vector>
#include <any>
JNIEXPORT void JNICALL Java_runTask(JNIEnv*env,jclass,jobject o){
    vector<any>d;
    readData(d,/*some other arguments*/,o);
    do{
        //do something useful
    }while(predicate);
}

void readData(vector<any>*data/*some other arguments*/,jobject o){
    //fill the vector with jobject's fields' values
}

bool predicate(vector<any>*data){
    //determine the return value using vector contents
    return true;
}

vector<any> соответствует моим условиям, НО...

  1. Я не уверен, что вектор — самый быстрый контейнер для чтения.
  2. any_cast<type>() ПРОВЕРЯЕТ тип перед регистром, что означает, что он имеет несовершенную производительность.

@Eljay, если честно, я не замерял производительность, просто где-то прочитал, что "общаться" между C++ и Java (в обоих направлениях) обходится дороже. Я собираюсь провести измерение прямо сейчас. Я обновлю вопрос и сообщу вам, как только буду готов.

Tech of the Absence 07.04.2024 15:44

@Eljay, чтение целого числа из контейнера std::vector<std::any> происходит примерно в 20 раз быстрее, чем получение целого числа через JNI. Это важно.

Tech of the Absence 07.04.2024 16:17

Под «Я не знаю его размера» вы имеете в виду, что не знаете, каков тип значения хеш-карты? Или вы не знаете количество элементов в хэш-карте?

Wutz 07.04.2024 16:38

@Wutz, я не знаю обоих. Я имею в виду, что я не знаю, что будет передано в функцию во время выполнения, поэтому я не могу подготовить контейнер только для одного типа или контейнер с размером, предопределенным во время компиляции.

Tech of the Absence 07.04.2024 16:47

@Wutz Я не знаю ни того, ни другого. Вы можете получить размер, используя FindClass("Ljava/util/HashMap;"), чтобы получить класс Java, а затем GetMethodID, чтобы получить идентификатор метода функции size. Затем вызовите CallIntMethod для идентификатора класса и метода.

PaulMcKenzie 07.04.2024 17:31

@PaulMcKenzie, я знаю, что могу легко получить его размер, но некоторые подходы могут включать использование, например, структур с ограниченным количеством полей std::any, которые не подходят, поскольку количество полей определяется во время компиляции, и я хочу избегайте подобных рекомендаций.

Tech of the Absence 07.04.2024 17:39

Я думаю, что любой вектор кажется лучшим решением, учитывая ваши текущие ограничения. Но мне интересно: если вы вообще не знаете тип значения, как вы вообще извлекаете информацию для хранения в any? Если это может быть только ограниченное количество различных типов значений, вместо этого вы можете использовать std::variant<...>.

Wutz 07.04.2024 21:09

Это своеобразный шаблон доступа. Как часто сторона Java читает и записывает свои данные? Как часто родная сторона читает/пишет?

Botje 08.04.2024 08:28

@Botje, все делается в коде C++, но он сначала записывает несколько env->CallObjectMethod «расшифрованных» (удобных для использования) результатов в какое-то хранилище (vector<any>, в моем случае), а затем считывает все необходимое из этого хранилища.

Tech of the Absence 08.04.2024 15:27

Если все делается на собственной стороне, почему данные вообще хранятся на стороне Java? Рассмотрите возможность сериализации состояния Java в байтовый буфер для чтения C++ с использованием, например, плоских буферов или буферов протокола.

Botje 08.04.2024 15:35

@Botje, я неправильно это объяснил. Под «всем» я подразумеваю все, начиная с первого вызова метода JNI. Точнее, программный цикл делится на два этапа: взаимодействие с пользователем через Java (и запись полей) и выполнение автоматического задания через C++. Поскольку первый этап пройден, все делается на нативной стороне, поэтому мы можем считать все задания постоянными и считанные из них данные всегда актуальны.

Tech of the Absence 08.04.2024 18:53

JAVA не очень любит C++. JNI — это минимальная совместимость с C. Любое правильное решение на C++ требует оболочки C API. Официального API C++ не существует, поэтому ответственность лежит на программисте. Вы можете просмотреть различные проекты с открытым исходным кодом и выбрать подходящий. В противном случае вы можете отметить сохранение родной части.

Red.Wave 08.04.2024 19:23

Тогда я повторяю: передайте плоский буфер нативной стороне.

Botje 08.04.2024 19:25

@Ботье, почему? Мне удалось прочитать данные из заданий, мне нужно найти наиболее подходящее хранилище для многотипных данных.

Tech of the Absence 09.04.2024 13:10

@Red.Wave, Ботье, ты неправильно истолковал мой вопрос. Цель — найти многотипное хранилище с максимальной скоростью чтения.

Tech of the Absence 09.04.2024 13:13
HashMap<String,Object> — красивый тип сообщения для отправки через языковую границу. Можете ли вы сделать лучше, например? HashMap<String, SomeDTO>? И если вы можете это сделать, могли бы вы вместо этого сериализовать эти SomeDTO и вообще не использовать JNI?
Caleth 09.04.2024 13:19

@Калет, пожалуйста, прочитай обновленный вопрос (в конце). Обратите внимание: у меня нет проблем с использованием jobject, мне просто нужен объект/массив, куда я могу поместить все извлеченное из него. У меня даже есть такой, но должен быть вариант получше.

Tech of the Absence 09.04.2024 13:23

Можете ли вы хотя бы рассказать нам, какие типы вы извлекаете из Java? Возможно, все это укладывается в общую структуру данных, подобную той, что используется библиотеками JSON. И тогда вы могли бы вместо этого передать JSON-объект через границу JNI.

Botje 09.04.2024 14:03

@Botje, этот объект не будет передаваться с одного языка на другой, я буду использовать контейнер только в C++. Что касается типов, то, к сожалению, я не могу предсказать, какие типы буду использовать в будущем, поэтому подойдет любой тип.

Tech of the Absence 09.04.2024 14:21

«Лучшая скорость чтения» — не использовать JNI.

Caleth 09.04.2024 14:21

«Я не могу предсказать, какие типы я буду использовать в будущем». Почему это должно подходить каждому типу? будет быстрее, если он специализируется на одной задаче

Caleth 09.04.2024 14:26

Я сдаюсь. Вы можете иметь максимальную гибкость или максимальную скорость, но не то и другое. Прими решение.

Botje 09.04.2024 14:28

@Калет, позволь мне еще раз прояснить ситуацию: у меня есть jobject, информация о котором считывается один раз (это место не требует скорости). Эта информация должна храниться в каком-то контейнере (далее – целевой контейнер). Данные из целевого контейнера будут считываться несколько раз, и этот процесс должен быть максимально быстрым. Цель (и помощь, о которой я спрашиваю) состоит в том, чтобы определить, какой контейнер использовать в качестве целевого контейнера. Обратите внимание: при чтении из него не будут использоваться JNI, Java и все, что связано с языками высокого уровня.

Tech of the Absence 09.04.2024 14:30

Самым быстрым будет SomeSpecificType, заточенный ровно под одну задачу. Может быть, сделать шаблоны readData и predicate? Возможно, если бы все поля были одного типа, вы могли бы иметь std::vector<SomeOtherType>

Caleth 09.04.2024 14:32

@Калет, я думаю, декодирование всего из одного типа потребует больше времени, чем преобразование any в необходимый тип. Шаблоны исключают возможность многотипного хранения. Один из выходов, который я вижу, — это использовать что-то вроде any, но без проверки безопасности типов при приведении типов (если такая вещь вообще существует).

Tech of the Absence 09.04.2024 14:39

Можете ли вы определить конкретный интерфейс того, что вы хотите делать с этими произвольными данными? class BaseTask { virtual foo frobnicate() = 0; /* etc */ }; и template<typename T> class Task<T> { foo frobnicate() override { /* do T specific stuff */ } };

Caleth 09.04.2024 14:49

Предположим, я передаю вам вектор any, не сообщая, какие реальные типы any могут храниться. Что ты можешь сделать с этим?

n. m. could be an AI 09.04.2024 15:56

Как говорилось ранее, основная проблема — плохой API JNI. Он содержит много повторяющегося строкового кода, написанного очень примитивным образом. Одной из основных задач будет преодоление недействительности ссылок, происходящих при вызовах модуля C++. Для этого вам действительно нужно использовать стороннюю оболочку C++; необходимо изучить множество основ C++, прежде чем вы сможете справиться с этим самостоятельно.

Red.Wave 09.04.2024 17:21

@n.m.couldbeanAI, у меня есть две функции, обе состоят из выражения switch, которое получает число на основе данных, которые должны храниться в целевом контейнере. Они «знают», что делают. Все, что мне нужно, это просто передать этот контейнер из одной функции в другую. Вторая функция будет выполняться много раз, поэтому мне нужен многотипный контейнер.

Tech of the Absence 09.04.2024 17:57

@Калет, пожалуйста, прочитай мой комментарий выше. Я не понимаю, зачем мне такой интерфейс.

Tech of the Absence 09.04.2024 17:59

Если кто-то знает, как улучшить структуру моего вопроса, чтобы было понятно тому, кто придет после нас, подскажите, пожалуйста. Как я вижу, сейчас это совершенно непонятно.

Tech of the Absence 09.04.2024 18:01

Нет, мы понимаем ваш вопрос. Мы просто думаем, что у вас проблема XY

Caleth 09.04.2024 18:02

Не уверен, что именно вы пытаетесь сделать, но первая реакция заключается в том, что каждая ветвь вашего переключателя должна быть отдельной функцией, и контейнер должен хранить эту функцию вместе с данными для работы, то есть привязкой std::function.

n. m. could be an AI 09.04.2024 18:03

Любое решение, которое позволяет хранить произвольные типы в контейнере, а код, взаимодействующий с этим контейнером, волшебным образом зная, к чему приводить, будет медленнее и содержит больше ошибок, чем код, в котором с каждым фрагментом данных связан статический тип. Интерфейс, который я написал выше, предлагал вам поднять полиморфизм с data, чтобы также включить операции, которые вы выполняете с этими данными.

Caleth 09.04.2024 18:06

@Caleth, я новичок в C++, и с этого момента я ничего не понимаю :( Я изо всех сил старался переописать проблему в вопросе, чтобы сосредоточиться на очень конкретной задаче. Буду рад, если вы попробуйте объяснить подход с полиморфизмом еще раз (но это не обязательно).

Tech of the Absence 09.04.2024 18:36
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
35
128
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вместо того, чтобы пытаться использовать контейнер, который может хранить произвольные данные, мы можем обернуть прочитанные и используемые данные в полиморфный тип.

В качестве эскиза вам придется переработать Java_runTask, чтобы не иметь дело с данными напрямую.

struct BaseTask
{
    virtual bool predicate() = 0;
    virtual Result process() = 0; // whatever "do something useful" does
};

std::unique_ptr<BaseTask> readData(JNIEnv* env, jclass c, jobject o)
{
    // based on c, construct a subclass of BaseTask, and read the data from o into it
}

JNIEXPORT void JNICALL Java_runTask(JNIEnv*env,jclass,jobject o){
    auto task = readData(/*some other arguments*/,o);
    do{
        auto res = task->process();
        // do more processing
    } while(task->predicate());
}

Затем вам потребуются подклассы BaseTask, специфичные для разных типов данных. Один из способов сделать это — создать подкласс шаблона.

template <typename T>
struct Task : public BaseTask
{
    T data;
    bool predicate() override;
    Result process() override; 
};

template <>
bool Task<Foo>::predicate()
{
    // data is a Foo here
}

template <>
bool Task<Bar>::predicate()
{
    // data is a Bar here
}

По сути, это то же самое, что определение

class FooTask : public BaseTask { /* all the members */ };
class BarTask : public BaseTask { /* all the members */ };

О Конечно! Предикат может быть построен динамически! Это похоже на структуру, которая у меня есть в Java (где я создаю новый Consumer<> для каждой команды) (которая оказалась слишком медленной из-за безопасности языка). Большое спасибо.

Tech of the Absence 10.04.2024 13:21

Другие вопросы по теме