Skip to content

Commit

Permalink
Merge pull request #635 from Kicer86/refresh_performance
Browse files Browse the repository at this point in the history
Improve collection refresh performance
  • Loading branch information
mergify[bot] committed Jun 17, 2024
2 parents a59e35a + d9cc6a1 commit 67c74cf
Show file tree
Hide file tree
Showing 49 changed files with 1,180 additions and 255 deletions.
2 changes: 1 addition & 1 deletion .github/expected_file_list
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ opencv_videoio4.dll
opengl32sw.dll
photo_broom.exe
photos_crawler.dll
plugins\database\database_sqlite_backend.dll
plugins\database\database_sqlite_plugin.dll
project_utils.dll
psapi.dll
Qt6Core.dll
Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,6 @@
[submodule "src/core/learning_tests/test-videos"]
path = src/core/learning_tests/test-videos
url = https://github.com/Kicer86/VOF-media.git
[submodule "src/third_party/nanobench"]
path = src/third_party/nanobench
url = https://github.com/martinus/nanobench.git
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ if(POLICY CMP0146)
endif()

if(POLICY CMP0160)
cmake_policy(SET CMP0160 OLD)
cmake_policy(SET CMP0160 NEW)
endif()

set(CMAKE_MODULE_PATH
Expand Down
24 changes: 24 additions & 0 deletions src/core/generic_concepts.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
#ifndef GENERIC_CONCEPTS
#define GENERIC_CONCEPTS

#include <map>
#include <unordered_map>


template<typename T>
concept SmartPointer = requires(T p)
{
Expand Down Expand Up @@ -40,4 +44,24 @@ concept Container = requires(T p)
{ p.end() };
};


// https://stackoverflow.com/a/64088175/1749713
template<typename T>
concept map_type =
std::same_as<T, std::map<typename T::key_type, typename T::mapped_type, typename T::key_compare, typename T::allocator_type>> ||
std::same_as<T, std::unordered_map<typename T::key_type, typename T::mapped_type, typename T::hasher, typename T::key_equal, typename T::allocator_type>>;


template<typename>
struct is_std_vector: std::false_type {};

template<typename T, typename A>
struct is_std_vector<std::vector<T,A>>: std::true_type {};

template<typename T> inline constexpr bool is_std_vector_v = is_std_vector<T>::value;


template<typename T> struct always_false: std::false_type {};
template<typename T> inline constexpr bool always_false_v = always_false<T>::value;

#endif
11 changes: 11 additions & 0 deletions src/core/id.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class Id
{
public:
typedef T type;
typedef Tag tag;

Id()
{
Expand Down Expand Up @@ -121,4 +122,14 @@ class Id
bool m_valid = false;
};


template<typename T, typename Tag>
struct std::hash<Id<T, Tag>>
{
std::size_t operator()(const Id<T, Tag>& k) const
{
return k.value();
}
};

#endif
17 changes: 4 additions & 13 deletions src/core/learning_tests/image_aligner_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <ranges>
#include <QDir>

#include <core/generic_concepts.hpp>
#include <unit_tests_utils/empty_logger.hpp>
#include <unit_tests_utils/printers.hpp>
#include "containers_utils.hpp"
Expand All @@ -13,16 +14,6 @@ using testing::ElementsAre;

namespace
{
template<typename>
struct is_std_vector : std::false_type {};

template<typename T, typename A>
struct is_std_vector<std::vector<T,A>> : std::true_type {};


template<typename T> struct always_false : std::false_type {};


template<typename T>
bool areNotSimilar(const T& lhs, const T& rhs)
{
Expand All @@ -33,14 +24,14 @@ namespace
template<typename T>
bool isSimilarToImpl(const T& arg, const T& v)
{
if constexpr (is_std_vector<T>::value)
if constexpr (is_std_vector_v<T>)
{
const auto s = arg.size();
assert(v.size() == s);

using vector_value_type = T::value_type;

if constexpr (is_std_vector<vector_value_type>::value) // vector in vector
if constexpr (is_std_vector_v<vector_value_type>) // vector in vector
{
for(std::size_t i = 0; i < s; i++)
if (isSimilarToImpl(arg[i], v[i]) == false)
Expand All @@ -65,7 +56,7 @@ namespace
return false;
}
else
static_assert(always_false<T>::value, "Argument type is not supported");
static_assert(always_false_v<T>, "Argument type is not supported");

return true;
}
Expand Down
36 changes: 35 additions & 1 deletion src/core/utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
#ifndef UTILS_HPP_INCLUDED
#define UTILS_HPP_INCLUDED

#include <magic_enum.hpp>
#include <cmath>


template<typename T, typename R, const R T::*member>
R extract(const T& type)
Expand All @@ -22,4 +25,35 @@ requires (std::numeric_limits<T>::is_integer == false)
|| std::fabs(x-y) < std::numeric_limits<T>::min();
}

#endif // UTILS_HPP_INCLUDED

namespace details
{
template<auto... args, typename F>
void unfold(F&& op)
{
(op(std::integral_constant<decltype(args), args>{}), ...);
}

template<auto arr, typename IS = decltype(std::make_index_sequence<arr.size()>())> struct Generator;

template<auto arr, std::size_t... I>
struct Generator<arr, std::index_sequence<I...>>
{
void operator()(auto op)
{
unfold<arr[I]...>(op);
}
};
}

/**
* @brief call op for each value of enum E
*/
template<typename E>
void for_each(auto op)
{
constexpr auto enum_values = magic_enum::enum_values<E>();
details::Generator<enum_values>{}(op);
}

#endif
8 changes: 6 additions & 2 deletions src/database/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ if(BUILD_LEARNING_TESTS)
add_subdirectory(learning_tests)
endif()

add_subdirectory(benchmarks)

find_package(Qt6 REQUIRED COMPONENTS Core Gui)
find_package(Threads REQUIRED)
find_package(MagicEnum REQUIRED)
Expand Down Expand Up @@ -72,8 +74,9 @@ add_library(database SHARED ${DATABASE_SOURCES})
target_link_libraries(database
PUBLIC
Qt::Gui
core # core is being used in some public interfaces (see photo_data.hpp)

PRIVATE
core
Qt::Core
opencv_img_hash # do not link all possible libs, just those we will need (opencv_cvv uses Qt5 which causes problems)
${CMAKE_THREAD_LIBS_INIT}
Expand All @@ -83,8 +86,9 @@ target_include_directories(database
PUBLIC
${MAGIC_ENUM_INCLUDE_DIR}
${CMAKE_CURRENT_BINARY_DIR}
${PROJECT_SOURCE_DIR}/src

PRIVATE
${CMAKE_SOURCE_DIR}/src
${CMAKE_CURRENT_SOURCE_DIR}
${OpenCV_INCLUDE_DIRS}
)
Expand Down
3 changes: 2 additions & 1 deletion src/database/backends/memory_backend/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ target_link_libraries(database_memory_backend
Qt::Core
Boost::boost

database

PRIVATE
core
database
)

generate_export_header(database_memory_backend)
74 changes: 58 additions & 16 deletions src/database/backends/memory_backend/memory_backend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,13 @@ namespace Database
using T = std::decay_t<decltype(filter)>;
if constexpr (std::is_same_v<T, Database::FilterSimilarPhotos>)
{
result.erase(std::remove_if(result.begin(), result.end(), [](const MemoryBackend::StoregeDelta& photo) {
result.erase(std::remove_if(result.begin(), result.end(), [](const MemoryBackend::StoregeDelta& photo)
{
return !photo.get<Photo::Field::PHash>().valid();
}), result.end());

std::sort(result.begin(), result.end(), [](const MemoryBackend::StoregeDelta& lhs, const MemoryBackend::StoregeDelta& rhs) {
std::sort(result.begin(), result.end(), [](const MemoryBackend::StoregeDelta& lhs, const MemoryBackend::StoregeDelta& rhs)
{
return lhs.get<Photo::Field::PHash>() < rhs.get<Photo::Field::PHash>();
});

Expand All @@ -108,14 +110,15 @@ namespace Database
}
else if constexpr (std::is_same_v<T, Database::FilterPhotosWithPHash>)
{
result.erase(std::remove_if(result.begin(), result.end(), [](const MemoryBackend::StoregeDelta& photo) {
result.erase(std::remove_if(result.begin(), result.end(), [](const MemoryBackend::StoregeDelta& photo)
{
return !photo.get<Photo::Field::PHash>().valid();
}), result.end());
}
else if constexpr (std::is_same_v<T, Database::FilterPhotosWithGeneralFlag>)
{
result.erase(std::remove_if(result.begin(), result.end(), [&filter, &db](const MemoryBackend::StoregeDelta& photo) {

result.erase(std::remove_if(result.begin(), result.end(), [&filter, &db](const MemoryBackend::StoregeDelta& photo)
{
int value = 0;
auto it = db.m_flags.find(photo.getId());

Expand All @@ -135,7 +138,8 @@ namespace Database
}
else if constexpr (std::is_same_v<T, Database::FilterFaceAnalysisStatus>)
{
result.erase(std::remove_if(result.begin(), result.end(), [&filter, &db](const MemoryBackend::StoregeDelta& photo) {
result.erase(std::remove_if(result.begin(), result.end(), [&filter, &db](const MemoryBackend::StoregeDelta& photo)
{
bool performed = false;

const auto ph_id = photo.getId();
Expand All @@ -158,6 +162,21 @@ namespace Database
(filter.status == Database::FilterFaceAnalysisStatus::NotPerformed && performed);
}), result.end());
}
else if constexpr (std::is_same_v<T, Database::FilterPhotosWithTag>)
{
result.erase(std::remove_if(result.begin(), result.end(), [&filter](const MemoryBackend::StoregeDelta& photo)
{
const auto& tags = photo.get<Photo::Field::Tags>();
const auto it = tags.find(filter.tagType);

if (it == tags.end())
return true;
else
{
return it->second != filter.tagValue;
}
}), result.end());
}

}, dbFilter);

Expand Down Expand Up @@ -232,7 +251,7 @@ namespace Database
const Photo::Id id(m_db->m_nextPhotoId);
delta.setId(id);

auto [it, i] = m_db->m_photos.insert(StoregeDelta(delta));
auto [it, i] = m_db->m_photos.insert(StoregeDelta(delta - Photo::Field::People));
assert(i == true);

if (delta.has(Photo::Field::People))
Expand Down Expand Up @@ -317,21 +336,16 @@ namespace Database
}


Photo::DataDelta MemoryBackend::getPhotoDelta(const Photo::Id& id, const std::set<Photo::Field>& _fields)
Photo::DataDelta MemoryBackend::getPhotoDelta(const Photo::Id& id, const std::set<Photo::Field>& fields)
{
assert(fields.empty() == false);

StoregeDelta storageDelta;
auto it = m_db->m_photos.find(id);

if (it != m_db->m_photos.end())
storageDelta = *it;

std::set<Photo::Field> fields = _fields;
if (fields.empty())
{
const auto allEntries = magic_enum::enum_values<Photo::Field>();
fields.insert(allEntries.begin(), allEntries.end());
}

Photo::DataDelta delta(id);

for_each<Photo::Field::Path, Photo::Field::Tags, Photo::Field::Geometry, Photo::Field::GroupInfo, Photo::Field::Flags, Photo::Field::PHash>(delta, storageDelta, fields);
Expand Down Expand Up @@ -730,7 +744,7 @@ namespace Database

std::vector<Photo::DataDelta> photo_data;
for(const auto id: ids)
photo_data.push_back(getPhotoDelta(id, {}));
photo_data.push_back(getPhotoDelta(id, Photo::AllFields));

onPhotos(photo_data, action);

Expand All @@ -756,6 +770,34 @@ namespace Database
}


std::vector<Photo::DataDelta> MemoryBackend::fetchData(const Filter& filter, const std::set<Photo::Field>& fields)
{
assert(fields.empty() == false);

std::vector<StoregeDelta> data(m_db->m_photos.begin(), m_db->m_photos.end());
data = filterPhotos(data, *m_db, filter);

auto& peopleAccessor = peopleInformationAccessor();
std::vector<Photo::DataDelta> deltas;

for(const auto& d: data)
{
Photo::DataDelta delta(d.getId());
for_each<Photo::Field::Path, Photo::Field::Tags, Photo::Field::Geometry, Photo::Field::GroupInfo, Photo::Field::Flags, Photo::Field::PHash>(delta, d, fields);

if (fields.contains(Photo::Field::People))
{
const auto peopleData = peopleAccessor.listPeopleFull(delta.getId());
delta.insert<Photo::Field::People>(peopleData);
}

deltas.push_back(delta);
}

return deltas;
}


void MemoryBackend::setPHash(const Photo::Id& id, const Photo::PHashT& phash)
{
auto it = m_db->m_photos.find(id);
Expand Down
1 change: 1 addition & 0 deletions src/database/backends/memory_backend/memory_backend.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ namespace Database
bool removePhotos(const Filter &) override;
std::vector<Photo::Id> onPhotos(const Filter &, const Action &) override;
std::vector<Photo::Id> getPhotos(const Filter &) override;
std::vector<Photo::DataDelta> fetchData(const Filter &, const std::set<Photo::Field> & = Photo::AllFields) override;
void setPHash(const Photo::Id &, const Photo::PHashT & ) override;
std::optional<Photo::PHashT> getPHash(const Photo::Id &) override;
bool hasPHash(const Photo::Id &) override;
Expand Down
Loading

0 comments on commit 67c74cf

Please sign in to comment.