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

Improve collection refresh performance #635

Merged
merged 63 commits into from
Jun 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
7789974
Update comments
Kicer86 May 1, 2024
43352ee
Introduce fetchData()
Kicer86 May 2, 2024
ca70a54
Use auto and drop TODO
Kicer86 May 3, 2024
fd5932a
Cleanup database interface
Kicer86 May 3, 2024
266d2b3
Format
Kicer86 May 3, 2024
f5c242d
Split SQLite backend into backend and plugin
Kicer86 May 3, 2024
af11fb4
Add nanobench submodule
Kicer86 May 4, 2024
455a61b
Prepare base for benchmarking
Kicer86 May 4, 2024
d77778e
Update renamed plugin
Kicer86 May 4, 2024
daa8886
Make sure photos won't get optimized out
Kicer86 May 5, 2024
108d4e7
Silence warnings
Kicer86 May 5, 2024
82e9876
Fix warning
Kicer86 May 5, 2024
ec789ab
Handle FilterPhotosWithTag in MemoryBackend
Kicer86 May 6, 2024
cf8bced
Add hash (for unordered_map) for Id class
Kicer86 May 7, 2024
577bb14
Provide initial implementation for fetchData()
Kicer86 May 7, 2024
793b467
Format
Kicer86 May 7, 2024
65dea38
Add benchmark for fetchData()
Kicer86 May 7, 2024
4036b43
Format
Kicer86 May 7, 2024
ae5e0a0
Handle tags when fetching data
Kicer86 May 8, 2024
38a1963
Include people in data
Kicer86 May 8, 2024
e9b731a
Update policy to new
Kicer86 May 9, 2024
bc2eb86
Add concept for maps recognition
Kicer86 May 9, 2024
282af84
Simplify
Kicer86 May 9, 2024
f170842
Handle flags when fetching data
Kicer86 May 9, 2024
a6ac84c
Handle geometry when fetching data
Kicer86 May 9, 2024
3dc51a8
Fix syntax
Kicer86 May 9, 2024
5cff030
Handle groups when fetching data
Kicer86 May 13, 2024
f1adca7
Add 'tag' type so traits can be used on `Id`
Kicer86 May 14, 2024
256ec06
Handle phashes when fetching data
Kicer86 May 14, 2024
eebbf85
Simplify
Kicer86 May 14, 2024
5ebb27d
Move traits to common file
Kicer86 May 16, 2024
21afdcc
Use traits from 'core'
Kicer86 May 16, 2024
d058ed3
Handle people when fetching data
Kicer86 May 16, 2024
02a611f
Use proper column for path
Kicer86 May 17, 2024
8666cbb
Add some brief printer for Photo::DataDelta
Kicer86 May 17, 2024
e929cb8
Add method for clearing field
Kicer86 May 17, 2024
3ff3572
Introduce utils with function for face location decoding
Kicer86 May 18, 2024
1119e62
Introduce function for fingerprints decoding
Kicer86 May 18, 2024
711d825
Add missing header
Kicer86 May 18, 2024
9c7f3b5
Add new cosntructor
Kicer86 May 18, 2024
362e8c6
Use new functions
Kicer86 May 18, 2024
d0a7ca4
Make sure all fields are set
Kicer86 May 18, 2024
57553be
Add people to rich db
Kicer86 May 18, 2024
1a3e003
Handle invalid input
Kicer86 May 18, 2024
6ad4742
Implement people fetching
Kicer86 May 18, 2024
7bbd853
Include people in benchmark
Kicer86 May 18, 2024
1db6c35
Speedup decodeFaceLocation()
Kicer86 May 19, 2024
af04304
Speedup decodeFingerprint()
Kicer86 May 19, 2024
d06dc68
Unify code
Kicer86 May 19, 2024
bf11f5b
Prepare base for exact fields read
Kicer86 May 22, 2024
c82e398
Prepare base for tests of Delta fetching
Kicer86 May 23, 2024
637a328
Introduce utility for iterating over enum's values
Kicer86 May 24, 2024
cd4433c
Simplify
Kicer86 May 24, 2024
554c309
Add comparison operator
Kicer86 May 24, 2024
61bf718
Properly convert vector
Kicer86 May 24, 2024
63b181d
Add method for copying Delta without given field
Kicer86 May 25, 2024
f55b921
Do not copy people into storage
Kicer86 May 25, 2024
04b351f
Introduce set with all Photo::Fields
Kicer86 May 25, 2024
5093e3f
Invert logic of default 'fields' parameter
Kicer86 May 25, 2024
047c4f8
Handle fields properly
Kicer86 May 26, 2024
b87f1cb
Fetch required fields only
Kicer86 May 26, 2024
0b65c0f
Adopt benchmark to new approach
Kicer86 May 26, 2024
d9cc6a1
Improve speed for db photos fetch
Kicer86 Jun 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@
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 @@
}
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 @@
}
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 @@
(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;

Check warning on line 173 in src/database/backends/memory_backend/memory_backend.cpp

View check run for this annotation

Codecov / codecov/patch

src/database/backends/memory_backend/memory_backend.cpp#L173

Added line #L173 was not covered by tests
else
{
return it->second != filter.tagValue;
}
}), result.end());
}

}, dbFilter);

Expand Down Expand Up @@ -232,7 +251,7 @@
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 @@
}


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 @@

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 @@
}


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
Loading